security-guidance: SessionStart builds a 304 MB SDK venv with no credentials check, and ignores SECURITY_GUIDANCE_DISABLE

Author: fadmazCreated Aug 14, 2026Updated Sep 17, 2026

Summary

hooks/ensure_agent_sdk.py runs on every SessionStart and builds a ~304 MB venv (~/.claude/security/agent-sdk-venv, 3,831 files) to install claude_agent_sdk. That SDK exists solely to power the LLM commit/push/Stop reviews.

main() decides whether to build using six gates — Python version, SDK on syspath, a build sentinel, an existing venv, a --target fallback, and a signal-kill cooldown. It checks none of the three things that determine whether the SDK will ever be used:

  1. API credentials. grep -iE 'ANTHROPIC_API_KEY|AUTH_TOKEN|credential' hooks/ensure_agent_sdk.py returns nothing. llm.py:123 gates every LLM path on HAS_API_CREDENTIALS, but the bootstrap never consults it.
  2. ENABLE_STOP_REVIEW / ENABLE_COMMIT_REVIEW. Both are read only in security_reminder_hook.py. A user who explicitly turned the reviews off still gets the venv.
  3. SECURITY_GUIDANCE_DISABLE — the documented master kill switch. security_reminder_hook.py:164-170 describes it as disabling "the plugin entirely," but it is read only there. ensure_agent_sdk.py's imports are importlib.util, json, os, subprocess, sys, time, pathlib and _base.state_dir — the kill switch never reaches it.

So on a machine with no credentials, the plugin's own kill switch does not stop SessionStart from building a 304 MB venv for a feature that cannot run.

Evidence

From this machine's ~/.claude/security/log.txt + log.txt.1, 2026-07-29 → 2026-08-14 (16 days):

Observation Value
Stop reviews that ran 0 of 315 — all logged LLM review disabled or no API credentials
Commit reviews that ran 0 of 227 — 224 logged the same, 3 no review line
LLM API calls made, ever 0 (grep for request/token/agent-sdk-query markers: no hits)
agent-sdk-venv size 318,800,844 B (304 MiB), 3,831 files
Installed for claude_agent_sdk 0.2.115, built 2026-07-10

ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are both unset here, and have been for the life of that venv.

Second effect: a 300-second re-spawn loop

Because the SDK is installed in the venv and not importable from system Python, _sdk_on_syspath() is false on every run. security_reminder_hook.py:2112-2148 therefore spawns a detached ensure_agent_sdk.py every 300 s for the whole session. Observed live: ~/.claude/security/.sdk_bootstrap_spawned advanced 10:50 → 10:55 during a single session.

Each of those re-spawns then pays the sg-python.sh interpreter probe and lands on NOOP_VENV, whose check is itself a subprocess — measured at ~1.3–1.4 s for <venv>/Scripts/python.exe -c "import claude_agent_sdk" (the call at ensure_agent_sdk.py:496).

Deleting the venv does not help

main() short-circuits to NOOP_VENV only when venv_py.exists() and the import succeeds. With the venv removed, no system SDK, no --target install and no cooldown file, the next SessionStart falls straight through to the build branch and re-creates all 304 MB. There is no supported way to opt out — SECURITY_GUIDANCE_DISABLE=1 does not reach this code path (see above), so the only workaround is disabling the whole plugin.

Suggested fix

Add a credentials/enablement gate at the top of main(), before the venv probe:

python
if sys.version_info < (3, 10):
    return HOOK_PY_INCOMPATIBLE, "hook_py", ...

# NEW: the SDK only powers LLM review. If review can't or won't run,
# building a 304 MB venv is pure cost.
if not _llm_review_possible():          # credentials AND not disabled
    return SKIP_NO_CREDENTIALS, "", ""

where _llm_review_possible() mirrors llm.py's HAS_API_CREDENTIALS and honours SECURITY_GUIDANCE_DISABLE / ENABLE_SECURITY_REMINDER / ENABLE_STOP_REVIEW / ENABLE_COMMIT_REVIEW. A new stable outcome code keeps the telemetry split clean.

Building lazily — on the first review that actually has credentials — would work equally well and would also cover the case where a user adds a key mid-session.

At minimum, SECURITY_GUIDANCE_DISABLE=1 should be honoured here, since the code comment already promises it "disables the plugin entirely."

Environment

  • security-guidance 2.0.6
  • Claude Code 2.1.232, native install
  • Windows 11 Pro 26200, Git Bash
  • Python 3.12.3 (venv built by c:\python312\python.exe -m venv --clear)

Split out from #5330 as requested there.

Source: anthropics/claude-plugins-official