mirror of
https://github.com/only-cli/oc.git
synced 2026-09-15 10:40:56 +02:00
fix: never let a '<' survive title flattening
CodeQL flagged the tag strip in plainTitle: requiring a closing '>' means a title whose markup never closes keeps its '<script' fragment. The '>' is optional now, so every '<' starts a strip and none reaches the results page, and the output is trimmed. Both call sites already HTML-escape the result; this hardens the flattening itself. Covered by a new test.
This commit is contained in:
+6
-2
@@ -151,8 +151,12 @@ export function searchIndex(index, query) {
|
||||
|
||||
// Titles in the index arrive as the HTML of the page's <h1>, markup and all
|
||||
// (docs.python.org wraps module names in <code> spans), so they are flattened
|
||||
// to text before they are placed on the results page.
|
||||
const plainTitle = (t) => String(t).replace(/<[^>]*>/g, '');
|
||||
// to text before they are placed on the results page. The '>' is optional so
|
||||
// a tag the title never closes is dropped too, not left standing as '<script'
|
||||
// for some later layer to trip on: a title is index data from the network,
|
||||
// and no '<' survives this. A literal less-than in a real title arrives as
|
||||
// '<', so nothing legitimate is lost.
|
||||
const plainTitle = (t) => String(t).replace(/<[^>]*>?/g, '').trim();
|
||||
|
||||
const escapeHTML = (s) => String(s)
|
||||
.replaceAll('&', '&').replaceAll('<', '<')
|
||||
|
||||
@@ -43,6 +43,15 @@ test('titles are flattened to text before they reach the results page', () => {
|
||||
assert.equal(found.docs[0].title, 'json - JSON encoder and decoder');
|
||||
});
|
||||
|
||||
test('a tag the title never closes is stripped, not left standing', () => {
|
||||
// 'json encoder <script src=' has no closing '>', so a strip that requires
|
||||
// one would hand '<script' onward. No '<' may survive the flattening.
|
||||
const nested = { ...INDEX, titles: ['json encoder <script src=', ...INDEX.titles.slice(1)] };
|
||||
const found = searchIndex(nested, 'json');
|
||||
assert.equal(found.docs[0].title, 'json encoder');
|
||||
assert.doesNotMatch(resultsToHTML(BASE, 'json', found, nested), /<script/);
|
||||
});
|
||||
|
||||
test('every word must match, and when none can, any-word results say so', () => {
|
||||
// 'json' hits doc 0, 'socket' hits doc 2, nothing hits both.
|
||||
const found = searchIndex(INDEX, 'json socket');
|
||||
|
||||
Reference in New Issue
Block a user