diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a08239826..c2c04f9e3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## Fixed - [#2362](https://github.com/plotly/dash/pull/2362) Global namespace not polluted any more when loading clientside callbacks. +- [#2833](https://github.com/plotly/dash/pull/2833) Allow data url in link props. Fixes [#2764](https://github.com/plotly/dash/issues/2764) ## [2.16.1] - 2024-03-06 diff --git a/components/dash-core-components/package-lock.json b/components/dash-core-components/package-lock.json index 371ffd4d5b..5854c781f3 100644 --- a/components/dash-core-components/package-lock.json +++ b/components/dash-core-components/package-lock.json @@ -9,7 +9,6 @@ "version": "2.13.1", "license": "MIT", "dependencies": { - "@braintree/sanitize-url": "^7.0.0", "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-regular-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4", @@ -1862,11 +1861,6 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, - "node_modules/@braintree/sanitize-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.0.0.tgz", - "integrity": "sha512-GMu2OJiTd1HSe74bbJYQnVvELANpYiGFZELyyTM1CR0sdv5ReQAcJ/c/8pIrPab3lO11+D+EpuGLUxqz+y832g==" - }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -11503,11 +11497,6 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, - "@braintree/sanitize-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.0.0.tgz", - "integrity": "sha512-GMu2OJiTd1HSe74bbJYQnVvELANpYiGFZELyyTM1CR0sdv5ReQAcJ/c/8pIrPab3lO11+D+EpuGLUxqz+y832g==" - }, "@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", diff --git a/components/dash-core-components/package.json b/components/dash-core-components/package.json index c38b0bed77..71d0fb3ceb 100644 --- a/components/dash-core-components/package.json +++ b/components/dash-core-components/package.json @@ -35,7 +35,6 @@ "maintainer": "Alex Johnson ", "license": "MIT", "dependencies": { - "@braintree/sanitize-url": "^7.0.0", "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-regular-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4", @@ -87,13 +86,15 @@ "react": "^16.14.0", "react-dom": "^16.14.0", "react-jsx-parser": "1.21.0", + "rimraf": "^5.0.5", "style-loader": "^3.3.3", "styled-jsx": "^3.4.4", "webpack": "^5.90.3", - "webpack-cli": "^5.1.4", - "rimraf": "^5.0.5" + "webpack-cli": "^5.1.4" + }, + "optionalDependencies": { + "fsevents": "*" }, - "optionalDependencies": { "fsevents": "*" }, "files": [ "/dash_core_components/*{.js,.map}", "/lib/" diff --git a/components/dash-core-components/src/components/Link.react.js b/components/dash-core-components/src/components/Link.react.js index dba962af94..61036dace4 100644 --- a/components/dash-core-components/src/components/Link.react.js +++ b/components/dash-core-components/src/components/Link.react.js @@ -1,7 +1,6 @@ import PropTypes from 'prop-types'; import React, {useEffect, useMemo} from 'react'; -import {sanitizeUrl} from '@braintree/sanitize-url'; import {isNil} from 'ramda'; /* @@ -46,8 +45,9 @@ const Link = props => { refresh, setProps, } = props; + const cleanUrl = window.dash_clientside.clean_url; const sanitizedUrl = useMemo(() => { - return href ? sanitizeUrl(href) : undefined; + return href ? cleanUrl(href) : undefined; }, [href]); const updateLocation = e => { diff --git a/components/dash-html-components/package-lock.json b/components/dash-html-components/package-lock.json index ba0cf4aff5..205f3f02d3 100644 --- a/components/dash-html-components/package-lock.json +++ b/components/dash-html-components/package-lock.json @@ -9,7 +9,6 @@ "version": "2.0.17", "license": "MIT", "dependencies": { - "@braintree/sanitize-url": "^7.0.0", "prop-types": "^15.8.1", "ramda": "^0.29.0" }, @@ -1848,11 +1847,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@braintree/sanitize-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.0.0.tgz", - "integrity": "sha512-GMu2OJiTd1HSe74bbJYQnVvELANpYiGFZELyyTM1CR0sdv5ReQAcJ/c/8pIrPab3lO11+D+EpuGLUxqz+y832g==" - }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -9755,11 +9749,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "@braintree/sanitize-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.0.0.tgz", - "integrity": "sha512-GMu2OJiTd1HSe74bbJYQnVvELANpYiGFZELyyTM1CR0sdv5ReQAcJ/c/8pIrPab3lO11+D+EpuGLUxqz+y832g==" - }, "@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", diff --git a/components/dash-html-components/package.json b/components/dash-html-components/package.json index 0d43687785..15b5a15e16 100644 --- a/components/dash-html-components/package.json +++ b/components/dash-html-components/package.json @@ -28,7 +28,6 @@ "author": "Chris Parmer ", "maintainer": "Alex Johnson ", "dependencies": { - "@braintree/sanitize-url": "^7.0.0", "prop-types": "^15.8.1", "ramda": "^0.29.0" }, @@ -44,16 +43,16 @@ "eslint": "^8.41.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.2", + "mkdirp": "^0.5.1", "npm-run-all": "^4.1.5", + "react": "^16.14.0", "react-docgen": "^5.4.3", + "react-dom": "^16.14.0", "request": "^2.88.2", + "rimraf": "^5.0.5", "string": "^3.3.3", "webpack": "^5.90.3", - "webpack-cli": "^5.1.4", - "react": "^16.14.0", - "react-dom": "^16.14.0", - "rimraf": "^5.0.5", - "mkdirp": "^0.5.1" + "webpack-cli": "^5.1.4" }, "files": [ "/dash_html_components/*{.js,.map}" diff --git a/components/dash-html-components/scripts/generate-components.js b/components/dash-html-components/scripts/generate-components.js index 01874937bd..46ba7a2cf3 100644 --- a/components/dash-html-components/scripts/generate-components.js +++ b/components/dash-html-components/scripts/generate-components.js @@ -249,18 +249,12 @@ const customDocs = { * .` }; -const customImportsForComponents = { - a: `import {sanitizeUrl} from '@braintree/sanitize-url';`, - form: `import {sanitizeUrl} from '@braintree/sanitize-url';`, - iframe: `import {sanitizeUrl} from '@braintree/sanitize-url';`, - object: `import {sanitizeUrl} from '@braintree/sanitize-url';`, - embed: `import {sanitizeUrl} from '@braintree/sanitize-url';`, - button: `import {sanitizeUrl} from '@braintree/sanitize-url';`, -} +const customImportsForComponents = {}; function createXSSProtection(propName) { return ` - const ${propName} = React.useMemo(() => props.${propName} && sanitizeUrl(props.${propName}), [props.${propName}]); + const cleanUrl = window.dash_clientside.clean_url; + const ${propName} = React.useMemo(() => props.${propName} && cleanUrl(props.${propName}), [props.${propName}]); if (${propName}) { extraProps.${propName} = ${propName}; diff --git a/dash/dash-renderer/src/utils/clientsideFunctions.ts b/dash/dash-renderer/src/utils/clientsideFunctions.ts index 1f33383205..88c422fa6f 100644 --- a/dash/dash-renderer/src/utils/clientsideFunctions.ts +++ b/dash/dash-renderer/src/utils/clientsideFunctions.ts @@ -18,6 +18,31 @@ const set_props = (id: string | object, props: {[k: string]: any}) => { } }; +// Clean url code adapted from https://github.com/braintree/sanitize-url/blob/main/src/constants.ts +// to allow for data protocol. +const invalidProtocols = /^([^\w]*)(javascript|vbscript)/im; +const newLines = /&(tab|newline);/gi; + +// eslint-disable-next-line no-control-regex +const ctrlChars = /[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim; +const htmlEntities = /&#(\w+)(^\w|;)?/g; + +const clean_url = (url: string, fallback = 'about:blank') => { + if (url === '') { + return url; + } + const cleaned = url + .replace(newLines, '') + .replace(ctrlChars, '') + .replace(htmlEntities, (_, dec) => String.fromCharCode(dec)) + .trim(); + if (invalidProtocols.test(cleaned)) { + return fallback; + } + return url; +}; + const dc = ((window as any).dash_clientside = (window as any).dash_clientside || {}); dc['set_props'] = set_props; +dc['clean_url'] = dc['clean_url'] === undefined ? clean_url : dc['clean_url']; diff --git a/package-lock.json b/package-lock.json index 794258aaea..be221d74ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,7 @@ "license": "UNLICENSED", "dependencies": { "npm-run-all": "4.1.5", - "rimraf": "^5.0.5", - "run-script": "^0.1.1", - "shx": "^0.3.4" + "rimraf": "^5.0.5" }, "devDependencies": { "@lerna/filter-options": "^6.4.1", @@ -3537,7 +3535,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/function-bind": { "version": "1.1.1", @@ -4269,6 +4268,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -4277,7 +4277,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "1.3.8", @@ -4378,14 +4379,6 @@ "node": ">= 0.4" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", @@ -5969,6 +5962,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7112,6 +7106,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -7598,6 +7593,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8230,17 +8226,6 @@ "node": ">= 6" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -8440,14 +8425,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/run-script": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/run-script/-/run-script-0.1.1.tgz", - "integrity": "sha512-j1skh5zbkS0W8O9m4x7qSW2u4uNEANGEmCbuTMwGCxJL1Ysi1rwO5UoRJ/pcEobhcpYVhvmZG/kga5e1Dfj2Ng==", - "engines": { - "node": ">=4" - } - }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -8568,56 +8545,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/shx": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", - "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", - "dependencies": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - }, - "bin": { - "shx": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -9659,7 +9586,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "4.0.1", @@ -12494,7 +12422,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "function-bind": { "version": "1.1.1", @@ -13024,6 +12953,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -13032,7 +12962,8 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "ini": { "version": "1.3.8", @@ -13117,11 +13048,6 @@ "side-channel": "^1.0.4" } }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" - }, "ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", @@ -14293,7 +14219,8 @@ "minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true }, "minimist-options": { "version": "4.1.0", @@ -15180,6 +15107,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "requires": { "wrappy": "1" } @@ -15536,7 +15464,8 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true }, "path-key": { "version": "3.1.1", @@ -16007,14 +15936,6 @@ "util-deprecate": "^1.0.1" } }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "requires": { - "resolve": "^1.1.6" - } - }, "redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -16141,11 +16062,6 @@ "queue-microtask": "^1.2.2" } }, - "run-script": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/run-script/-/run-script-0.1.1.tgz", - "integrity": "sha512-j1skh5zbkS0W8O9m4x7qSW2u4uNEANGEmCbuTMwGCxJL1Ysi1rwO5UoRJ/pcEobhcpYVhvmZG/kga5e1Dfj2Ng==" - }, "rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -16230,40 +16146,6 @@ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==" }, - "shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "shx": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", - "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", - "requires": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - } - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -17062,7 +16944,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "write-file-atomic": { "version": "4.0.1", diff --git a/tests/integration/security/test_xss.py b/tests/integration/security/test_xss.py index 545fb01a90..fe1341d860 100644 --- a/tests/integration/security/test_xss.py +++ b/tests/integration/security/test_xss.py @@ -9,7 +9,19 @@ def test_xss001_banned_protocols(dash_duo): dcc.Link("dcc-link", href="javascript:alert(1)", id="dcc-link"), html.Br(), html.A( - "html.A", href='javascr\nipt:alert(1);console.log("xss");', id="html-A" + "html.A", + href='javascr\n\nipt:alert(1);console.log("xss");', + id="html-A", + ), + html.A( + "html.A.escape", + id="html-A-escape", + href="""javascript\x09\x0a:alert(1)""", + ), + html.A( + "html.A.encoded", + id="html-A-encoded", + href="javascript:alert('XSS')", ), html.Br(), html.Form( @@ -35,6 +47,8 @@ def test_xss001_banned_protocols(dash_duo): for element_id, prop in ( ("#dcc-link", "href"), ("#html-A", "href"), + ("#html-A-escape", "href"), + ("#html-A-encoded", "href"), ("#iframe-src", "src"), ("#object-data", "data"), ("#embed-src", "src"), @@ -42,9 +56,10 @@ def test_xss001_banned_protocols(dash_duo): ): element = dash_duo.find_element(element_id) + prop_value = element.get_attribute(prop) assert ( - element.get_attribute(prop) == "about:blank" - ), f"Failed prop: {element_id}.{prop}" + prop_value == "about:blank" + ), f"Failed prop: {element_id}.{prop} = {prop_value}" def test_xss002_blank_href(dash_duo): @@ -58,3 +73,21 @@ def test_xss002_blank_href(dash_duo): assert element.get_attribute("href") is None assert dash_duo.get_logs() == [] + + +def test_xss003_data_allowed(dash_duo): + app = Dash() + + app.layout = html.Div( + [ + html.Img( + id="image", + src="data:image/png;base64,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", + ) + ] + ) + + dash_duo.start_server(app) + element = dash_duo.find_element("#image") + assert element.get_attribute("src") != "about:blank" + assert dash_duo.get_logs() == []