fix: make sure we escape all innerHTML calls

This commit is contained in:
Yohan Boniface 2024-04-30 23:19:19 +02:00
parent cdb46752a9
commit dccb93c8a8
7 changed files with 85 additions and 57 deletions

View file

@ -669,20 +669,29 @@ const ControlsMixin = {
L.DomUtil.createTitle(container, this.options.name, 'icon-caption') L.DomUtil.createTitle(container, this.options.name, 'icon-caption')
this.permissions.addOwnerLink('h5', container) this.permissions.addOwnerLink('h5', container)
if (this.options.description) { if (this.options.description) {
const description = L.DomUtil.create('div', 'umap-map-description', container) const description = L.DomUtil.element(
description.innerHTML = U.Utils.toHTML(this.options.description) 'div',
{
className: 'umap-map-description',
safeHTML: U.Utils.toHTML(this.options.description),
},
container
)
} }
const datalayerContainer = L.DomUtil.create('div', 'datalayer-container', container) const datalayerContainer = L.DomUtil.create('div', 'datalayer-container', container)
this.eachVisibleDataLayer((datalayer) => { this.eachVisibleDataLayer((datalayer) => {
if (!datalayer.options.inCaption) return if (!datalayer.options.inCaption) return
const p = L.DomUtil.create('p', 'datalayer-legend', datalayerContainer), const p = L.DomUtil.create('p', 'datalayer-legend', datalayerContainer),
legend = L.DomUtil.create('span', '', p), legend = L.DomUtil.create('span', '', p),
headline = L.DomUtil.create('strong', '', p), headline = L.DomUtil.create('strong', '', p)
description = L.DomUtil.create('span', '', p)
datalayer.onceLoaded(function () { datalayer.onceLoaded(function () {
datalayer.renderLegend(legend) datalayer.renderLegend(legend)
if (datalayer.options.description) { if (datalayer.options.description) {
description.innerHTML = U.Utils.toHTML(datalayer.options.description) L.DomUtil.element(
'span',
{ safeHTML: U.Utils.toHTML(datalayer.options.description) },
p
)
} }
}) })
datalayer.renderToolbox(headline) datalayer.renderToolbox(headline)
@ -692,11 +701,12 @@ const ControlsMixin = {
credits = L.DomUtil.createFieldset(creditsContainer, L._('Credits')) credits = L.DomUtil.createFieldset(creditsContainer, L._('Credits'))
title = L.DomUtil.add('h5', '', credits, L._('User content credits')) title = L.DomUtil.add('h5', '', credits, L._('User content credits'))
if (this.options.shortCredit || this.options.longCredit) { if (this.options.shortCredit || this.options.longCredit) {
L.DomUtil.add( L.DomUtil.element(
'p', 'p',
'', {
credits, safeHTML: U.Utils.toHTML(this.options.longCredit || this.options.shortCredit),
U.Utils.toHTML(this.options.longCredit || this.options.shortCredit) },
credits
) )
} }
if (this.options.licence) { if (this.options.licence) {
@ -718,21 +728,26 @@ const ControlsMixin = {
L.DomUtil.create('hr', '', credits) L.DomUtil.create('hr', '', credits)
title = L.DomUtil.create('h5', '', credits) title = L.DomUtil.create('h5', '', credits)
title.textContent = L._('Map background credits') title.textContent = L._('Map background credits')
const tilelayerCredit = L.DomUtil.create('p', '', credits), const tilelayerCredit = L.DomUtil.create('p', '', credits)
name = L.DomUtil.create('strong', '', tilelayerCredit), L.DomUtil.element(
attribution = L.DomUtil.create('span', '', tilelayerCredit) 'strong',
name.textContent = `${this.selected_tilelayer.options.name} ` { textContent: `${this.selected_tilelayer.options.name} ` },
attribution.innerHTML = this.selected_tilelayer.getAttribution() tilelayerCredit
)
L.DomUtil.element(
'span',
{ safeHTML: this.selected_tilelayer.getAttribution() },
tilelayerCredit
)
L.DomUtil.create('hr', '', credits) L.DomUtil.create('hr', '', credits)
const umapCredit = L.DomUtil.create('p', '', credits), const urls = {
urls = { leaflet: 'http://leafletjs.com',
leaflet: 'http://leafletjs.com', django: 'https://www.djangoproject.com',
django: 'https://www.djangoproject.com', umap: 'http://wiki.openstreetmap.org/wiki/UMap',
umap: 'http://wiki.openstreetmap.org/wiki/UMap', changelog: 'https://umap-project.readthedocs.io/en/master/changelog/',
changelog: 'https://umap-project.readthedocs.io/en/master/changelog/', version: this.options.umap_version,
version: this.options.umap_version, }
} const creditHTML = L._(
umapCredit.innerHTML = L._(
` `
Powered by <a href="{leaflet}">Leaflet</a> and Powered by <a href="{leaflet}">Leaflet</a> and
<a href="{django}">Django</a>, <a href="{django}">Django</a>,
@ -741,6 +756,7 @@ const ControlsMixin = {
`, `,
urls urls
) )
L.DomUtil.element('p', { innerHTML: creditHTML }, credits)
this.panel.open({ content: container }) this.panel.open({ content: container })
}, },
@ -1052,16 +1068,16 @@ U.AttributionControl = L.Control.Attribution.extend({
// Use our own container, so we can hide/show on small screens // Use our own container, so we can hide/show on small screens
const credits = this._container.innerHTML const credits = this._container.innerHTML
this._container.innerHTML = '' this._container.innerHTML = ''
const container = L.DomUtil.add( const container = L.DomUtil.create('div', 'attribution-container', this._container)
'div', container.innerHTML = credits
'attribution-container',
this._container,
credits
)
const shortCredit = this._map.getOption('shortCredit'), const shortCredit = this._map.getOption('shortCredit'),
captionMenus = this._map.getOption('captionMenus') captionMenus = this._map.getOption('captionMenus')
if (shortCredit) { if (shortCredit) {
L.DomUtil.add('span', '', container, `${U.Utils.toHTML(shortCredit)}`) L.DomUtil.element(
'span',
{ safeHTML: `${U.Utils.toHTML(shortCredit)}` },
container
)
} }
if (captionMenus) { if (captionMenus) {
const link = L.DomUtil.add('a', '', container, `${L._('About')}`) const link = L.DomUtil.add('a', '', container, `${L._('About')}`)

View file

@ -73,7 +73,7 @@ L.DomUtil.add = (tagName, className, container, content) => {
if (content.nodeType && content.nodeType === 1) { if (content.nodeType && content.nodeType === 1) {
el.appendChild(content) el.appendChild(content)
} else { } else {
el.innerHTML = content el.textContent = content
} }
} }
return el return el
@ -165,6 +165,11 @@ L.DomUtil.classIf = (el, className, bool) => {
L.DomUtil.element = (what, attrs, parent) => { L.DomUtil.element = (what, attrs, parent) => {
const el = document.createElement(what) const el = document.createElement(what)
if (attrs.innerHTML) {
attrs.innerHTML = U.Utils.escapeHTML(attrs.innerHTML)
} else if (attrs.safeHTML) {
attrs.innerHTML = attrs.safeHTML
}
for (const attr in attrs) { for (const attr in attrs) {
el[attr] = attrs[attr] el[attr] = attrs[attr]
} }

View file

@ -757,12 +757,12 @@ L.FormBuilder.FacetSearchChoices = L.FormBuilder.Element.extend({
}, },
buildLabel: function () { buildLabel: function () {
this.label = L.DomUtil.element('legend', {textContent: this.options.label}) this.label = L.DomUtil.element('legend', { textContent: this.options.label })
}, },
buildLi: function (value) { buildLi: function (value) {
const property_li = L.DomUtil.create('li', '', this.ul) const property_li = L.DomUtil.create('li', '', this.ul)
const label = L.DomUtil.add('label', '', property_li) const label = L.DomUtil.create('label', '', property_li)
const input = L.DomUtil.create('input', '', label) const input = L.DomUtil.create('input', '', label)
L.DomUtil.add('span', '', label, value) L.DomUtil.add('span', '', label, value)
@ -800,14 +800,14 @@ L.FormBuilder.MinMaxBase = L.FormBuilder.Element.extend({
build: function () { build: function () {
this.container = L.DomUtil.create('fieldset', 'umap-facet', this.parentNode) this.container = L.DomUtil.create('fieldset', 'umap-facet', this.parentNode)
this.container.appendChild(this.label) this.container.appendChild(this.label)
const {min, max, type} = this.options.criteria const { min, max, type } = this.options.criteria
this.type = type this.type = type
this.inputType = this.getInputType(this.type) this.inputType = this.getInputType(this.type)
const [minLabel, maxLabel] = this.getLabels() const [minLabel, maxLabel] = this.getLabels()
this.minLabel = L.DomUtil.create('label', '', this.container) this.minLabel = L.DomUtil.create('label', '', this.container)
this.minLabel.innerHTML = minLabel this.minLabel.textContent = minLabel
this.minInput = L.DomUtil.create('input', '', this.minLabel) this.minInput = L.DomUtil.create('input', '', this.minLabel)
this.minInput.type = this.inputType this.minInput.type = this.inputType
@ -817,9 +817,8 @@ L.FormBuilder.MinMaxBase = L.FormBuilder.Element.extend({
this.minInput.dataset.value = min this.minInput.dataset.value = min
} }
this.maxLabel = L.DomUtil.create('label', '', this.container) this.maxLabel = L.DomUtil.create('label', '', this.container)
this.maxLabel.innerHTML = maxLabel this.maxLabel.textContent = maxLabel
this.maxInput = L.DomUtil.create('input', '', this.maxLabel) this.maxInput = L.DomUtil.create('input', '', this.maxLabel)
this.maxInput.type = this.inputType this.maxInput.type = this.inputType
@ -834,7 +833,7 @@ L.FormBuilder.MinMaxBase = L.FormBuilder.Element.extend({
}, },
buildLabel: function () { buildLabel: function () {
this.label = L.DomUtil.element('legend', {textContent: this.options.label}) this.label = L.DomUtil.element('legend', { textContent: this.options.label })
}, },
toJS: function () { toJS: function () {
@ -974,22 +973,25 @@ L.FormBuilder.Range = L.FormBuilder.FloatInput.extend({
}, },
buildHelpText: function () { buildHelpText: function () {
const datalist = L.DomUtil.create(
'datalist',
'umap-field-datalist',
this.getHelpTextParent()
)
datalist.id = `range-${this.options.label || this.name}`
this.input.setAttribute('list', datalist.id)
let options = '' let options = ''
const step = this.options.step || 1, const step = this.options.step || 1
digits = step < 1 ? 1 : 0 const digits = step < 1 ? 1 : 0
const id = `range-${this.options.label || this.name}`
for (let i = this.options.min; i <= this.options.max; i += this.options.step) { for (let i = this.options.min; i <= this.options.max; i += this.options.step) {
options += `<option value="${i.toFixed(digits)}" label="${i.toFixed( options += `<option value="${i.toFixed(digits)}" label="${i.toFixed(
digits digits
)}"></option>` )}"></option>`
} }
datalist.innerHTML = options const datalist = L.DomUtil.element(
'datalist',
{
className: 'umap-field-datalist',
safeHTML: options,
id: id,
},
this.getHelpTextParent()
)
this.input.setAttribute('list', id)
L.FormBuilder.Input.prototype.buildHelpText.call(this) L.FormBuilder.Input.prototype.buildHelpText.call(this)
}, },
}) })

View file

@ -62,9 +62,14 @@ U.MapPermissions = L.Class.extend({
L.DomUtil.createTitle(container, L._('Update permissions'), 'icon-key') L.DomUtil.createTitle(container, L._('Update permissions'), 'icon-key')
if (this.isAnonymousMap()) { if (this.isAnonymousMap()) {
if (this.options.anonymous_edit_url) { if (this.options.anonymous_edit_url) {
const helpText = `${L._('Secret edit link:')}<br>${this.options.anonymous_edit_url const helpText = `${L._('Secret edit link:')}<br>${
}` this.options.anonymous_edit_url
L.DomUtil.add('p', 'help-text', container, helpText) }`
L.DomUtil.element(
'p',
{ className: 'help-text', innerHTML: helpText },
container
)
fields.push([ fields.push([
'options.edit_status', 'options.edit_status',
{ {

View file

@ -190,7 +190,7 @@ U.PopupTemplate.Table = U.PopupTemplate.BaseWithTitle.extend({
addRow: function (container, key, value) { addRow: function (container, key, value) {
const tr = L.DomUtil.create('tr', '', container) const tr = L.DomUtil.create('tr', '', container)
L.DomUtil.add('th', '', tr, key) L.DomUtil.add('th', '', tr, key)
L.DomUtil.add('td', '', tr, this.formatRow(key, value)) L.DomUtil.element('td', { innerHTML: this.formatRow(key, value) }, tr)
}, },
renderBody: function () { renderBody: function () {

View file

@ -143,7 +143,7 @@ U.Share = L.Class.extend({
} }
const iframeExporter = new U.IframeExporter(this.map) const iframeExporter = new U.IframeExporter(this.map)
const buildIframeCode = () => { const buildIframeCode = () => {
iframe.innerHTML = iframeExporter.build() iframe.textContent = iframeExporter.build()
exportUrl.value = window.location.protocol + iframeExporter.buildUrl() exportUrl.value = window.location.protocol + iframeExporter.buildUrl()
} }
buildIframeCode() buildIframeCode()

View file

@ -51,13 +51,13 @@ U.UI = L.Evented.extend({
close, close,
this this
) )
L.DomUtil.add('i', 'umap-close-icon', closeButton) L.DomUtil.create('i', 'umap-close-icon', closeButton)
const label = L.DomUtil.create('span', '', closeButton) const label = L.DomUtil.create('span', '', closeButton)
label.title = label.textContent = L._('Close') label.title = label.textContent = L._('Close')
L.DomUtil.add('div', '', this._alert, e.content) L.DomUtil.element('div', {innerHTML: e.content}, this._alert)
if (e.actions) { if (e.actions) {
let action, el, input let action, el, input
const form = L.DomUtil.add('div', 'umap-alert-actions', this._alert) const form = L.DomUtil.create('div', 'umap-alert-actions', this._alert)
for (let i = 0; i < e.actions.length; i++) { for (let i = 0; i < e.actions.length; i++) {
action = e.actions[i] action = e.actions[i]
if (action.input) { if (action.input) {
@ -97,7 +97,7 @@ U.UI = L.Evented.extend({
this.anchorTooltipAbsolute() this.anchorTooltipAbsolute()
} }
L.DomUtil.addClass(this.parent, 'umap-tooltip') L.DomUtil.addClass(this.parent, 'umap-tooltip')
this._tooltip.innerHTML = opts.content this._tooltip.innerHTML = U.Utils.escapeHTML(opts.content)
} }
this.TOOLTIP_ID = window.setTimeout(L.bind(showIt, this), opts.delay || 0) this.TOOLTIP_ID = window.setTimeout(L.bind(showIt, this), opts.delay || 0)
const id = this.TOOLTIP_ID const id = this.TOOLTIP_ID