From c7442c3adf43d55cdb2b4e5c7e23222541fda6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Fri, 4 Aug 2017 20:01:37 -0700 Subject: [PATCH] Linkify URLs in the annotation use linkifyjs to make all URLs in the annotation clickable, but since it requires us to stop escaping html when rendering annotation object let's first manually escape it to prevent rogue alerts with malicious annotations from executing and other ugly things in user browsers --- .../__snapshots__/templates.test.js.snap | 13 +++++++++++ assets/static/templates.js | 2 ++ assets/static/templates.test.js | 23 +++++++++++++++++++ assets/templates/alertgroup.html | 2 +- package-lock.json | 5 ++++ package.json | 1 + 6 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 assets/static/__snapshots__/templates.test.js.snap diff --git a/assets/static/__snapshots__/templates.test.js.snap b/assets/static/__snapshots__/templates.test.js.snap new file mode 100644 index 000000000..719f74fcb --- /dev/null +++ b/assets/static/__snapshots__/templates.test.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`linkify kibana link 1`] = `"https://kibana/app/kibana#/dashboard/dashboard_name?_g=(time:(from:now-1h,mode:quick,to:now))&_a=(filters:!((query:(match:(host:(query:hostname,type:phrase))),meta:(alias:!n,disabled:!f,index:'logstash-*',key:host,negate:!f,value:hostname)),(meta:(alias:!n,disabled:!f,index:'logstash-*',key:program,negate:!f,value:puppet-agent),query:(match:(program:(query:puppet-agent,type:phrase)))),(meta:(alias:!n,disabled:!f,index:'logstash-*',key:level,negate:!f,value:ERROR),query:(match:(level:(query:ERROR,type:phrase))))))"`; + +exports[`linkify kibana link 2`] = `"foo https://kibana/app/kibana#/dashboard/dashboard_name?_g=(time:(from:now-1h,mode:quick,to:now))&_a=(filters:!((query:(match:(host:(query:hostname,type:phrase))),meta:(alias:!n,disabled:!f,index:'logstash-*',key:host,negate:!f,value:hostname)),(meta:(alias:!n,disabled:!f,index:'logstash-*',key:program,negate:!f,value:puppet-agent),query:(match:(program:(query:puppet-agent,type:phrase)))),(meta:(alias:!n,disabled:!f,index:'logstash-*',key:level,negate:!f,value:ERROR),query:(match:(level:(query:ERROR,type:phrase)))))) bar"`; + +exports[`linkify simple link 1`] = `"http://localhost"`; + +exports[`linkify simple link 2`] = `"http://localhost:8080/abc"`; + +exports[`linkify simple link 3`] = `"http://localhost:8080/abc#foo"`; + +exports[`linkify simple link 4`] = `"http://localhost:8080/abc?foo"`; diff --git a/assets/static/templates.js b/assets/static/templates.js index 2035ed6cb..ddaedaab3 100644 --- a/assets/static/templates.js +++ b/assets/static/templates.js @@ -3,6 +3,7 @@ const $ = require("jquery"); const _ = require("underscore"); const moment = require("moment"); +require("javascript-linkify"); const alerts = require("./alerts"); @@ -73,6 +74,7 @@ function init() { function renderTemplate(name, context) { context["moment"] = moment; + context["linkify"] = window.linkify; context["renderTemplate"] = renderTemplate; context["sortMapByKey"] = alerts.sortMapByKey; context["getLabelAttrs"] = alerts.getLabelAttrs; diff --git a/assets/static/templates.test.js b/assets/static/templates.test.js index f2add3139..885bf0775 100644 --- a/assets/static/templates.test.js +++ b/assets/static/templates.test.js @@ -1,7 +1,30 @@ const templates = require("./templates"); const templatesMock = require("./__mocks__/templatesMock"); +require("javascript-linkify"); test("templates init()", () => { document.body.innerHTML = templatesMock.loadTemplates(); templates.init(); }); + +test("linkify simple link", () => { + expect(window.linkify("http://localhost")).toMatchSnapshot(); + expect(window.linkify("http://localhost:8080/abc")).toMatchSnapshot(); + expect(window.linkify("http://localhost:8080/abc#foo")).toMatchSnapshot(); + expect(window.linkify("http://localhost:8080/abc?foo")).toMatchSnapshot(); +}); + +test("linkify kibana link", () => { + let longLink = + "https://kibana/app/kibana#/dashboard/dashboard_name?_g=" + + "(time:(from:now-1h,mode:quick,to:now))&_a=(filters:!((query:" + + "(match:(host:(query:hostname,type:phrase))),meta:" + + "(alias:!n,disabled:!f,index:'logstash-*',key:host,negate:!f," + + "value:hostname)),(meta:(alias:!n,disabled:!f,index:'logstash-*'" + + ",key:program,negate:!f,value:puppet-agent),query:(match:(program:" + + "(query:puppet-agent,type:phrase)))),(meta:(alias:!n,disabled:" + + "!f,index:'logstash-*',key:level,negate:!f,value:ERROR),query:" + + "(match:(level:(query:ERROR,type:phrase))))))"; + expect(window.linkify(longLink)).toMatchSnapshot(); + expect(window.linkify("foo " + longLink + " bar")).toMatchSnapshot(); +}); diff --git a/assets/templates/alertgroup.html b/assets/templates/alertgroup.html index 77ac699ac..afbf0e8f8 100644 --- a/assets/templates/alertgroup.html +++ b/assets/templates/alertgroup.html @@ -31,7 +31,7 @@
<% if (annotation.value) { %> - <%- annotation.value %> + <%= linkify(_.escape(annotation.value)) %> <% } else { %> [ missing annotation value ] diff --git a/package-lock.json b/package-lock.json index 716664a78..e25c6a13f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3593,6 +3593,11 @@ "handlebars": "4.0.10" } }, + "javascript-linkify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/javascript-linkify/-/javascript-linkify-0.3.0.tgz", + "integrity": "sha1-Ep/6I7Uon73mxsUM73efz1kFWtY=" + }, "jest": { "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest/-/jest-20.0.4.tgz", diff --git a/package.json b/package.json index 99ded97e6..fd77f04f8 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "favico.js": "^0.3.10", "font-awesome": "^4.7.0", "is-in-viewport": "^3.0.0", + "javascript-linkify": "^0.3.0", "jquery": "^3.2.1", "js-cookie": "^2.1.4", "js-sha1": "^0.4.1",