Skip to content

Fix critical + high severity issues from code audit (B1–B5) - #365

Merged
andrewklingelhofer merged 2 commits into
mainfrom
fix/audit-critical-high
Jul 18, 2026
Merged

Fix critical + high severity issues from code audit (B1–B5)#365
andrewklingelhofer merged 2 commits into
mainfrom
fix/audit-critical-high

Conversation

@andrewklingelhofer

Copy link
Copy Markdown
Contributor

Summary

Fixes the Critical and 4 High-severity findings from a full code audit of the daemon. Each was independently verified against source before and after the fix.

Severity Bug File Fix
🔴 Critical RCE via issue title in tmux pane script src/cli/spawn.ts buildPaneBanner() single-quotes the untrusted window name via shellEscape, so $(…)/backticks in a title can no longer execute as shell (also de-dups the two identical script branches)
🟠 High Execution drops committed-but-unpushed work src/runner/execution.ts When commits exist but the branch isn't on the remote, salvage (push + draft PR) instead of returning null into the cleanup-and-delete path
🟠 High Reflected XSS via window.__CRITTERS__ bootstrap src/dashboard/main-page.ts Escape </>/&/U+2028-9 to \uXXXX in the serialized JSON so a path-segment identifier can't break out of the inline <script>
🟠 High clean --panes kills live critters on a health blip src/cli-clean.ts Fail closed: distinguish connection-refused (daemon down → safe) from timeout/non-200 (daemon maybe-alive → abort)
🟠 High Transient tracker error flips success → failure src/unified-spawner.ts applySuccessOutcome() wraps the four success/merged/needs-changes transitions defensively, mirroring the already-guarded failure path

Tests

New src/__tests__/audit-fixes.test.ts: B1 (banner neutralizes $(…)/backticks/quotes), B3 (escapes </script>, JSON round-trips to original values), B4 (refused-vs-timeout classification, fail-closed default).

typecheck clean · lint clean · 850 tests pass (842 existing + 8 new).

Notes

  • B2/B5 live in the heavy spawner/runner path; covered by typecheck + the existing salvagePartialProgress git-harness tests + code review rather than a dedicated unit test.
  • Remaining audit findings (B6–B22, resilience, ~1,800 LOC of dead-code removal) are landing as separate focused PRs.

🤖 Generated with Claude Code

Addresses the Critical and 4 High-severity findings from a full code audit
of the daemon. Each was independently verified against source.

- B1 (Critical): RCE via issue title in the tmux pane script. Untrusted
  titles were interpolated into a double-quoted `echo -e` with only quote
  escaping, so `$(…)`/backticks executed as shell. Extract buildPaneBanner()
  which single-quotes the name via shellEscape (also de-dups the two script
  branches).
- B2 (High): Execution success path discarded committed-but-unpushed work.
  Now salvages (push + draft PR) instead of returning null into cleanup.
- B3 (High): Reflected XSS via the window.__CRITTERS__ bootstrap. Escape
  script-breaking chars (<, >, &, U+2028/9) in the serialized JSON.
- B4 (High): `clean --panes` could kill live critters on a transient health
  blip. Distinguish connection-refused (down) from timeout/non-200 (maybe
  alive) and fail closed.
- B5 (High): Transient tracker error on a terminal success transition flipped
  a real success into the failure path. Wrap success outcomes defensively,
  mirroring the already-guarded failure path.

Adds regression tests for B1, B3, B4. typecheck + lint clean, 850 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
resolvePhaseMcpConfig expands ~ via node:os homedir(); the test compared it to
process.env.HOME. Bun caches homedir() on first call and ignores later
process.env.HOME mutations, so when a sibling test changes HOME they diverge on
Linux CI and this test fails (works on macOS by execution-order luck). Assert
against the same homedir() the implementation uses.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@andrewklingelhofer
andrewklingelhofer merged commit 8dd8a5b into main Jul 18, 2026
1 check passed
@andrewklingelhofer
andrewklingelhofer deleted the fix/audit-critical-high branch July 18, 2026 17:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant