fix(duckduckgo): guard out-of-range numeric HTML entities#96583
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 26, 2026, 2:53 AM ET / 06:53 UTC. Summary PR surface: Source +6, Tests +14. Total +20 across 2 files. Reproducibility: yes. source-reproducible: current main routes DuckDuckGo title, URL, and snippet fields through unchecked numeric entity decoding that can throw or emit lone surrogates. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Next step before merge
Security Review detailsBest possible solution: Land this plugin-local decoder guard with regression coverage after maintainer review; keep Telegram and web-fetch decoder work in their own surface-specific PRs. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible: current main routes DuckDuckGo title, URL, and snippet fields through unchecked numeric entity decoding that can throw or emit lone surrogates. Is this the best way to solve the issue? Yes. The plugin-local code-point guard is the narrowest maintainable fix; a broader shared decoder refactor is unnecessary for this PR, and adjacent Telegram/web-fetch surfaces are tracked separately. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against e9f9a68d68c9. Label changesLabel justifications:
Evidence reviewedPR surface: Source +6, Tests +14. Total +20 across 2 files. View PR surface stats
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
5da140b to
7f8dc7b
Compare
decodeHtmlEntities decoded numeric entities with String.fromCodePoint(parseInt(...)) without a range check, so an out-of-range entity such as � or � threw RangeError and made the whole results page fail to parse. Validate the code point is within 0..0x10FFFF and keep the original entity text otherwise. Add a regression covering decimal and hex out-of-range entities plus a valid astral entity.
7f8dc7b to
7c44ffd
Compare
…6583) decodeHtmlEntities decoded numeric entities with String.fromCodePoint(parseInt(...)) without a range check, so an out-of-range entity such as � or � threw RangeError and made the whole results page fail to parse. Validate the code point is within 0..0x10FFFF and keep the original entity text otherwise. Add a regression covering decimal and hex out-of-range entities plus a valid astral entity.
…6583) decodeHtmlEntities decoded numeric entities with String.fromCodePoint(parseInt(...)) without a range check, so an out-of-range entity such as � or � threw RangeError and made the whole results page fail to parse. Validate the code point is within 0..0x10FFFF and keep the original entity text otherwise. Add a regression covering decimal and hex out-of-range entities plus a valid astral entity.
…6583) decodeHtmlEntities decoded numeric entities with String.fromCodePoint(parseInt(...)) without a range check, so an out-of-range entity such as � or � threw RangeError and made the whole results page fail to parse. Validate the code point is within 0..0x10FFFF and keep the original entity text otherwise. Add a regression covering decimal and hex out-of-range entities plus a valid astral entity.
…6583) decodeHtmlEntities decoded numeric entities with String.fromCodePoint(parseInt(...)) without a range check, so an out-of-range entity such as � or � threw RangeError and made the whole results page fail to parse. Validate the code point is within 0..0x10FFFF and keep the original entity text otherwise. Add a regression covering decimal and hex out-of-range entities plus a valid astral entity. (cherry picked from commit 2720ac0)
…6583) decodeHtmlEntities decoded numeric entities with String.fromCodePoint(parseInt(...)) without a range check, so an out-of-range entity such as � or � threw RangeError and made the whole results page fail to parse. Validate the code point is within 0..0x10FFFF and keep the original entity text otherwise. Add a regression covering decimal and hex out-of-range entities plus a valid astral entity.
Summary: DuckDuckGo's HTML entity decoder ran
String.fromCodePointon parsed numeric entities without validation, so out-of-range values threwRangeError(breaking the whole results-page parse) and surrogate-range values produced lone surrogates. This validates the code point (in range, non-surrogate) and otherwise keeps the original entity text.What Problem This Solves
DuckDuckGo HTML result parsing decoded numeric HTML entities with
String.fromCodePoint(...)without validating the parsed code point first.That meant hostile or malformed result text could break real parsing behavior:
�is outside Unicode range and throwsRangeError.�/�is in the UTF-16 surrogate range and decodes into a lone surrogate (\uD800), producing ill-formed output.😀should still decode to the complete emoji surrogate pair.Fix
The DuckDuckGo entity decoder now validates numeric entity code points before decoding:
0..0x10ffffare decoded0xd800..0xdfffare not decodedThis is a range-validation fix in
extensions/duckduckgo/src/ddg-client.ts. It does not use a truncation helper because this path is not slicing or truncating text; it is decoding HTML entities. The important invariant is the same UTF-16 safety rule: never emit a lone surrogate into parsed search-result text.Evidence
Command run from an isolated worktree checked out at
fix/duckduckgo-entity-range:Terminal output:
The demo calls the real fixed
testing.decodeHtmlEntitiesentry fromextensions/duckduckgo/src/ddg-client.ts. After the fix, surrogate-range and out-of-range entities stay escaped, valid emoji entities still decode, and every AFTER case reportslone-surrogate=false.Tests
Focused regression coverage is in
extensions/duckduckgo/src/ddg-search-provider.test.ts:Validation run:
Result: