Skip to content

security: harden against local attack vectors#35

Merged
graykode merged 6 commits intograykode:mainfrom
tbouquet:security/harden-local-attack-surface
Apr 18, 2026
Merged

security: harden against local attack vectors#35
graykode merged 6 commits intograykode:mainfrom
tbouquet:security/harden-local-attack-surface

Conversation

@tbouquet
Copy link
Copy Markdown
Contributor

Summary

Hardens abtop against local attack vectors. While abtop reads only local files with no network activity, the files it reads (session transcripts, JSONL logs, config) are writable by other processes on the same machine. A compromised or malicious Claude Code session could craft payloads targeting abtop's parsing.

Threat model

Local attacker with write access to ~/.claude/ (e.g. malicious MCP server, compromised subagent, rogue extension) attempting: symlink exploitation, PID reuse for kill misdirection, OOM via crafted transcripts, terminal injection via bidi overrides, credential exfiltration via tool arg display, or installer supply chain hijack.

Changes

Commit 1 - core hardening:

  • Verify PID command before kill to prevent TOCTOU race on PID reuse
  • Skip symlinks in all session/transcript/subagent file reads (fail-closed on error)
  • Add visited set to all 3 process tree walkers to prevent infinite loops
  • Cap token_history (10K entries) and JSONL line buffer (10MB) to prevent OOM
  • Validate CLAUDE_CONFIG_DIR as existing directory before use
  • Atomic write for summary cache (temp + rename)
  • Harden statusline hook setup: exact match check, 0o700 permissions
  • Download update installer to file before executing (no more curl | sh)
  • Sanitize --once output: strip terminal escape sequences
  • Redact common secret patterns (sk-ant-, ghp_, gho_, glpat-, xoxb-, AKIA, ...) in tool arg display
  • Truncate deserialized session fields at char boundaries (no UTF-8 panic)

Commit 2 - follow-up tightening:

  • Replace predictable /tmp/abtop-installer.sh with tempfile::NamedTempFile (O_EXCL + random suffix) to prevent symlink planting
  • Cap read_line via .take(MAX+1) instead of post-read check (the previous check fired after the buffer had already grown unbounded)
  • Extend sanitize_output to strip Unicode bidi overrides (U+202A-U+202E, U+2066-U+2069, U+200E, U+200F) - closes CVE-2021-42574 Trojan Source vector

Testing

  • cargo clippy -- -D warnings - clean
  • cargo test - 31/31 pass
  • cargo build --release - compiles, binary functional
  • Manual: abtop --once with active sessions, verified sanitized output

- Verify PID command before kill to prevent TOCTOU race
- Abort kill if ps check fails (fail-closed)
- Skip symlinks in session, transcript, and subagent file reads
- Symlink check defaults to true on error (fail-closed)
- Add visited set to all process tree walkers to prevent infinite loops
- Cap token_history and JSONL line buffer to prevent OOM
- Validate CLAUDE_CONFIG_DIR as existing directory
- Atomic write for summary cache (temp + rename)
- Harden statusline hook setup (exact match, 0o700)
- Download update installer to file before executing
- Validate cwd as directory before git operations
- Sanitize --once output to strip terminal escape sequences
- Redact common secret patterns in tool arg display
- Truncate deserialized session fields at char boundaries
Follow-up to 4cd51e2 - three issues a paranoid re-read of the hardening
commit turned up.

- Download update installer via tempfile::NamedTempFile instead of a
  predictable /tmp/abtop-installer.sh path. Prevents a local attacker
  from pre-planting a symlink at the fixed path and redirecting the
  curl -o write (symlink clobber + post-download swap). Promotes the
  tempfile crate from [dev-dependencies] to [dependencies].

- Cap read_line allocation with take(MAX_LINE_BYTES + 1) instead of
  checking line length after the fact. The previous check fired only
  after read_line had already grown the buffer unbounded, so a single
  10GB-no-newline transcript line would OOM the process before the
  cap engaged. On cap hit, skip to end-of-file for this tick; the
  file_identity reset picks it up if the transcript is later replaced.

- Extend sanitize_output to strip Unicode bidi overrides
  (U+202A-U+202E, U+2066-U+2069, U+200E, U+200F) in addition to
  control characters. Closes a Trojan Source (CVE-2021-42574) vector
  where an LLM-generated task description or session cwd containing
  RTLO could visually reorder text in --once output.
Pre-existing code flagged by newer Rust toolchain in CI. Collapses
the if-guard into the match arm pattern as clippy suggests.
@tbouquet tbouquet marked this pull request as ready for review April 17, 2026 20:43
…-attack-surface

# Conflicts:
#	src/collector/codex.rs
- run_update: shasum (macOS default) first, fall back to sha256sum (coreutils).
  Previously sha256sum alone silently failed on macOS, so the "Show checksum
  so the user can verify" line was never printed on the primary target OS.
- sanitize_output: remove `|| *c == ' '` dead branch. Space is not a control
  character, so the OR never fired; expression simplifies to `!c.is_control()`.
The original PR hardened Claude's transcript parser but left Codex exposed
to the same local attack vectors. Bring Codex to parity:

- Extract redact_secrets into collector/mod.rs and expand coverage:
  add sk-proj-, sk-or-, ghs_, ghr_, ghu_, github_pat_, xoxa-, xoxs-,
  rk_live_, rk_test_, ASIA, and "Bearer " prefixes.
- Cap codex JSONL line reads at 10 MB via take(MAX+1) + read_line,
  matching the Claude transcript parser. Prevents OOM from a malformed
  or hostile rollout file.
- Cap codex token_history at 10,000 entries.
- Redact secrets in codex function_call args and user_message prompts
  before they reach the TUI.
@graykode graykode merged commit 22d594b into graykode:main Apr 18, 2026
12 of 13 checks passed
@graykode graykode mentioned this pull request Apr 18, 2026
6 tasks
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.

2 participants