diff --git a/public/app.js b/public/app.js index 6ca80cd0..e5677a3f 100644 --- a/public/app.js +++ b/public/app.js @@ -1281,7 +1281,7 @@ function navigate() { else { app.setAttribute('tabindex', '-1'); app.focus({ preventScroll: true }); } }); } else { - app.innerHTML = `

${route}

Page not yet implemented.

`; + app.innerHTML = `

${escapeHtml(route)}

Page not yet implemented.

`; } } diff --git a/tests/unit/test-xss-escape-sinks.js b/tests/unit/test-xss-escape-sinks.js index 350927cc..5867f9ee 100644 --- a/tests/unit/test-xss-escape-sinks.js +++ b/tests/unit/test-xss-escape-sinks.js @@ -588,6 +588,18 @@ test('loadDetail catch block does NOT interpolate e.message into innerHTML', () 'loadDetail missing textContent assignment for e.message: ' + body); }); +// ========================================================================= +// E. URL-derived strings +// ========================================================================= +console.log('\n=== E. URL-derived strings ==='); + +test('app.js unknown-route page escapes the route taken from location.hash', () => { + const html = evalTemplate('public/app.js', + /(

\$\{[^}]*route[^}]*\}<\/h2>

Page not yet implemented\.<\/p><\/div>)/, + { route: TAG_PAYLOAD + ATTR_PAYLOAD }); + assertNoXss(html, 'app.js unknown route'); +}); + // ========================================================================= // SUMMARY // =========================================================================