Skip to content

fix: address Codex security audit findings (v2)#64

Merged
sheeki03 merged 10 commits intomainfrom
fix/codex-security-audit-v2
Mar 21, 2026
Merged

fix: address Codex security audit findings (v2)#64
sheeki03 merged 10 commits intomainfrom
fix/codex-security-audit-v2

Conversation

@sheeki03
Copy link
Copy Markdown
Owner

Summary

Addresses findings from the Codex automated security audit. Of 47 reported findings (many duplicates), 7 distinct real bugs were identified and fixed:

Fixes

  • Cmd caret escape bypass — Handle ^ inside double quotes for ShellType::Cmd so "c^md" normalizes to cmd for interpreter detection
  • Inline TIRITH=0 paste bypass — Remove inline content prefix check from all 4 shell hooks (bash/zsh/fish/PowerShell); keep only the env var opt-out
  • Gateway JSON-RPC notification bypass — Analyze guarded notifications through the full engine pipeline instead of forwarding blindly
  • Env long-flag parsing bypass — Whitelist env flags that take values (--unset, --chdir, --split-string) instead of blindly skipping next token for all -- flags
  • PowerShell comment bypass — Stop split_raw_words at # for PowerShell so comment text can't trigger $env:TIRITH=0 bypass
  • Cloaking SSRF — Add validate_fetch_url() with DNS resolution checks; re-validate each redirect target
  • MCP scan DoS — Cap file enumeration at 5000 files

Additional hardening

  • Escape blocked command/paste previews in shell hooks to prevent terminal escape injection
  • Redact env assignment values in evidence strings and JSON output
  • Remove self-invocation guard (user-typed tirith commands now go through full analysis)
  • Reject inline bypass when input has multiple segments (pipes, &&, ;, &)
  • Quote shell metacharacters in tirith init hook paths
  • Add HostResolver type alias for clippy compliance

Not fixed (by design)

  • Env-prefix URLs intentionally skipped in extraction (~17 duplicate findings) — now re-enabled with FP suppression
  • Self-invocation guard trusts bare name — removed entirely instead
  • Docker -- stops scan — correct CLI semantics
  • Shell hooks echo raw blocked input — uses printf '%s' (safe)

Test plan

  • cargo fmt --all -- --check passes
  • cargo clippy --workspace -- -D warnings passes
  • cargo test --workspace passes (all tests)
  • Manual verification of shell hook paste bypass removal
  • Manual verification of gateway notification analysis

sheeki03 and others added 10 commits March 21, 2026 16:56
…s in findings

- Add caret escape handling in QState::Double for ShellType::Cmd, matching
  cmd.exe behavior where ^ is processed even inside double quotes.
- Redact env assignment values in pipe-to-interpreter, dotfile overwrite, and
  archive extract evidence strings to avoid leaking secrets in output.
- Change redact_env_value to always return [REDACTED] instead of a prefix,
  preventing partial secret leakage in sensitive env export findings.
- Skip pipe-to-interpreter when source is plain `tirith` (trusted) but still
  flag when source is `tirith run` (user-fetched content).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…view

Security fixes for shell hooks (bash, zsh, fish, PowerShell):

- Remove inline TIRITH=0 prefix check on pasted content that allowed
  attacker-controlled paste to skip scanning. The env var check (TIRITH=0
  set in environment) remains as an intentional user opt-out.
- Escape blocked command/paste previews using shell-appropriate quoting
  (printf %q for bash, string escape for fish, ConvertTo-Json for PS)
  to prevent terminal escape injection when echoing blocked content.
- Add _TIRITH_BASH_INTERNAL guard to prevent recursive tirith invocations
  when the hook calls tirith check/paste.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ndly

Previously, a tools/call message without a JSON-RPC id was classified as
GuardedNotification and forwarded to the upstream tool without running
security analysis. This allowed an attacker-controlled MCP client to
bypass tirith's command inspection by omitting the id field.

Now notifications matching guarded tools are analyzed through the full
engine pipeline. Blocked notifications are silently dropped (no response
possible per JSON-RPC spec). Allow/warn notifications are forwarded with
audit logging.

Also adds InvalidRequest handling for guarded requests with non-standard
id types and NotificationExtractionFailed for notifications where the
command cannot be extracted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add env_long_flag_takes_value() whitelist so only --unset, --chdir,
  --split-string consume the next token. Flags like --ignore-environment
  no longer incorrectly skip the command argument.
- Stop split_raw_words at '#' for PowerShell to prevent comment text
  from being parsed as words (which could false-trigger $env:TIRITH=0).
- Reject inline bypass when input has multiple segments (pipes, &&, ;)
  or unquoted &, preventing TIRITH=0 from suppressing analysis of
  chained malicious commands.
- Remove self-invocation guard entirely. User-typed tirith commands now
  go through the full analysis pipeline like any other command, closing
  the PATH-spoofing and command-substitution bypass vectors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add validate_fetch_url() that validates URLs before cloaking requests,
  blocking private IPs, loopback, link-local, metadata endpoints, and
  embedded credentials.
- Add DNS resolution check: hostnames are resolved and each IP is
  validated against the private-network blocklist, preventing DNS rebinding
  and hostname-based SSRF bypasses.
- Replace cloaking's redirect policy with a custom policy that re-validates
  each redirect target, preventing SSRF via open redirects.
- Expand url_validate with comprehensive test coverage for both server
  and fetch URL validation modes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extend extract_urls() to scan leading env-assignment values (e.g.,
  URL=https://evil.com curl ...) in addition to command + args tokens.
  Adds ignores_env_assignment_url() to skip known false-positive vars
  like HTTPS_PROXY, HTTP_PROXY, etc.
- Add leading_env_assignments() and leading_env_assignment_values() to
  tokenize.rs for structured env-prefix parsing.
- Set MCP_SCAN_MAX_FILES=5000 default cap for MCP scan operations to
  prevent unbounded directory enumeration DoS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add redact_shell_assignments() and redacted_findings() to redact.rs,
  scrubbing VAR=value patterns and $env:VAR assignments from evidence
  strings before they reach JSON output or last_trigger.json.
- Pass DLP custom patterns through write_json() and last_trigger to
  ensure user-defined secrets are redacted in all output paths.
- Use posix_single_quote() and powershell_single_quote() in init.rs
  to properly escape hook source paths, preventing shell metacharacter
  injection via TIRITH_SHELL_DIR or unusual install paths.
- Quote paths in shell_profile.rs setup for consistency.
- Add cli_integration tests for redaction and quoting behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set LOCALAPPDATA in addition to XDG_DATA_HOME in the last_trigger
  redaction test so it finds the data dir on Windows.
- Update rustls-webpki 0.103.9 → 0.103.10 to fix RUSTSEC-2026-0049
  (CRL distribution point matching advisory).
…0.101.x

- Windows test: set APPDATA (not LOCALAPPDATA) since etcetera's Windows
  strategy uses APPDATA for data_dir().
- Ignore RUSTSEC-2026-0049 for rustls-webpki 0.101.7 which is pinned by
  rust-s3 → rustls 0.21. The 0.103.x copy is already updated to 0.103.10.
  Tirith does not use CRL revocation checking.
@sheeki03 sheeki03 merged commit bc71915 into main Mar 21, 2026
8 checks passed
@sheeki03 sheeki03 deleted the fix/codex-security-audit-v2 branch March 21, 2026 12:40
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