Security: add DOM-purification, add 'norefferer' and 'noopener' automatically to links to new tabs

This commit is contained in:
Pieter Vander Vennet 2023-09-20 23:05:08 +02:00
parent 9252aafa2d
commit 3a77c6f33e
5 changed files with 71 additions and 13 deletions

58
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "mapcomplete",
"version": "0.31.4",
"version": "0.33.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "mapcomplete",
"version": "0.31.4",
"version": "0.33.0",
"license": "GPL-3.0-or-later",
"dependencies": {
"@rgossiaux/svelte-headlessui": "^1.0.2",
@ -18,12 +18,14 @@
"@turf/distance": "^6.5.0",
"@turf/length": "^6.5.0",
"@turf/turf": "^6.5.0",
"@types/dompurify": "^3.0.2",
"@types/showdown": "^2.0.0",
"chart.js": "^3.8.0",
"country-language": "^0.1.7",
"country-to-currency": "^1.0.10",
"csv-parse": "^5.1.0",
"doctest-ts-improved": "^0.8.8",
"dompurify": "^3.0.5",
"email-validator": "^2.0.4",
"escape-html": "^1.0.3",
"fake-dom": "^1.0.4",
@ -3799,6 +3801,14 @@
"@types/chai": "*"
}
},
"node_modules/@types/dompurify": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.2.tgz",
"integrity": "sha512-YBL4ziFebbbfQfH5mlC+QTJsvh0oJUrWbmxKMyEdL7emlHJqGR2Qb34TEFKj+VCayBvjKy3xczMFNhugThUsfQ==",
"dependencies": {
"@types/trusted-types": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
@ -3926,6 +3936,11 @@
"resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.0.tgz",
"integrity": "sha512-70xBJoLv+oXjB5PhtA8vo7erjLDp9/qqI63SRHm4REKrwuPOLs8HhXwlZJBJaB4kC18cCZ1UUZ6Fb/PLFW4TCA=="
},
"node_modules/@types/trusted-types": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.4.tgz",
"integrity": "sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ=="
},
"node_modules/@types/wikidata-sdk": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/wikidata-sdk/-/wikidata-sdk-6.1.0.tgz",
@ -6009,10 +6024,9 @@
}
},
"node_modules/dompurify": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==",
"optional": true
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.5.tgz",
"integrity": "sha512-F9e6wPGtY+8KNMRAVfxeCOHU0/NPWMSENNq4pQctuXRqqdEPW7q3CrLbR5Nse044WwacyjHGOMlvNsBe1y6z9A=="
},
"node_modules/domutils": {
"version": "1.3.0",
@ -8394,6 +8408,12 @@
"url": "https://opencollective.com/core-js"
}
},
"node_modules/jspdf/node_modules/dompurify": {
"version": "2.4.7",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz",
"integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==",
"optional": true
},
"node_modules/jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
@ -16125,6 +16145,14 @@
"@types/chai": "*"
}
},
"@types/dompurify": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.2.tgz",
"integrity": "sha512-YBL4ziFebbbfQfH5mlC+QTJsvh0oJUrWbmxKMyEdL7emlHJqGR2Qb34TEFKj+VCayBvjKy3xczMFNhugThUsfQ==",
"requires": {
"@types/trusted-types": "*"
}
},
"@types/estree": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
@ -16252,6 +16280,11 @@
"resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.0.tgz",
"integrity": "sha512-70xBJoLv+oXjB5PhtA8vo7erjLDp9/qqI63SRHm4REKrwuPOLs8HhXwlZJBJaB4kC18cCZ1UUZ6Fb/PLFW4TCA=="
},
"@types/trusted-types": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.4.tgz",
"integrity": "sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ=="
},
"@types/wikidata-sdk": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/wikidata-sdk/-/wikidata-sdk-6.1.0.tgz",
@ -17770,10 +17803,9 @@
}
},
"dompurify": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==",
"optional": true
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.5.tgz",
"integrity": "sha512-F9e6wPGtY+8KNMRAVfxeCOHU0/NPWMSENNq4pQctuXRqqdEPW7q3CrLbR5Nse044WwacyjHGOMlvNsBe1y6z9A=="
},
"domutils": {
"version": "1.3.0",
@ -19559,6 +19591,12 @@
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz",
"integrity": "sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==",
"optional": true
},
"dompurify": {
"version": "2.4.7",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz",
"integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==",
"optional": true
}
}
},

View file

@ -115,12 +115,14 @@
"@turf/distance": "^6.5.0",
"@turf/length": "^6.5.0",
"@turf/turf": "^6.5.0",
"@types/dompurify": "^3.0.2",
"@types/showdown": "^2.0.0",
"chart.js": "^3.8.0",
"country-language": "^0.1.7",
"country-to-currency": "^1.0.10",
"csv-parse": "^5.1.0",
"doctest-ts-improved": "^0.8.8",
"dompurify": "^3.0.5",
"email-validator": "^2.0.4",
"escape-html": "^1.0.3",
"fake-dom": "^1.0.4",

View file

@ -113,6 +113,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
readonly floors: Store<string[]>
constructor(layout: LayoutConfig) {
Utils.initDomPurify()
this.layout = layout
this.featureSwitches = new FeatureSwitchState(layout)
this.guistate = new MenuState(

View file

@ -2,12 +2,18 @@
/**
* Given an HTML string, properly shows this
*/
import DOMPurify from 'dompurify';
export let src: string
let cleaned = DOMPurify.sanitize(src, { USE_PROFILES: { html: true },
ADD_ATTR: ['target'] // Don't remove target='_blank'. Note that Utils.initDomPurify does add a hook which automatically adds 'rel=noopener'
});
let htmlElem: HTMLElement
$: {
if (htmlElem) {
htmlElem.innerHTML = src
htmlElem.innerHTML = cleaned
}
}

View file

@ -1,4 +1,5 @@
import colors from "./assets/colors.json"
import DOMPurify from "dompurify"
export class Utils {
/**
@ -25,6 +26,16 @@ Note that these values can be prepare with javascript in the theme by using a [c
`
public static readonly imageExtensions = new Set(["jpg", "png", "svg", "jpeg", ".gif"])
public static initDomPurify() {
DOMPurify.addHook("afterSanitizeAttributes", function (node) {
// set all elements owning target to target=_blank + add noopener noreferrer
if ("target" in node) {
node.setAttribute("target", "_blank")
node.setAttribute("rel", "noopener noreferrer")
}
})
}
public static readonly special_visualizations_importRequirementDocs = `#### Importing a dataset into OpenStreetMap: requirements
If you want to import a dataset, make sure that: