Commit 26aa1f0c by Dominik Prokop Committed by GitHub

DataLinks: Sanitize data/panel link URLs (#21140)

* Sanitize html in panel links

* Add sanitize-url package

* Enable config mocking

* Sanitize datalinks urls

* Update public/app/core/config.ts

* Minor test update

* Remove sanitize-url dependency

* Remove typings

* Review update

* Revert "Remove sanitize-url dependency"

This reverts commit c4f38e6de64b36fb398f0dc61906ae070d69c5ed.

* Revert "Remove typings"

This reverts commit 676d47e8c2e46d6eeb57003d0a7e0fd99e75310f.

* Sanitaze, don't escape html when sanitizing URL
parent 475bd7ad
......@@ -202,6 +202,7 @@
},
"dependencies": {
"@babel/polyfill": "7.6.0",
"@braintree/sanitize-url": "4.0.0",
"@grafana/slate-react": "0.22.9-grafana",
"@torkelo/react-select": "2.4.1",
"@types/react-loadable": "5.5.2",
......
import { config, GrafanaBootConfig } from '@grafana/runtime';
// Legacy binding paths
export { config, GrafanaBootConfig as Settings };
export default config;
let grafanaConfig: GrafanaBootConfig = config;
export default grafanaConfig;
export const getConfig = () => {
return grafanaConfig;
};
export const updateConfig = (update: Partial<GrafanaBootConfig>) => {
grafanaConfig = {
...grafanaConfig,
...update,
};
};
import xss from 'xss';
import { sanitizeUrl as braintreeSanitizeUrl } from '@braintree/sanitize-url';
const XSSWL = Object.keys(xss.whiteList).reduce((acc, element) => {
// @ts-ignore
......@@ -26,6 +27,10 @@ export function sanitize(unsanitizedString: string): string {
}
}
export function sanitizeUrl(url: string): string {
return braintreeSanitizeUrl(url);
}
export function hasAnsiCodes(input: string): boolean {
return /\u001b\[\d{1,2}m/.test(input);
}
......
......@@ -276,11 +276,10 @@ export class PanelCtrl {
let html = '<div class="markdown-html panel-info-content">';
const md = renderMarkdown(interpolatedMarkdown);
html += config.disableSanitizeHtml ? md : sanitize(md);
html += md;
if (panel.links && panel.links.length > 0) {
const interpolatedLinks = getPanelLinksSupplier(panel).getLinks();
html += '<ul class="panel-info-corner-links">';
for (const link of interpolatedLinks) {
html +=
......@@ -297,7 +296,7 @@ export class PanelCtrl {
html += '</div>';
return html;
return config.disableSanitizeHtml ? html : sanitize(html);
}
// overriden from react
......
......@@ -32,11 +32,11 @@ describe('getLinksFromLogsField', () => {
links: [
{
title: 'title1',
url: 'domain.com/${__value.raw}',
url: 'http://domain.com/${__value.raw}',
},
{
title: 'title2',
url: 'anotherdomain.sk/${__value.raw}',
url: 'http://anotherdomain.sk/${__value.raw}',
},
],
},
......@@ -44,8 +44,8 @@ describe('getLinksFromLogsField', () => {
};
const links = getLinksFromLogsField(field, 2);
expect(links.length).toBe(2);
expect(links[0].href).toBe('domain.com/3');
expect(links[1].href).toBe('anotherdomain.sk/3');
expect(links[0].href).toBe('http://domain.com/3');
expect(links[1].href).toBe('http://anotherdomain.sk/3');
});
it('handles zero links', () => {
......
......@@ -3,6 +3,8 @@ import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import templateSrv, { TemplateSrv } from 'app/features/templating/template_srv';
import coreModule from 'app/core/core_module';
import { appendQueryToUrl, toUrlParams } from 'app/core/utils/url';
import { sanitizeUrl } from 'app/core/utils/text';
import { getConfig } from 'app/core/config';
import { VariableSuggestion, VariableOrigin, DataLinkBuiltInVars } from '@grafana/ui';
import { DataLink, KeyValue, deprecationWarning, LinkModel, DataFrame, ScopedVars } from '@grafana/data';
......@@ -209,7 +211,7 @@ export class LinkSrv implements LinkService {
value: variablesQuery,
},
});
info.href = getConfig().disableSanitizeHtml ? info.href : sanitizeUrl(info.href);
return info;
};
......
......@@ -4,6 +4,7 @@ import _ from 'lodash';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { advanceTo } from 'jest-date-mock';
import { updateConfig } from '../../../../core/config';
jest.mock('angular', () => {
const AngularJSMock = require('test/mocks/angular');
......@@ -137,4 +138,36 @@ describe('linkSrv', () => {
).toEqual('/d/1?time=1000000001');
});
});
describe('sanitization', () => {
const url = "javascript:alert('broken!);";
it.each`
disableSanitizeHtml | expected
${true} | ${url}
${false} | ${'about:blank'}
`(
"when disable disableSanitizeHtml set to '$disableSanitizeHtml' then result should be '$expected'",
({ disableSanitizeHtml, expected }) => {
updateConfig({
disableSanitizeHtml,
});
const link = linkSrv.getDataLinkUIModel(
{
title: 'Any title',
url,
},
{
__value: {
value: { time: dataPointMock.datapoint[0] },
text: 'Value',
},
},
{}
).href;
expect(link).toBe(expected);
}
);
});
});
......@@ -46,7 +46,7 @@ describe('DebugSection', () => {
{
matcherRegex: 'traceId=(\\w+)',
name: 'traceIdLink',
url: 'localhost/trace/${__value.raw}',
url: 'http://localhost/trace/${__value.raw}',
},
{
matcherRegex: 'traceId=(\\w+)',
......@@ -70,7 +70,7 @@ describe('DebugSection', () => {
wrapper
.find('tr')
.at(1)
.contains('localhost/trace/1234')
.contains('http://localhost/trace/1234')
).toBeTruthy();
});
});
declare module '@braintree/sanitize-url' {
function sanitizeUrl(url: string): string;
}
......@@ -1665,6 +1665,11 @@
resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz#860ce718b0b73f4009e153541faff2cb6b85d047"
integrity sha512-4Th98KlMHr5+JkxfcoDT//6vY8vM+iSPrLNpHhRyLx2CFYi8e2RfqPLdpbnpo0Q5lQC5hNB79yes07zb02fvCw==
"@braintree/sanitize-url@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-4.0.0.tgz#2cda79ffd67b6ea919a63b5e1a883b92d636e844"
integrity sha512-bOoFoTxuEUuri/v1q0OXN0HIrZ2EiZlRSKdveU8vS5xf2+g0TmpXhmxkTc1s+XWR5xZNoVU4uvf/Mher98tfLw==
"@cnakazawa/watch@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef"
......@@ -8567,7 +8572,7 @@ debug@^0.7.2:
resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
integrity sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=
debuglog@*, debuglog@^1.0.1:
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
......@@ -11829,7 +11834,7 @@ import-local@^2.0.0:
pkg-dir "^3.0.0"
resolve-cwd "^2.0.0"
imurmurhash@*, imurmurhash@^0.1.4:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
......@@ -13825,11 +13830,6 @@ lodash-es@^4.2.1:
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
......@@ -13838,29 +13838,12 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@*:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
lodash._getnative@*, lodash._getnative@^3.0.0:
lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
......@@ -13964,11 +13947,6 @@ lodash.padend@^4.6.1:
resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e"
integrity sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=
lodash.restparam@*:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
lodash.set@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment