Skip to content

Commit 9a4a479

Browse files
authored
Merge pull request #2833 from plotly/fix/xss-data-url
Allow data urls
2 parents 0d96c02 + 30e2755 commit 9a4a479

File tree

10 files changed

+100
-186
lines changed

10 files changed

+100
-186
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
2020
## Fixed
2121

2222
- [#2362](https://github.com/plotly/dash/pull/2362) Global namespace not polluted any more when loading clientside callbacks.
23+
- [#2833](https://github.com/plotly/dash/pull/2833) Allow data url in link props. Fixes [#2764](https://github.com/plotly/dash/issues/2764)
2324

2425
## [2.16.1] - 2024-03-06
2526

components/dash-core-components/package-lock.json

Lines changed: 0 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/dash-core-components/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
"maintainer": "Alex Johnson <[email protected]>",
3636
"license": "MIT",
3737
"dependencies": {
38-
"@braintree/sanitize-url": "^7.0.0",
3938
"@fortawesome/fontawesome-svg-core": "1.2.36",
4039
"@fortawesome/free-regular-svg-icons": "^5.15.4",
4140
"@fortawesome/free-solid-svg-icons": "^5.15.4",
@@ -87,13 +86,15 @@
8786
"react": "^16.14.0",
8887
"react-dom": "^16.14.0",
8988
"react-jsx-parser": "1.21.0",
89+
"rimraf": "^5.0.5",
9090
"style-loader": "^3.3.3",
9191
"styled-jsx": "^3.4.4",
9292
"webpack": "^5.90.3",
93-
"webpack-cli": "^5.1.4",
94-
"rimraf": "^5.0.5"
93+
"webpack-cli": "^5.1.4"
94+
},
95+
"optionalDependencies": {
96+
"fsevents": "*"
9597
},
96-
"optionalDependencies": { "fsevents": "*" },
9798
"files": [
9899
"/dash_core_components/*{.js,.map}",
99100
"/lib/"

components/dash-core-components/src/components/Link.react.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import PropTypes from 'prop-types';
22

33
import React, {useEffect, useMemo} from 'react';
4-
import {sanitizeUrl} from '@braintree/sanitize-url';
54
import {isNil} from 'ramda';
65

76
/*
@@ -46,8 +45,9 @@ const Link = props => {
4645
refresh,
4746
setProps,
4847
} = props;
48+
const cleanUrl = window.dash_clientside.clean_url;
4949
const sanitizedUrl = useMemo(() => {
50-
return href ? sanitizeUrl(href) : undefined;
50+
return href ? cleanUrl(href) : undefined;
5151
}, [href]);
5252

5353
const updateLocation = e => {

components/dash-html-components/package-lock.json

Lines changed: 0 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/dash-html-components/package.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
"author": "Chris Parmer <[email protected]>",
2929
"maintainer": "Alex Johnson <[email protected]>",
3030
"dependencies": {
31-
"@braintree/sanitize-url": "^7.0.0",
3231
"prop-types": "^15.8.1",
3332
"ramda": "^0.29.0"
3433
},
@@ -44,16 +43,16 @@
4443
"eslint": "^8.41.0",
4544
"eslint-plugin-import": "^2.27.5",
4645
"eslint-plugin-react": "^7.32.2",
46+
"mkdirp": "^0.5.1",
4747
"npm-run-all": "^4.1.5",
48+
"react": "^16.14.0",
4849
"react-docgen": "^5.4.3",
50+
"react-dom": "^16.14.0",
4951
"request": "^2.88.2",
52+
"rimraf": "^5.0.5",
5053
"string": "^3.3.3",
5154
"webpack": "^5.90.3",
52-
"webpack-cli": "^5.1.4",
53-
"react": "^16.14.0",
54-
"react-dom": "^16.14.0",
55-
"rimraf": "^5.0.5",
56-
"mkdirp": "^0.5.1"
55+
"webpack-cli": "^5.1.4"
5756
},
5857
"files": [
5958
"/dash_html_components/*{.js,.map}"

components/dash-html-components/scripts/generate-components.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -249,18 +249,12 @@ const customDocs = {
249249
* <body>.`
250250
};
251251

252-
const customImportsForComponents = {
253-
a: `import {sanitizeUrl} from '@braintree/sanitize-url';`,
254-
form: `import {sanitizeUrl} from '@braintree/sanitize-url';`,
255-
iframe: `import {sanitizeUrl} from '@braintree/sanitize-url';`,
256-
object: `import {sanitizeUrl} from '@braintree/sanitize-url';`,
257-
embed: `import {sanitizeUrl} from '@braintree/sanitize-url';`,
258-
button: `import {sanitizeUrl} from '@braintree/sanitize-url';`,
259-
}
252+
const customImportsForComponents = {};
260253

261254
function createXSSProtection(propName) {
262255
return `
263-
const ${propName} = React.useMemo(() => props.${propName} && sanitizeUrl(props.${propName}), [props.${propName}]);
256+
const cleanUrl = window.dash_clientside.clean_url;
257+
const ${propName} = React.useMemo(() => props.${propName} && cleanUrl(props.${propName}), [props.${propName}]);
264258
265259
if (${propName}) {
266260
extraProps.${propName} = ${propName};

dash/dash-renderer/src/utils/clientsideFunctions.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,31 @@ const set_props = (id: string | object, props: {[k: string]: any}) => {
1818
}
1919
};
2020

21+
// Clean url code adapted from https://github.com/braintree/sanitize-url/blob/main/src/constants.ts
22+
// to allow for data protocol.
23+
const invalidProtocols = /^([^\w]*)(javascript|vbscript)/im;
24+
const newLines = /&(tab|newline);/gi;
25+
26+
// eslint-disable-next-line no-control-regex
27+
const ctrlChars = /[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;
28+
const htmlEntities = /&#(\w+)(^\w|;)?/g;
29+
30+
const clean_url = (url: string, fallback = 'about:blank') => {
31+
if (url === '') {
32+
return url;
33+
}
34+
const cleaned = url
35+
.replace(newLines, '')
36+
.replace(ctrlChars, '')
37+
.replace(htmlEntities, (_, dec) => String.fromCharCode(dec))
38+
.trim();
39+
if (invalidProtocols.test(cleaned)) {
40+
return fallback;
41+
}
42+
return url;
43+
};
44+
2145
const dc = ((window as any).dash_clientside =
2246
(window as any).dash_clientside || {});
2347
dc['set_props'] = set_props;
48+
dc['clean_url'] = dc['clean_url'] === undefined ? clean_url : dc['clean_url'];

0 commit comments

Comments
 (0)