Repository navigation
Fix regex escapes collapsing in emitted client-side scripts - #33
Merged
Merged
Conversation
These regex literals live inside TypeScript template literals that emit
client-side JavaScript. A single backslash is not a recognized escape
sequence, so it collapses when the template is evaluated and the browser
receives the bare character instead of the character class.
library.ts kioskHtml /\D/g -> /D/g
library.ts manageHtml /^\d{4}-\d{2}-\d{2}$/ -> /^d{4}-d{2}-d{2}$/
admin-ui-v2.ts adminHtml /(\d{4})(\d{4})/ -> /(d{4})(d{4})/
Effects: the kiosk pairing PIN sanitizer stripped the letter D while
keeping hyphens, spaces and letters; the manage-page CSV export rejected
every real date and always threw "Choose a start and end date."; the
pairing PIN rendered without its group separator.
Escaping the backslash makes the emitted page receive \d and \D.
Left unchanged deliberately: the \n sequences in admin-ui-v3.ts and
admin-ui-v6.ts rely on this collapse to produce real newlines as
statement separators, and library.ts escapeHtml's /[&<>\"]/g collapses
to /[&<>"]/g, which is already the intended regex.
Verified by rendering kioskHtml(), manageHtml() and adminHtml() and
asserting on the served markup: the correct patterns are present, the
broken ones absent, the emitted sanitizer and date validator behave
correctly, and all inline script blocks still parse.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
flow | e6f1895 | Aug 11 2026, 12:01 PM |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Three regex literals live inside TypeScript template literals that emit client-side JavaScript. A single backslash is not a recognized escape sequence, so it collapses when the template is evaluated — the browser receives the bare character instead of the character class.
This is invisible to
tsc: the TypeScript compiles cleanly, and only the served page is wrong.What was broken
library.tskioskHtml/D/gDwhile keeping hyphens, spaces and letterslibrary.tsmanageHtml(×2)/^d{4}-d{2}-d{2}$/admin-ui-v2.tsadminHtml/(d{4})(d{4})/12345678instead of1234 5678(cosmetic)Fix
Escape the backslash so the emitted page receives
\d/\D. Three lines changed.Deliberately left unchanged
Not every single backslash in a template literal is a bug — some depend on the collapse:
admin-ui-v3.ts:7,admin-ui-v6.ts:10,admin-ui-v6.ts:18(×3) —\ncollapses to a real newline, which is exactly what's needed as a statement separator in the emitted JS. Escaping these to\\nwould emit a literal two-char\nand cause a syntax error.library.tsescapeHtml—/[&<>\"]/gcollapses to/[&<>"]/g; inside a character class"needs no escape, so the served regex is already correct.access-router.ts,admin.ts,admin-v2/v3/v4.tsare clean — all their backslashes are ordinary server-side TS regexes.How this was audited
Grep can't distinguish an emitted-script backslash from ordinary TS code, so the sweep used a lexer tracking template-literal nesting,
${}substitutions, strings, comments and regex literals, reporting each backslash's run length and enclosing template opener. All 13src/*.tsfiles, 25 backslashes total, each classification confirmed by reading its context.Verification
tsc --noEmiton the repo tsconfig — cleankioskHtml(),manageHtml(),adminHtml()and asserted on the served markup: correct patterns present, broken forms absent"12-34 567"→"1234567","abc12345678"→"12345678"2026-08-11→true,dddd-dd-dd→falsevm.Script; zero literal\nsequences leaked into outputFollow-up worth considering
This class of bug is invisible to
tscand will recur, since the codebase emits a lot of client-side JS through template literals. The audit scanner is ~60 lines and could run in CI as a guard — the rule being "an odd backslash run in template text is suspect unless followed byn".🤖 Generated with Claude Code