CVE-2026-96620 (CVSS 4.0 9.2) lets an untrusted session ID passed to resume in claude-agent-sdk before 0.2.121 run commands. Fixed in July; no exploitation reported.
Any Python application that feeds an untrusted session ID into the Claude Agent SDK's resume option can be made to run attacker-chosen commands on its host. The flaw, CVE-2026-96620 (GHSA-h4mw-j7qp-8mwm), affects claude-agent-sdk on PyPI before 0.2.121 and carries a CVSS 4.0 score of 9.2 (Critical). The fix has been available since July 2026, but the CVE record only appeared on 16 October 2026.
Who is exposed
The advisory sets one precondition: the application must pass untrusted input as the session ID to resume. A script that resumes a session ID it generated itself is not exposed. The risk is in services that wrap the SDK behind a web endpoint, chat backend or bot and let a request, cookie, header or stored value choose which session to resume. The advisory's network vector and "no privileges required" rating reflect that setup, with the attack requirement marked present because the application has to take such input.
The attacker gains execution in the context of the process that launches the Claude CLI. That is commonly a service account holding API keys, repository access or cloud credentials.
What went wrong
The SDK launches the Claude CLI as a subprocess. According to the fix PR, SubprocessCLITransport._build_command() added --resume and the session ID as two separate argument-vector tokens. The CLI declares --resume with an optional value, so a value starting with a dash is not bound to the flag. The CLI instead reads it as a flag of its own. The maintainers demonstrated the behaviour with a harmless flag that made the CLI print its version rather than treat the value as a session.
No shell is involved, so this is argument injection (CWE-88) rather than classic shell metacharacter injection, though the advisory also tags CWE-78 and CWE-20. The advisory describes the dangerous case: an attacker supplies a value that injects the CLI's --mcp-config flag, defining a stdio MCP server. The CLI starts that server's command during startup, before the SDK's permission controls come into play. Permission callbacks, allowed-tool lists and similar guardrails therefore do not help. We are not publishing a working value, and none is needed to act on the advisory. (We also covered a separate Claude Code symlink race fixed in the CLI.) For background on how the CLI starts up and launches tools, see our earlier piece on how Claude Code works internally.
The fix
PR #1123, merged on 16 July 2026, changes the command builder to emit --resume=<value> as one token. With the equals form the value always binds to the flag, so a dash-leading string is treated as a session identifier that fails to resolve. The PR reports that resume="--version" now raises a ProcessError, that ordinary UUID resumes still work, and that regression tests assert injected values never appear as standalone tokens. The same change applies to --session-id. According to the PR, --session-id <uuid> already bound a dash-leading token because it declares a required value, so it was switched only for uniformity and was not exploitable.
The maintainers' own severity note in the PR was restrained. It called the bug "Bounded" and "argv-level", said "no shell is involved, so this is flag injection, not arbitrary command execution", and added that an injected flag taking a value (it cites --settings) "escalates this further". The advisory later rated the --mcp-config route as 9.2 with command execution. That may explain why the July release note was low-key.
Version 0.2.121 was published on 17 July 2026 (GitHub release at 00:39 UTC; PyPI upload about a minute earlier). Its release notes describe an "argv flag injection" fix and do not mention a CVE; the same release also fixed shell injection in the build scripts via CLAUDE_CLI_VERSION (#1117).
The disclosure came in steps. The maintainer published the repository advisory on 12 September 2026, about eight weeks after the release. The CVE record and the global GitHub Advisory Database entry, which tools such as pip-audit and Dependabot read, followed on 16 October, 91 days after the release; NVD picked it up the same day. We could not determine why the steps were spaced this way. Until 16 October, teams that track only CVE feeds or pip-audit results had no signal to upgrade.
The fix shipped on 17 July; the CVE and global advisory entry followed 91 days later.
Status and exploitation
- NVD lists the record as "Received", with no NVD-assigned score or analysis yet. The 9.2 CVSS 4.0 score comes from GitHub as the CNA. GitLab's advisory database separately lists a CVSS 3.1 score of 9.8.
- CVE-2026-96620 is not in CISA's Known Exploited Vulnerabilities catalog (version 2026.10.16).
- We found no reports of exploitation in the wild. One aggregator site publishes a "proof of concept" under a different CVE number; we could not tie it to this issue and do not treat it as evidence.
- The reporter is credited as moamenmahmood via HackerOne.
Other SDKs
The PR states the TypeScript SDK received the same fix in v0.3.208. That release's notes (14 July 2026) read: "Fixed extraArgs values that look like flags (e.g. resume: '--version') being parsed as their own CLI flags; dash-leading values are now bound with equals-form argv." We found no separate CVE or advisory for the TypeScript package. Check which version you run rather than assuming it is safe. Note the contrast: the TypeScript note names extraArgs, while the Python fix deliberately left extra_args on the two-token form, so do not pass untrusted data there.
What defenders should do
- Find affected installs. Run
pip show claude-agent-sdkorpip listin each environment, container image and lockfile. Anything below 0.2.121 is affected. - Upgrade to 0.2.168 (the current PyPI release) or at least 0.2.121. Version 0.2.121 bundles Claude CLI 2.1.212. Rebuild images; pinned lockfiles will not update themselves.
- Validate session IDs at your boundary regardless of version. Accept only the exact format you issue (typically a UUID), and reject anything else, especially values starting with
-. Better, map user-facing handles to server-side session IDs so users never supply the raw value. - Audit code paths that pass request data to
resumeorextra_args, and validate anything else you forward to the CLI. - Review logs for unusual session IDs beginning with a dash, and for unexpected child processes spawned by the CLI around startup. Treat any hit as possible compromise and rotate credentials available to that service.
- Run the agent with least privilege: a sandboxed account, restricted network egress and minimal secrets limit the damage if a similar bug appears, as with the Claude Desktop Cowork command execution flaw.