Only the latest release receives security fixes.
Do not open a public GitHub issue for security vulnerabilities.
Report privately via GitHub's Security Advisories feature. Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
You will receive a response within 72 hours. If confirmed, a fix will be released as soon as possible.
In scope:
monitor.py,statusline.py,shared.py,update.py,pulse.pycheck-requirements.ps1,check-requirements.sh- Setup documentation under
docs/ - CI workflows under
.github/workflows/
Out of scope:
- Claude Code CLI itself — report to anthropics/claude-code
- Python standard library — report to python.org/security
- GitHub Actions runners and the GitHub platform — report to GitHub
- Third-party packages pinned in
.github/workflows/requirements-bandit.txt— report upstream to the respective maintainers
CC AIO MON reads session data from Claude Code via stdin and writes snapshots to a local temp directory. The update.py --apply command invokes git pull --ff-only as a subprocess only when explicitly requested. The pulse.py module performs outbound HTTPS requests to Anthropic's public status endpoints (see below) — no credentials, no API keys, no user data are transmitted.
Outbound network (pulse.py):
GET https://status.claude.com/api/v2/summary.json— public status page JSON (every 30 s)GET https://api.anthropic.com/v1/messages— unauthenticated liveness probe; expects 401/405 (every 30 s)- Request body: none. Headers:
User-Agent: cc-aio-mon-pulse/<version>only — version string tracksshared.VERSION(single source of truth since v1.10.2). - Response size capped at 512 KB; socket timeouts 4–5 s.
- Opt-out: set
CC_AIO_MON_NO_PULSE=1to disable the background worker entirely.
- statusLine JSON input (stdin) — size-capped (1 MB), parse-error bounded.
- Session IDs — regex-validated (
^[a-zA-Z0-9_\-]{1,128}$), reserved names (rls,stats,pulse) blocked. - transcript_path (statusline JSON) — containment-checked:
~/.claude/projects/itself must be a real directory, and the transcript must be a regular file inside it; symlinks/junctions are rejected. (added 2026-04; root validation tightened 2026-05) - Transcript content (read-only,
~/.claude/projects/) — per-file 50 MB cap, model IDs sanitized before terminal render. (added 2026-04) - File I/O — confined to
$TMPDIR/claude-aio-monitor/(writes) and~/.claude/projects/(reads). Atomic writes viaNamedTemporaryFile+os.replace. - Subprocess — git only.
shell=False, list args,GIT_TERMINAL_PROMPT=0, bounded timeouts, minimal env whitelist (added 2026-04). Never uses--no-verify/--force. - Network — Pulse:
status.claude.com+api.anthropic.comHTTPS GET, no auth, bounded size (512 KB) + timeout (5 s). Update: git fetch origin main (HTTPS or SSH, configurable by user's git remote).
Opt-out env vars: CC_AIO_MON_NO_PULSE=1, CC_AIO_MON_NO_UPDATE_CHECK=1.
Key protections:
- Session ID validated against
[a-zA-Z0-9_-]{1,128}— prevents path traversal - All JSON fields sanitized before terminal output — prevents escape injection
- Atomic writes via
NamedTemporaryFile+os.replace()— prevents partial reads - File size limits on all reads (1 MB JSON/source files, 2 MB JSONL, 10 MB cross-session, 50 MiB transcripts, 512 KB pulse response, 1 MB
pulse.jsonl) — prevents memory exhaustion - Symlink and NTFS junction rejection on temp data directory —
lstat()+S_ISDIRverification withFILE_ATTRIBUTE_REPARSE_POINTcheck on Windows. Defends against TOCTOU races between mkdir and validation. - Temp directory created with
0o700permissions where supported update.pyguards: dirty tree, wrong branch, detached HEAD, divergence, downgrade, Python version mismatchpulse.pyusesurllib.request.urlopenwith default CA verification (nossl._create_unverified_context); errors are caught by specific type, never broadly suppressed in probe logic- (v1.12.0) Both
monitor.py(interactive TUI) ANDupdate.py --apply(CLI updater) acquire the same singleton OS-level lock (shared.acquire_singleton_lock—fcntl.flockon Unix,msvcrt.lockingon Windows) on$TMPDIR/claude-aio-monitor/monitor.lock. Prevents two concurrent dashboards from racing on shared snapshot reads and crash-log writes, AND preventsupdate.py --applyfrom rewriting.pyfiles while monitor's threads still hold open references.monitor.py --listmode is exempt (one-shot, non-interactive read) - (v1.12.0; extended v1.12.2)
monitor-crash.logrotated to.log.1viashared.rotate_crash_log— bounds disk growth across repeated crash cycles. Since v1.12.2_install_crash_loggercalls it withalways=Trueso the previous traceback is preserved even when both crashes are under 1 MB (two crashes in quick succession can no longer overwrite each other) - (v1.12.0) File-IPC schema tagged with
shared.SCHEMA_VERSION(currently1) — statusline writes the field on every snapshot + history entry; monitor reads viadict.get()so unknown values are silently ignored, providing forward-compat for future incompatible bumps - (v1.12.0) Post-pull syntax check unified in
shared.check_syntax_after_pull— single canonical loop iterated by bothupdate.py --apply(CLI) andmonitor.py:_apply_update_worker(TUI background thread). A regression-guard test fails if either consumer reintroduces the parallel implementation
- README.md — feature overview and architecture
- NOTICE — legal notice and affiliation disclaimer
- CHANGELOG.md — release history