CodeVetter · Repo Unpack · Risk map
anthropics/claude-code
The highest-impact code is the security-guidance Python hook (sends diffs to a model, shells out, writes under the home directory), telemetry (credentialed POST plus a shell probe and a read of .claude.json), and the GitHub workflows that let non-writers invoke Claude and that close issues. sec-default fails closed and can block every user-tier tool.register when policy cannot be read. Plugin and script paths are effectively untested. The function-hook API is marked early access.
- Telemetry sends the session authorize() handle to https://api.anthropic.com/api/event_logging/v2/batch. It also reads CLAUDE_CODE_SESSION_ACCESS_TOKEN, CLAUDE_SESSION_INGRESS_TOKEN_FILE, and CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR only as set-or-not, but it JSON.parses .claude.json via $.fs.read, which contains oauthAccount and userID. A shell probe runs uname and command -v through process.run.
- security-guidance transmits changed paths, diff hunks, and file contents to the model endpoint, and the agentic commit reviewer may also Read/Grep/Glob further files. claude-security-guidance.md contents are appended to the prompt. The plugin shells out through bash ${CLAUDE_PLUGIN_ROOT}/hooks/sg-python.sh. It writes ~/.claude/security/log.txt. SessionStart runs ensure_agent_sdk.py with a 180s timeout and may bootstrap an SDK venv on remote pods.
- security-guidance pattern checks include github_actions_workflow, which warns when editing .github/workflows YAML about untrusted github.event.* interpolation. That matters because this repo's own log-issue-events.yml interpolates ISSUE_TITLE into a curl JSON body with sed quote escaping, and issue-opened-dispatch.yml interpolates ISSUE_URL into gh api. The workflow file itself says values are passed via env to prevent injection, but the title is still expanded inside the shell -d string.
- claude-issue-triage.yml and claude-dedupe-issues.yml set allowed_non_write_users to "*", so users without write access can trigger Claude Code Action. non-write-users-check.yml comments on PRs that add or change that key under .github/**, calling it a security risk, and does not block the PR.
- issue-opened-dispatch.yml has permissions issues:read and actions:write, reads secrets ISSUE_OPENED_DISPATCH_TARGET_REPO and ISSUE_OPENED_DISPATCH_TOKEN, and on failure of gh api .../dispatches exits 0. The payload is only the issue URL, but a bad token or target fails silently.
- sweep.ts closes issues using the workflow GITHUB_TOKEN (permissions issues:write). It treats any non-bot comment as human activity, so a bot other than type Bot would not save an issue, and a human emoji-only comment would. The +1 threshold is 10. close uses state_reason not_planned, which GitHub shows as not planned rather than completed.
- sec-default fail-closed: if $.settings.read({source:'policy'}) is denied, decidedByPolicy treats policy as in force and a user-tier tool.register is refused. The test 'a policy that cannot be read counts as one in force' expects zero tools registered. Loading the plugin with --plugin-dir cannot call next.to, so a local copy does not reproduce org behaviour.
- hookify compiles rule regexes with re.compile(pattern, re.IGNORECASE) and an lru_cache of 128, with no ReDoS screen. security-guidance's extensibility.py does skip custom regexes that look prone to catastrophic backtracking. hookify fail-open (exit 0) means a hang or a logic bug fails open only after the process ends; a catastrophic pattern can still stall the tool call until the 10s timeout.
- hookify prepends the parent of CLAUDE_PLUGIN_ROOT and the plugin root onto sys.path before importing hookify.core. A writable plugin root changes which modules import. pretooluse.py documents this as required for the package import.
- comment-on-duplicates.sh hardcodes REPO=anthropics/claude-code and takes the issue number only from GITHUB_EVENT_PATH (.issue.number or .inputs.issue_number). A fork or a rename of the workflow repo would comment on the upstream repo if GH credentials allow it. Duplicate numbers are positional args after --potential-duplicates; other flags are rejected.
- The function-hook surface is early access. claude-code.d.ts and mods/README.md both say the API may change between releases without notice, and the declaration file must be regenerated with /plugin-types rather than edited. mods/types/claude-code.d.ts is about 12990 lines, so a stale copy will typecheck against an API the installed CLI no longer implements.
- Diff git children are the blast radius for a working tree: they run with --git-dir and --work-tree pinned so a later cd cannot retarget them, but they still execute the git binary via $.process.run. INSTALLED_BACKEND_PROBES is an empty module-level Set; a host that mutates it at load time changes which VCS is read, and nothing in-repo registers a non-git probe.
- .devcontainer runArgs add NET_ADMIN and NET_RAW to the sandbox container. That is broader than a normal dev container and is unrelated to the mods test job, which runs on ubuntu-latest without those capabilities.
- Managed settings examples can disable the product's safety valves if copied blindly: settings-strict.json sets disableBypassPermissionsMode, allowManagedHooksOnly, allowManagedPermissionRulesOnly, and denies WebSearch and WebFetch. The README warns the snippets are community-maintained and may be unsupported. MDM templates sit above user settings and cannot be overridden.