Skip to content

Security: ExecApprovalPolicy bypass, thread safety, dispose races#62

Merged
shanselman merged 2 commits intomasterfrom
security/consensus-fixes-triple-review
Mar 18, 2026
Merged

Security: ExecApprovalPolicy bypass, thread safety, dispose races#62
shanselman merged 2 commits intomasterfrom
security/consensus-fixes-triple-review

Conversation

@shanselman
Copy link
Collaborator

Triple-Model Consensus Security Fixes

Identified by independent reviews from Claude Opus, GPT Codex, and Gemini - issues where 2+ models agreed.

1. ExecApprovalPolicy bypass (CRITICAL)

When system.run received an argv array like ["rm", "-rf", "/"], only argv[0] was evaluated against policy rules. Rules like "rm *" would not match.

Fix: Build full command string via FormatExecCommand before policy evaluation.

2. Thread safety - _sessions/_nodes dictionaries (Opus + Gemini)

Plain Dictionary accessed from both background WebSocket listen loop and UI thread. Could crash with InvalidOperationException.

Fix: Added lock(_sessionsLock) and lock(_nodesLock) following existing _pendingRequestLock pattern.

3. SendRawAsync TOCTOU + Dispose race (Opus)

SendRawAsync used _webSocket directly after null-check. Dispose called _cts.Dispose() while listen loop still running.

Fix: Applied defensive patterns from WindowsNodeClient: local ws capture, try-catch, skip _cts.Dispose().

Tests

  • 3 new tests for array-style argv command policy evaluation
  • All 574 tests pass (481 shared + 93 tray)

…races

Triple-model review (Opus, Codex, Gemini) identified 3 consensus issues:

1. ExecApprovalPolicy bypass (CRITICAL): When system.run received an argv
   array like ["rm", "-rf", "/"], only argv[0] was evaluated against
   policy rules. Now builds full command string via FormatExecCommand
   before policy evaluation, so rules like "rm *" correctly match.

2. Thread safety (_sessions/_nodes): Plain Dictionary accessed from both
   the background WebSocket listen loop and UI thread via GetSessionList().
   Added lock(_sessionsLock) and lock(_nodesLock) following the existing
   _pendingRequestLock pattern. Events fired outside locks to avoid deadlock.

3. SendRawAsync TOCTOU + Dispose race: SendRawAsync now captures a local
   WebSocket reference before state check (prevents NullReferenceException
   during reconnect). Dispose no longer calls _cts.Dispose() while the
   listen loop may still reference it, matching WindowsNodeClient pattern.

Includes 3 new tests for array-style command policy evaluation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Each finding was independently validated by a different AI model:

1. CanvasCapability arbitrary file read (Opus→Codex confirmed):
   jsonlPath now restricted to system temp directory via Path.GetFullPath
   validation. Blocks path traversal attacks. Added 2 security tests.

2. ChannelHealth DisplayText mismatch (Opus→Codex confirmed):
   Added 'active' to the '[ON]' case in DisplayText switch to match
   IsHealthyStatus which already considers 'active' healthy.

3. OnSettingsSaved dual connection (Codex→Opus confirmed):
   Now mirrors startup if/else pattern — only initializes operator OR
   node connection, never both. Prevents gateway conflicts.

4. Canvas TryEnqueue hang (Codex→Opus confirmed):
   OnCanvasEval/OnCanvasSnapshot now check TryEnqueue return value
   and fail the TaskCompletionSource immediately if dispatch unavailable.

Rejected findings (false positives):
- Operator precedence in HandleExecApprovalsSet (safe by coincidence)
- WebSocket receive loop blocked (no cancel protocol exists)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@shanselman shanselman merged commit 27b1de3 into master Mar 18, 2026
7 checks passed
@shanselman shanselman deleted the security/consensus-fixes-triple-review branch March 18, 2026 04:55
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