fix(ui): escape the route string on the unknown-route page (#2124)

`app.js` wrote the URL fragment into `innerHTML` as the heading of the
"Page not yet implemented" view for routes it does not know. Browsers
percent-encode `<`, `>` and `"` in fragments, so this is unlikely to be
exploitable today, but it is a plain `innerHTML` sink on a
URL-controlled string and costs one `escapeHtml()` to close.

**Tests:** one test added to `tests/unit/test-xss-escape-sinks.js` in
the file's existing style.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Mythos 5.1 <noreply@anthropic.com>
This commit is contained in:
nullrouten0andClaude Mythos 5.1 authored and GitHub committed 2026-10-08 15:56:30 +02:00
1 parent 7309bdb5a9
commit 523a5edfa1
2 files changed
+13 -1

No files matched your search

+1 -1
View File
@@ -1281,7 +1281,7 @@ function navigate() {
else { app.setAttribute('tabindex', '-1'); app.focus({ preventScroll: true }); }
});
} else {
app.innerHTML = `<div style="padding:40px;text-align:center;color:#6b7280"><h2>${route}</h2><p>Page not yet implemented.</p></div>`;
app.innerHTML = `<div style="padding:40px;text-align:center;color:#6b7280"><h2>${escapeHtml(route)}</h2><p>Page not yet implemented.</p></div>`;
}
}
+12
View File
@@ -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',
/(<div style="padding:40px;text-align:center;color:#6b7280"><h2>\$\{[^}]*route[^}]*\}<\/h2><p>Page not yet implemented\.<\/p><\/div>)/,
{ route: TAG_PAYLOAD + ATTR_PAYLOAD });
assertNoXss(html, 'app.js unknown route');
});
// =========================================================================
// SUMMARY
// =========================================================================