Conversation
…secure-randomness / shell-quote)
This is a defence-in-depth pass that closes every open CodeQL finding
on the develop branch. No user-visible behaviour changes.
media/session.js
- esc() now also escapes " and ' so it is safe to use in HTML attribute
context, not just element text. This fixes the underlying cause of
the five js/incomplete-html-attribute-sanitization alerts at
lines 104, 166, 172, 218, 299.
- addU / addA: pass customTs (history-replay timestamps) through esc()
before interpolating into innerHTML (was bare).
- addTStart: escape lbl and the args.path branch of hint (was bare).
These were the user-controlled values flagged by js/xss at line 78.
- addImg: escape u (the data: URL / file path) before interpolating
into <img src=...>. Closes the js/xss alert at line 298.
- vcs_state additions/deletions span: coerce to integers via
Number(x)|0 before HTML concat; closes the js/xss alert at line 510.
- The Report Bug + View Full + Crash buttons used inline
onclick="rptTool(this,'X','Y')" with brittle JS-string escaping
via cleanR.replace(/'/g,"\\'") -> CodeQL flagged the regex pair as
js/identity-replacement and the surrounding template as
js/incomplete-html-attribute-sanitization. Replaced with
data-action / data-tool / data-output / data-text / data-title /
data-detail / data-repo attributes (HTML-escaped via esc()) plus
one delegated document.addEventListener('click',...) handler that
routes by btn.dataset.action. The brittle regex is gone; values
cross the JS boundary as plain DOM attributes.
src/ChatStreamConsumer.ts
- generateSessionId() now uses crypto.randomUUID() instead of
Math.random(); the 12-char hex slice is preserved so log lines
remain readable. Closes both js/insecure-randomness alerts.
src/extension.ts (specsmith.runPreflight) and src/GovernancePanel.ts
(case 'agentTask')
- The double-quoted shell argument was previously built with
utterance.replace(/"/g,'\\"') which leaves a trailing backslash able
to escape the closing quote. Now backslashes are escaped first, then
double quotes. Closes both js/incomplete-sanitization alerts.
src/test/session-logic.test.ts
- The two integrity tests that scan the bundle for stray escaped
backticks used /<script>...<\/script>/g which CodeQL flags as
js/bad-tag-filter because it does not match upper-case <SCRIPT>.
Switched to /<script\b[^>]*>...<\/script\s*>/gi. We never emit
uppercase tags, so behaviour is unchanged; the alert is closed.
Verification
- npm run lint: clean (tsc --noEmit).
- npm run build: 290.9 KB bundle.
- npm test: 144 passing.
Co-Authored-By: Oz <oz-agent@warp.dev>
tbitcs
added a commit
that referenced
this pull request
May 4, 2026
Bumps `package.json` from 0.10.0 to 0.10.1 and renames the existing `[Unreleased]` CHANGELOG section to `[0.10.1] - 2026-05-04`. The 0.10.0 tag captured the multi-agent + BYOE work (PRs #45/#47/#48); this point release rolls in the security hardening from #49 (17 CodeQL alerts closed) and the regex follow-up. Validation: - npm run lint: clean. - npm test: 144 passing. Co-Authored-By: Oz <oz-agent@warp.dev>
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.
What
Closes the 17 open CodeQL code-scanning alerts on
BitConcepts/specsmith-vscodedevelopso we hit zero open alertsacross the org (CLI is already at zero). No user-visible behaviour
changes — this is a defence-in-depth pass.
js/insecure-randomness×2src/ChatStreamConsumer.ts:155, 232generateSessionId()usescrypto.randomUUID()instead ofMath.random().js/xss×5 (error)media/session.js:20, 25, 78, 298, 510customTs,lbl,args.pathhint,addImgsrc, vcs additions/deletions) throughesc()/ numeric coercion.js/incomplete-html-attribute-sanitization×5media/session.js:104, 166, 172, 218, 299esc()now escapes"and'too. The Report Bug / View Full / Crash buttons no longer use inlineonclick="rptTool(this,'X','Y')"; they switched todata-action/data-*attributes plus a single delegated click listener, removing the brittle JS-string smuggling.js/identity-replacement×1media/session.js:172cleanR.replace(/'/g,"\\'")pair the linter flagged.js/incomplete-sanitization×2src/extension.ts:584(preflight),src/GovernancePanel.ts:528(agent task)\before"so a trailing backslash can no longer escape the closing quote.js/bad-tag-filter×2src/test/session-logic.test.ts:238, 249/<script\b[^>]*>...<\/script\s*>/giinstead of the lower-case-only literal regex.Why
The 17 alerts came from CodeQL's default JS query suite. The XSS class
is the highest-priority subset — when the chat panel rendered an
agent-controlled
customTs,args.path, image data URL, or vcs diffcounts, a hostile LLM response could in theory smuggle markup into the
webview. The shell-quote alerts in
extension.tsandGovernancePanel.tscould likewise be exploited by a hostile utterancethat ends in
\. The insecure-randomness alerts on session ids aremostly cosmetic (they're not used for auth) but
crypto.randomUUID()is a one-line drop-in.
How
media/session.jsesc()now also escapes"and'so it is safe in attributecontext, not just element-text context.
addU/addAescapecustomTs(history-replay timestamps).addTStartescapeslbland theargs.pathhint branch.addImgescapes theu(data:/file path) before<img src=…>.vcs_stateadditions/deletions go throughNumber(x)|0beforeHTML concatenation.
data-action="rptTool"/data-action="rptCrash"/data-action="viewFull"etc., wired through onedocument.addEventListener('click', …)handler at the bottom ofthe file. The brittle
cleanR.replace(/'/g,"\\'")is gone.src/ChatStreamConsumer.ts—generateSessionId()now does'sess_' + crypto.randomUUID().replace(/-/g, '').slice(0, 12).src/extension.ts(specsmith.runPreflight) andsrc/GovernancePanel.ts(case 'agentTask') — escapebackslashes before double quotes when building the shell argument.
src/test/session-logic.test.ts— case-insensitive<script>regex with explicit
\band\s*>boundaries.CHANGELOG.md— added a### Securitybullet under[Unreleased]describing the fix.Verification
npm run lint(tsc --noEmit): clean.npm run build(esbuild): 290.9 KB bundle, no errors.npm test: 144 passing (no test changes, no regressions).gh api /repos/BitConcepts/specsmith-vscode/code-scanning/alerts?state=openshould re-scan and returnlength=0once the CodeQL workflow finishes.Risk
Low. The biggest behavioural delta is the strengthened
esc()— it nowdouble-encodes
"and'in attribute strings, which the browserdecodes back transparently. The
data-attribute click handlers are wired via the same
vscode.postMessagecalls as before.
Co-Authored-By: Oz oz-agent@warp.dev