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 <scripts> and other ugly things in user browsers
This commit is contained in:
Łukasz Mierzwa
2017-08-04 20:01:37 -07:00
parent 89519267b7
commit c7442c3adf
6 changed files with 45 additions and 1 deletions

View File

@@ -0,0 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`linkify kibana link 1`] = `"<a href=\\"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))))))\\" title=\\"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))))))\\">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))))))</a>"`;
exports[`linkify kibana link 2`] = `"foo <a href=\\"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))))))\\" title=\\"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))))))\\">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))))))</a> bar"`;
exports[`linkify simple link 1`] = `"<a href=\\"http://localhost\\" title=\\"http://localhost\\">http://localhost</a>"`;
exports[`linkify simple link 2`] = `"<a href=\\"http://localhost:8080/abc\\" title=\\"http://localhost:8080/abc\\">http://localhost:8080/abc</a>"`;
exports[`linkify simple link 3`] = `"<a href=\\"http://localhost:8080/abc#foo\\" title=\\"http://localhost:8080/abc#foo\\">http://localhost:8080/abc#foo</a>"`;
exports[`linkify simple link 4`] = `"<a href=\\"http://localhost:8080/abc?foo\\" title=\\"http://localhost:8080/abc?foo\\">http://localhost:8080/abc?foo</a>"`;

View File

@@ -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;

View File

@@ -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();
});

View File

@@ -31,7 +31,7 @@
<div class="well well-sm annotation-well">
<i class="fa fa-question-circle text-muted" title="<%= annotation.key %>" data-toggle="tooltip" data-placement="top"/>
<% if (annotation.value) { %>
<%- annotation.value %>
<%= linkify(_.escape(annotation.value)) %>
<% } else { %>
<span class="text-muted">
[ missing annotation value ]

5
package-lock.json generated
View File

@@ -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",

View File

@@ -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",