fix(security): harden schema-preview webviews (CSP + HTML escaping) - #10
Conversation
…ME; keep extension description only
…ilent detail-truncation
Both preview webviews run with enableScripts:true but shipped no
Content-Security-Policy and interpolated CLI-derived 'detect' output
(sourceFormat) and the file name into their HTML unescaped -- an
injection vector into a script-enabled webview. Add a strict CSP
(default-src none; nonce'd inline script; inline styles only),
consistently HTML-escape sourceFormat/fileName/results via a shared
escapeHtml, and nonce the inline scripts.
Also fix a silent-failure bug in previewPanel: detectDetails.split('\\n')
split on the literal two-char sequence backslash-n instead of a newline,
so the intended 3-line truncation never took effect and the full detect
output was rendered. Now splits on '\n'.
Adds 3 network-free source-level regression guards (CSP+nonce present,
sourceFormat escaped, newline split).
Coding-Dev-Tools
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (Pre-PR Code Analyzer)
Solid security hardening of both schema-preview webviews: strict CSP with a per-script nonce, full HTML escaping of all interpolated values (sourceFormat, fileName, detectDetails, conversion results), and a real fix for the split('\n') silent-failure bug (was splitting on the literal two-char string instead of real newlines). Source-level regression tests lock in the CSP/escaping/truncation behavior. No logic or security regressions. Mergeable.
Coding-Dev-Tools
left a comment
There was a problem hiding this comment.
🤖 Pre-PR Code Review — APPROVE (pending 6h / 3-reviewer gate)
Security hardening (CSP + HTML escaping) — sound.
getNonce()+ strict CSP (default-src 'none', nonce'd inline scripts,style-srcfromcspSource) applied to bothpreviewPanelandschemaEditorProviderwebviews. Correctly addresses the priorenableScripts+ unescaped-content XSS hole.escapeHtml()now also escapes'(') — good.- Fixed a silent-failure bug:
detectDetailsnow splits on a real newline instead of the literal string'\n'. - 3 network-free regression guards added (CSP+nonce present, sourceFormat escaped, real-newline split).
CI: test (18 & 20) ✅, ensure-pr ✅. No security-scan findings.
Diff is well-scoped, tested, and reduces attack surface. Recommend merge once the 6h-age / 3-distinct-contributor gate clears (self-approval embargo: posted as comment, not formal approval).
Coding-Dev-Tools
left a comment
There was a problem hiding this comment.
Pre-PR Review (Reviewer: Pre-PR Code Analyzer) — Verdict: APPROVE (pending 6h / 3-reviewer gate)
Sound webview security hardening:
• Per-render CSP <meta http-equiv="Content-Security-Policy"> with a 32-char random nonce on inline <script> (default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-…').
• escapeHtml now also escapes apostrophes (was missing ').
• sourceFormat was previously interpolated UNESCAPED into the badge span — a real XSS/breakout hole — now escaped.
• detectDetails truncation corrected to split on REAL newlines (previously split on the literal two-char string \n, a no-op bug).
Regression tests updated/added (CSP nonce presence, escapeHtml coverage, real-newline split). CI is green (all test runs SUCCESS).
No blockers. Single author + young → formal approval withheld per policy; recommend merge once the gate clears.
Coding-Dev-Tools
left a comment
There was a problem hiding this comment.
Pre-PR review (Pre-PR Code Analyzer) — VERDICT: COMMENT (no blocker found; cannot formally APPROVE: single author, <3 distinct contributors per merge policy).
Solid webview hardening. A 32-char CSP nonce is generated per render and applied via <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src {csp} 'unsafe-inline'; script-src 'nonce-{nonce}';">, and the inline <script nonce=...> is whitelisted — this correctly blocks injected markup from executing. escapeHtml now also escapes the single quote (' -> '), and the detectDetails.split('\\n') -> .split('\n') fix corrects a real bug where the old code split on a literal backslash-n rather than real newlines.
Minor (non-blocking): style-src {csp} 'unsafe-inline' still permits inline styles; acceptable for VS Code webviews but could be tightened to a nonce/sha later.
Looks merge-ready once contributor/sign-off gates are satisfied.
Coding-Dev-Tools
left a comment
There was a problem hiding this comment.
Review Verdict: APPROVE (posted as COMMENT due to self-review embargo)
✅ Security Hardening — Well-Executed CSP + Nonce + Escaping
This PR adds Content-Security-Policy with per-request nonce generation and comprehensive HTML escaping to two VS Code webview panels:
src/panels/previewPanel.tssrc/providers/schemaEditorProvider.ts
Changes are correct and complete:
getNonce(): 32-char alphanumeric — sufficient entropy for CSP nonce.cspMeta(nonce): Strict policy —default-src 'none',style-srcwith webview cspSource + unsafe-inline (required for VS Code theming),script-src 'nonce-{nonce}'only.escapeHtml(): Escapes&,<,>,",'— covers all HTML context injection vectors.- All dynamic interpolations (
sourceFormat,fileName,detectDetails,c.error,c.result) now useescapeHtml(). - Inline
<script>tags carry the nonce:<script nonce="${nonce}">.
Regression tests added (tests/smoke.test.js):
- CSP + nonce presence guard
sourceFormatescaping guard (prevents unescaped interpolation)- Newline split correctness (
split('\n')notsplit('\\n'))
⚠️ Minor Nit
README brand references removed ("DevForge", "Revenue Holdings") — cosmetic, not a blocker.
Merge Readiness
- Security fix is correct, tested, and defense-in-depth.
- All CI passing (lint, test matrices).
- No merge conflicts.
Recommendation: APPROVE once title/diff alignment is confirmed (no mismatch observed).
⚖️ Council Gate Verdict: APPROVERisk level: Per-model scores
Rationales
Engraphis ref: not persisted (hook unavailable) Automated multi-model council review. APPROVE/APPROVE_WITH_NITS → |
⚖️ Council Gate Verdict: APPROVERisk level: Per-model scores
Rationales
Engraphis ref: not persisted (hook unavailable) Automated multi-model council review (NVIDIA Nemotron/LLaMA). APPROVE / APPROVE_WITH_NITS → |
⚖️ Council Gate Verdict: APPROVERisk level: Per-model scores
Rationales
Engraphis ref: not persisted (hook unavailable) Automated multi-model council review (NVIDIA Nemotron/LLaMA). APPROVE / APPROVE_WITH_NITS → |
Summary
Hardens both schema-preview webviews in the VS Code extension:
previewPanelandschemaEditorProvider— previously the webviews were created withenableScriptsand unescaped content.sourceFormat,fileName, results) via a sharedescapeHtml()helper instead of injecting them raw into the enableScripts webviews (prevents stored XSS from untrusted schema sources / issue titles).previewPanelsplitdetectDetailson the literal string\nso the 3-line truncation no-op'd; now splits on a real newline.tsc+eslint+ tests all green (5/5).Closes the
auto-pr-pendinggap left from the 2026-07-11 token-expired run. Builds on merged PR #9.Test plan
npm run compile/tsc -p ./strict cleaneslint0 errorstests/smoke.test.jspasses (5/5)