Skip to content

fix(security): harden sanitizers and regexes flagged by CodeQL#94

Merged
DavidBabinec merged 4 commits into
mainfrom
fix/codeql-sanitizer-hardening
Jun 29, 2026
Merged

fix(security): harden sanitizers and regexes flagged by CodeQL#94
DavidBabinec merged 4 commits into
mainfrom
fix/codeql-sanitizer-hardening

Conversation

@DavidBabinec

Copy link
Copy Markdown
Contributor

What

Resolves CodeQL code-scanning alerts across the sanitization and string-handling surfaces.

Real security fix

  • server/handlers/cms/svgSanitize.ts (Remove AI credential authentication selector #30–34) — the media-upload SVG sanitizer. Close-tag regexes now match </script foo>, </script\t\n>, and </script/> (HTML parsers end a tag at the first >; the old <\/script\s*> missed these — js/bad-tag-filter). Stripping now iterates to a fixpoint instead of a fixed 2 passes, so split-tag obfuscation (<scr<script>ipt>) can't survive (js/incomplete-multi-character-sanitization). Adds __tests__/svgSanitize.test.ts — the first test on this boundary.

Defense-in-depth

  • src/core/sanitize.ts (Add managed publishing MVP #1–5) — same close-tag fix + fixpoint loop in the stripHtmlFallback path (only used when no DOMPurify runtime exists); plain-text post-strip reuses it.

Correctness

Test infra

  • cms-handlers-capability-gated.test.ts skips __tests__ dirs (test files aren't routes).

Why

CodeQL flagged regex-based tag filtering and incomplete multi-character sanitization on the SVG upload boundary — the one path where these are genuinely security-relevant (uploads are served as image/svg+xml and embedded in published pages). The rest are low-risk hardenings and correctness fixes.

Not addressed (false positives — recommend dismissing in GitHub)

Verification

  • bun run build
  • bun run lint
  • bun test ✓ (5741 pass, 0 fail)

🤖 Generated with Claude Code

Address CodeQL code-scanning alerts across the sanitization and
string-handling surfaces:

- svgSanitize (upload boundary): close-tag regexes now match `</script foo>`
  / `</script\t\n>` / `</script/>` (bad-tag-filter), and stripping iterates to
  a fixpoint instead of a fixed 2 passes so split-tag obfuscation
  (`<scr<script>ipt>`) cannot survive. Adds the first test for this boundary.
- sanitize.ts fallback: same close-tag fix + fixpoint loop in the
  no-DOMPurify-runtime path; plain-text post-strip reuses it.
- markdownDocument.decodeEntities: decode `&amp;` last to stop the
  `&amp;lt;` -> `<` double-unescape (js/double-escaping).
- siteItemNames: drop dynamic RegExp for startsWith/slice.
- scaleModule / dependencyResolver: replace -> replaceAll.
- htmlPagePlan importer: harden script close-tag match.
- arch gate: cms-handlers-capability-gated skips __tests__ dirs.

Verified: bun run build, bun run lint, bun test (5741 pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/core/sanitize.ts Fixed
Comment thread src/core/sanitize.ts Fixed
CodeQL still flagged stripHtmlFallback (js/incomplete-multi-character-
sanitization) because the generic `<[^>]*>` tag strip isn't credited with
removing a residual `<script` / `<style` opener left by block removal.

Mirror the proven server SVG sanitizer: each block regex is now paired with
an explicit opener regex (`<script…>` / `<style…>`), making the dangerous
substring provably gone — the same shape CodeQL already accepts in
svgSanitize.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/core/sanitize.ts Fixed
Comment thread src/core/sanitize.ts Fixed
Comment thread src/core/sanitize.ts Fixed
Comment thread src/core/sanitize.ts Fixed
CodeQL's incomplete-multi-character-sanitization didn't credit the inline
chained-replace loop body as a fixpoint, re-flagging the same lines. Mirror
the exact structure CodeQL already accepts in svgSanitize.ts: extract the
replace chain into stripHtmlOnce(), and loop `current = stripHtmlOnce(current)`
to a fixpoint. Behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/core/sanitize.ts Fixed
Comment thread src/core/sanitize.ts Fixed
Comment thread src/core/sanitize.ts Fixed
Comment thread src/core/sanitize.ts Fixed
Comment thread src/core/sanitize.ts Fixed
CodeQL evaluates each .replace() independently and only credits a replace
that is itself looped to a fixpoint — an outer loop around a chain, or the
chain in a helper, left every individual replace flagged. Restructure
stripHtmlFallback into three single-literal-regex do-while-until-stable loops
(script block, style block, remaining tags), the exact shape CodeQL's qhelp
documents as the complete fix. Behavior unchanged: script/style content is
still removed (covered by the no-DOM server-runtime test).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DavidBabinec DavidBabinec marked this pull request as ready for review June 29, 2026 14:31
@DavidBabinec DavidBabinec merged commit d32a262 into main Jun 29, 2026
6 checks passed
@DavidBabinec DavidBabinec deleted the fix/codeql-sanitizer-hardening branch June 29, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants