Merge pull request #1094 from umap-project/use-dompurify
Use DOMPurify to escape malicious input from user
This commit is contained in:
commit
0cd1cf4ffc
7 changed files with 54 additions and 19 deletions
1
Makefile
1
Makefile
|
@ -42,6 +42,7 @@ vendors:
|
|||
mkdir -p umap/static/umap/vendors/togpx/ && cp -r node_modules/togpx/togpx.js umap/static/umap/vendors/togpx/
|
||||
mkdir -p umap/static/umap/vendors/tokml && cp -r node_modules/tokml/tokml.js umap/static/umap/vendors/tokml
|
||||
mkdir -p umap/static/umap/vendors/locatecontrol/ && cp -r node_modules/leaflet.locatecontrol/{dist/L.Control.Locate.css,src/L.Control.Locate.js} umap/static/umap/vendors/locatecontrol/
|
||||
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/purify.js umap/static/umap/vendors/dompurify/
|
||||
installjs:
|
||||
npm install
|
||||
testjsfx:
|
||||
|
|
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -10,6 +10,7 @@
|
|||
"license": "WTFPL",
|
||||
"dependencies": {
|
||||
"csv2geojson": "5.1.1",
|
||||
"dompurify": "^3.0.3",
|
||||
"georsstogeojson": "^0.1.0",
|
||||
"leaflet": "1.3.4",
|
||||
"leaflet-contextmenu": "^1.4.0",
|
||||
|
@ -676,6 +677,11 @@
|
|||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.3.tgz",
|
||||
"integrity": "sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ=="
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.3.0.tgz",
|
||||
|
@ -3719,6 +3725,11 @@
|
|||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.3.tgz",
|
||||
"integrity": "sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ=="
|
||||
},
|
||||
"domutils": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.3.0.tgz",
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"homepage": "http://wiki.openstreetmap.org/wiki/UMap",
|
||||
"dependencies": {
|
||||
"csv2geojson": "5.1.1",
|
||||
"dompurify": "^3.0.3",
|
||||
"georsstogeojson": "^0.1.0",
|
||||
"leaflet": "1.3.4",
|
||||
"leaflet-contextmenu": "^1.4.0",
|
||||
|
|
|
@ -44,7 +44,28 @@ L.Util.setNullableBooleanFromQueryString = (options, name) => {
|
|||
}
|
||||
L.Util.escapeHTML = (s) => {
|
||||
s = s ? s.toString() : ''
|
||||
return s.replace(/</gm, '<')
|
||||
s = DOMPurify.sanitize(s, {
|
||||
USE_PROFILES: { html: true },
|
||||
ADD_TAGS: ['iframe'],
|
||||
ALLOWED_TAGS: [
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'hr',
|
||||
'strong',
|
||||
'em',
|
||||
'ul',
|
||||
'li',
|
||||
'a',
|
||||
'div',
|
||||
'iframe',
|
||||
'img',
|
||||
'br',
|
||||
],
|
||||
ADD_ATTR: ['target', 'allow', 'allowfullscreen', 'frameborder', 'scrolling'],
|
||||
ALLOWED_ATTR: ['href', 'src', 'width', 'height'],
|
||||
})
|
||||
return s
|
||||
}
|
||||
L.Util.toHTML = (r) => {
|
||||
if (!r) return ''
|
||||
|
@ -53,9 +74,6 @@ L.Util.toHTML = (r) => {
|
|||
// detect newline format
|
||||
const newline = r.indexOf('\r\n') != -1 ? '\r\n' : r.indexOf('\n') != -1 ? '\n' : ''
|
||||
|
||||
// Escape tags
|
||||
r = r.replace(/</gm, '<')
|
||||
|
||||
// headings and hr
|
||||
r = r.replace(/^### (.*)/gm, '<h5>$1</h5>')
|
||||
r = r.replace(/^## (.*)/gm, '<h4>$1</h4>')
|
||||
|
@ -109,6 +127,8 @@ L.Util.toHTML = (r) => {
|
|||
// Preserver line breaks
|
||||
if (newline) r = r.replace(new RegExp(`${newline}(?=[^]+)`, 'g'), `<br>${newline}`)
|
||||
|
||||
r = L.Util.escapeHTML(r)
|
||||
|
||||
return r
|
||||
}
|
||||
L.Util.isObject = (what) => typeof what === 'object' && what !== null
|
||||
|
|
|
@ -38,49 +38,49 @@ describe('L.Util', function () {
|
|||
it('should handle links without formatting', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple http://osm.org link'),
|
||||
'A simple <a target="_blank" href="http://osm.org">http://osm.org</a> link'
|
||||
'A simple <a href="http://osm.org" target="_blank">http://osm.org</a> link'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle simple link in title', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('# http://osm.org'),
|
||||
'<h3><a target="_blank" href="http://osm.org">http://osm.org</a></h3>'
|
||||
'<h3><a href="http://osm.org" target="_blank">http://osm.org</a></h3>'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle links with url parameter', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple https://osm.org/?url=https%3A//anotherurl.com link'),
|
||||
'A simple <a target="_blank" href="https://osm.org/?url=https%3A//anotherurl.com">https://osm.org/?url=https%3A//anotherurl.com</a> link'
|
||||
'A simple <a href="https://osm.org/?url=https%3A//anotherurl.com" target="_blank">https://osm.org/?url=https%3A//anotherurl.com</a> link'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle simple link inside parenthesis', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple link (http://osm.org)'),
|
||||
'A simple link (<a target="_blank" href="http://osm.org">http://osm.org</a>)'
|
||||
'A simple link (<a href="http://osm.org" target="_blank">http://osm.org</a>)'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle simple link with formatting', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple [[http://osm.org]] link'),
|
||||
'A simple <a target="_blank" href="http://osm.org">http://osm.org</a> link'
|
||||
'A simple <a href="http://osm.org" target="_blank">http://osm.org</a> link'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle simple link with formatting and content', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple [[http://osm.org|link]]'),
|
||||
'A simple <a target="_blank" href="http://osm.org">link</a>'
|
||||
'A simple <a href="http://osm.org" target="_blank">link</a>'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle simple link followed by a carriage return', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple link http://osm.org\nAnother line'),
|
||||
'A simple link <a target="_blank" href="http://osm.org">http://osm.org</a><br>\nAnother line'
|
||||
'A simple link <a href="http://osm.org" target="_blank">http://osm.org</a><br>\nAnother line'
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -108,28 +108,28 @@ describe('L.Util', function () {
|
|||
it('should handle iframe', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple iframe: {{{http://osm.org/pouet.html}}}'),
|
||||
'A simple iframe: <div><iframe frameborder="0" src="http://osm.org/pouet.html" width="100%" height="300px"></iframe></div>'
|
||||
'A simple iframe: <div><iframe src="http://osm.org/pouet.html" width="100%" height="300px" frameborder="0"></iframe></div>'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle iframe with height', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple iframe: {{{http://osm.org/pouet.html|200}}}'),
|
||||
'A simple iframe: <div><iframe frameborder="0" src="http://osm.org/pouet.html" width="100%" height="200px"></iframe></div>'
|
||||
'A simple iframe: <div><iframe src="http://osm.org/pouet.html" width="100%" height="200px" frameborder="0"></iframe></div>'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle iframe with height and width', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple iframe: {{{http://osm.org/pouet.html|200*400}}}'),
|
||||
'A simple iframe: <div><iframe frameborder="0" src="http://osm.org/pouet.html" width="400px" height="200px"></iframe></div>'
|
||||
'A simple iframe: <div><iframe src="http://osm.org/pouet.html" width="400px" height="200px" frameborder="0"></iframe></div>'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle iframe with height with px', function () {
|
||||
assert.equal(
|
||||
L.Util.toHTML('A simple iframe: {{{http://osm.org/pouet.html|200px}}}'),
|
||||
'A simple iframe: <div><iframe frameborder="0" src="http://osm.org/pouet.html" width="100%" height="200px"></iframe></div>'
|
||||
'A simple iframe: <div><iframe src="http://osm.org/pouet.html" width="100%" height="200px" frameborder="0"></iframe></div>'
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -138,7 +138,7 @@ describe('L.Util', function () {
|
|||
L.Util.toHTML(
|
||||
'A simple iframe: {{{https://osm.org/?url=https%3A//anotherurl.com}}}'
|
||||
),
|
||||
'A simple iframe: <div><iframe frameborder="0" src="https://osm.org/?url=https%3A//anotherurl.com" width="100%" height="300px"></iframe></div>'
|
||||
'A simple iframe: <div><iframe src="https://osm.org/?url=https%3A//anotherurl.com" width="100%" height="300px" frameborder="0"></iframe></div>'
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -147,7 +147,7 @@ describe('L.Util', function () {
|
|||
L.Util.toHTML(
|
||||
'A double iframe: {{{https://osm.org/pouet}}}{{{https://osm.org/boudin}}}'
|
||||
),
|
||||
'A double iframe: <div><iframe frameborder="0" src="https://osm.org/pouet" width="100%" height="300px"></iframe></div><div><iframe frameborder="0" src="https://osm.org/boudin" width="100%" height="300px"></iframe></div>'
|
||||
'A double iframe: <div><iframe src="https://osm.org/pouet" width="100%" height="300px" frameborder="0"></iframe></div><div><iframe src="https://osm.org/boudin" width="100%" height="300px" frameborder="0"></iframe></div>'
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -156,14 +156,14 @@ describe('L.Util', function () {
|
|||
L.Util.toHTML(
|
||||
'A phrase with a [[http://iframeurl.com?to=http://another.com]].'
|
||||
),
|
||||
'A phrase with a <a target="_blank" href="http://iframeurl.com?to=http://another.com">http://iframeurl.com?to=http://another.com</a>.'
|
||||
'A phrase with a <a href="http://iframeurl.com?to=http://another.com" target="_blank">http://iframeurl.com?to=http://another.com</a>.'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#escapeHTML', function () {
|
||||
it('should escape HTML tags', function () {
|
||||
assert.equal(L.Util.escapeHTML('<a href="pouet">'), '<a href="pouet">')
|
||||
assert.equal(L.Util.escapeHTML('<span onload="alert(oups)">'), '<span></span>')
|
||||
})
|
||||
|
||||
it('should not fail with int value', function () {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<script src="../vendors/formbuilder/Leaflet.FormBuilder.js"></script>
|
||||
<script src="../vendors/measurable/Leaflet.Measurable.js"></script>
|
||||
<script src="../vendors/locatecontrol/L.Control.Locate.js"></script>
|
||||
<script src="../vendors/dompurify/purify.js"></script>
|
||||
<script src="../js/umap.core.js"></script>
|
||||
<script src="../js/umap.autocomplete.js"></script>
|
||||
<script src="../js/umap.popup.js"></script>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<script src="{{ STATIC_URL }}umap/vendors/togpx/togpx.js"></script>
|
||||
<script src="{{ STATIC_URL }}umap/vendors/tokml/tokml.js"></script>
|
||||
<script src="{{ STATIC_URL }}umap/vendors/locatecontrol/L.Control.Locate.js"></script>
|
||||
<script src="{{ STATIC_URL }}umap/vendors/dompurify/purify.js"></script>
|
||||
{% endcompress %}
|
||||
{% if locale %}
|
||||
<script src="{{ STATIC_URL }}umap/locale/{{ locale }}.js"></script>
|
||||
|
|
Loading…
Reference in a new issue