Repository navigation
fix: close the tag-stripping gaps CodeQL keeps finding #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
b44d4ed
a7942d7
5de4d4e
32baf63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2673,14 +2673,32 @@ | |
| return lensDecode(s).replace(/\s+/g, " ").trim(); | ||
| } | ||
|
|
||
| // One pass of /<[^>]+>/g is ALREADY a fixpoint, and it is worth writing down why so | ||
| // nobody re-derives it: the match runs from a `<` to the next `>`, so any `<` that | ||
| // survives a pass has no `>` after it anywhere, and a second pass can never match. | ||
| // The classic re-formation bug (a pass eating an inner tag and closing an outer one | ||
| // out of the leftovers) needs a NARROWER pattern, like /<script>/g, to bite. | ||
| // | ||
| // The loop is here for the two readers who cannot see that argument: CodeQL, which | ||
| // flags every single-pass multi-character strip as js/incomplete-multi-character- | ||
| // sanitization, and the next person to swap this regex for a narrower one, where the | ||
| // proof stops holding and the loop starts earning its keep. It costs one extra scan | ||
| // that finds nothing. Bounded anyway, because this runs on third-party pages up to | ||
| // 2MB and an unbounded fixpoint over someone else's bytes is their budget to spend. | ||
| function lensStripTags(h, repl = "") { | ||
| let out = String(h), prev = null, guard = 0; | ||
| while (out !== prev && guard++ < 8) { prev = out; out = out.replace(/<[^>]+>/g, repl); } | ||
| return out; | ||
| } | ||
|
|
||
| // best-effort, dependency-free HTML→Markdown — roughly what a basic LLM | ||
| // scraper ingests. High-fidelity Readability/Turndown is a deliberate v2. | ||
| export function lensMarkdown(html, baseUrl) { | ||
| let s = html; | ||
| const b = s.match(/<body[^>]*>([\s\S]*)<\/body>/i); if (b) s = b[1]; | ||
| s = s.replace(/<!--[\s\S]*?-->/g, ""); | ||
| s = s.replace(/<(script|style|noscript|template|svg|head|nav|footer|aside)[\s\S]*?<\/\1>/gi, ""); | ||
| s = s.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, (m, i) => "\n\n```\n" + lensDecode(i.replace(/<[^>]+>/g, "")).replace(/\n+$/g, "") + "\n```\n\n"); | ||
| s = s.replace(/<!--[\s\S]*?(?:-->|--!>)/g, ""); | ||
|
oddharsh marked this conversation as resolved.
Dismissed
|
||
| s = s.replace(/<(script|style|noscript|template|svg|head|nav|footer|aside)\b[\s\S]*?<\/\1\s*>/gi, ""); | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. Same fixpoint loop, plus the end tag is 🤖 Addressed by Claude Code
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same correction as the sibling thread: the end tag fix ( 🤖 Addressed by Claude Code |
||
| s = s.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, (m, i) => "\n\n```\n" + lensDecode(lensStripTags(i)).replace(/\n+$/g, "") + "\n```\n\n"); | ||
| s = s.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, (m, i) => "`" + lensStripInline(i).trim() + "`"); | ||
| s = s.replace(/<br\s*\/?>/gi, "\n"); | ||
| s = s.replace(/<hr\s*\/?>/gi, "\n\n---\n\n"); | ||
|
|
@@ -2692,13 +2710,13 @@ | |
| s = s.replace(/<(strong|b)\b[^>]*>([\s\S]*?)<\/\1>/gi, (m, t, i) => "**" + lensStripInline(i).trim() + "**"); | ||
| s = s.replace(/<(em|i)\b[^>]*>([\s\S]*?)<\/\1>/gi, (m, t, i) => "*" + lensStripInline(i).trim() + "*"); | ||
| s = s.replace(/<\/(p|div|section|article|header|main|ul|ol|table|tr|h[1-6])>/gi, "\n\n"); | ||
| s = s.replace(/<[^>]+>/g, ""); | ||
| s = lensStripTags(s); | ||
| s = lensDecode(s); | ||
| s = s.replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").replace(/[ \t]{2,}/g, " "); | ||
| return s.trim(); | ||
| } | ||
|
|
||
| export function lensStripInline(h) { return lensDecode(String(h).replace(/<[^>]+>/g, " ")).replace(/\s+/g, " "); } | ||
| export function lensStripInline(h) { return lensDecode(lensStripTags(h, " ")).replace(/\s+/g, " "); } | ||
|
|
||
| export function lensTagAttr(tag, name) { const m = String(tag).match(new RegExp(name + "\\s*=\\s*(\"([^\"]*)\"|'([^']*)'|([^\\s>]+))", "i")); return m ? (m[2] ?? m[3] ?? m[4] ?? "") : ""; } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.