A developer tool left its scratch paper in the shared printer tray, with the recipient's name written in pencil
CVE-2026-46406 affects Anthropic's @anthropic-ai/claude-code npm package, versions 2.1.59 through 2.1.127, patched in 2.1.128. The /copy slash command writes the model response to a hardcoded path (/tmp/claude/response.md) without per-UID isolation, randomness, or O_NOFOLLOW-style symlink protection. The directory is created 0755 and the file 0644, so any other local user on the same box can (a) read the privileged user's response — which often contains pasted secrets, source, or tokens — or (b) pre-stage a symlink at that path to redirect the write and clobber an attacker-chosen file owned by the victim. CWE-59 (link following) compounded by CWE-377 (insecure temp file) and CWE-200 (info disclosure).
There is no vendor CVSS from Anthropic in NVD, but GitLab's advisory tracker computed 6.1 Medium (AV:L/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:H). That math is fair. This is a local-only, user-interaction-required bug in an interactive developer CLI. It is not a remote code execution, not network-reachable, and the affected install base is developer workstations and CI runners — not domain controllers, hypervisors, or edge appliances. The vendor framing matches reality; we land at MEDIUM, slightly below 6.1 because in practice almost nobody runs Claude Code on a shared multi-user host.
4 steps from start to impact.
Attacker establishes a local unprivileged shell on the same host
AV:L, not network. On a single-user developer laptop, this step alone kills the chain.- Multi-user host (shared dev box, jump host, CI build host with multiple project tenants)
- Attacker has shell as a different UID than the Claude Code user
- Modern developer endpoints are single-user — laptops, ephemeral cloud workstations, per-job CI runners
- Cloud-shell / Codespaces / devcontainers spin a fresh container per user — no co-tenancy at the OS layer
Pre-stage /tmp/claude/ and plant the symlink (or just inotifywait the path)
/tmp/claude/ if absent and either ln -s /home/victim/.ssh/authorized_keys /tmp/claude/response.md (for the destructive write variant) or starts inotifywait -m /tmp/claude/response.md to harvest content the moment the victim runs /copy. The hardcoded path means no race window guessing — the attacker knows exactly where to point.- Write permission on
/tmp(default on every Unix) - Victim has not yet run
/copyin this session (so the file doesn't yet exist with victim ownership)
- Sticky bit on
/tmpdoesn't help — symlink creation in an empty subdir is unrestricted fs.protected_symlinks=1(default on most modern Linux) does block the symlink-write variant when the writer is not the symlink owner — significant friction for the integrity impact
path=/tmp/claude watchers will catch it; EDR with file-creation telemetry on /tmp will flag the symlink.Victim runs /copy in Claude Code
/copy slash command, which writes the latest assistant response to /tmp/claude/response.md with mode 0644. If the symlink was pre-planted and protected_symlinks is off, the write follows the link and overwrites the target. If the attacker is just reading, they cat the file or have already captured it via inotify.- Victim actively uses the
/copycommand - Claude Code version 2.1.59–2.1.127
/copyis not a high-frequency command — many users never invoke it- Response content is whatever was in the model output — may or may not contain secrets
node writing to /tmp/claude/ is the canonical signal.Loot or pivot
authorized_keys, .bashrc, or a CI config can be corrupted into a DoS or, with luck, a partial injection.- Captured content contains something useful, OR target file's corruption produces a usable outcome
- Write-variant payload is *uncontrolled* — attacker can't pick what gets written, only where
- Secrets in chat responses are user-discipline-dependent, not guaranteed
The supporting signals.
| In-the-wild exploitation | None observed. No public reports of active abuse as of 2026-06-30. |
|---|---|
| Public PoC | Conceptual PoC in the GHSA writeup and Phoenix Security analysis; trivial to reproduce (ln -s + /copy). No weaponized tooling on GitHub yet. |
| EPSS | Not yet scored by FIRST (CVE is <1 week old); expected <1% given local-only, UI-required vector. |
| KEV status | Not listed. Local privilege bugs in dev tooling rarely reach KEV. |
| CVSS | GitLab-computed CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:H = 6.1 Medium. No NVD/Anthropic authoritative score published. |
| Affected versions | @anthropic-ai/claude-code 2.1.59 → 2.1.127 (npm) |
| Fixed version | 2.1.128 and above. Auto-update users already remediated. |
| Exposure data | No Shodan/Censys signature — not a network service. Population = developer workstations and CI runners with Claude Code installed, which Anthropic does not publicly disclose. |
| Disclosure | 2026-06-25 via HackerOne; CVE assigned 2026-06-29. |
| Reporter | External security researcher via HackerOne (handle redacted in advisory). Coordinated disclosure with Anthropic. |
noisgate verdict.
The single most decisive factor is local-only attack vector with required victim interaction (AV:L/UI:R) on a developer-endpoint product — there is no network reachability, no privilege escalation to root, and the impacted population is single-user developer machines where co-tenancy is rare. The blast radius does not extend beyond the local user's secrets or one chosen file.
Why this verdict
- Local + UI-required: the chain cannot start without a co-resident unprivileged shell AND the victim choosing to run
/copy. That is two compounding gates that exclude >99% of Claude Code installs (single-user laptops, ephemeral CI containers). - Blast radius capped at one user: worst case is disclosure of one conversation or destructive write to one victim-owned file. No root, no lateral movement, no fleet impact.
- Role multiplier — developer workstation: even in the high-value role enumeration, Claude Code is canonically a *developer endpoint* tool, not a domain controller, hypervisor, IdP, PAM, backup admin, or kernel-mode agent. The component does not occupy any high-value role in the noisgate catalog, so no role-floor applies. Worst credible deployment is a shared bastion or shared CI runner — still bounded to that one host's secrets.
- Modern OS defenses partially mitigate: Linux
fs.protected_symlinks=1(default in essentially every distro since 2012) neutralizes the symlink-write variant when the writer UID ≠ symlink owner UID, leaving only the disclosure variant on those hosts. - No KEV, no in-the-wild, EPSS expected near-zero: this is a hygiene bug, not an active campaign.
Why not higher?
HIGH would require either network reachability, a privilege escalation to root, or a high-value-role component (IdP, hypervisor, CA, edge appliance). None apply: vector is AV:L, impact is bounded to the invoking user's data and one target file, and the affected component is a dev CLI — not in the noisgate high-value-role catalog. Calling this HIGH would devalue the bucket reserved for actively-exploited remote vulns.
Why not lower?
LOW understates the integrity impact (I:H in the CVSS write — authorized_keys clobber is a plausible outcome on hosts without protected_symlinks) and the realistic disclosure of pasted API keys/PII from chat history. Co-tenant shells do exist on shared bastions, build hosts with multi-tenant runners, and lab boxes. MEDIUM captures the genuine but bounded risk.
What to do — in priority order.
- Upgrade
@anthropic-ai/claude-codeto 2.1.128 or later fleet-wide — Per noisgate remediation SLA for MEDIUM, you have up to 365 days — but this is a one-linenpm i -g @anthropic-ai/claude-code@latestor auto-update flip. Do it this sprint. Inventory vianpm ls -gon managed dev endpoints and CI images. - Confirm
fs.protected_symlinks=1on every Linux dev host and CI runner —sysctl fs.protected_symlinksshould return1. This is default on modern distros but verify, especially on hardened-minimal containers. Neutralizes the symlink-write variant immediately without waiting on the npm update. - On shared multi-user hosts, set
/tmpto per-user namespaces — Usesystemd-tmpfilesPrivateTmp=yesfor services, or pam_namespace.so for interactive shells. Removes the co-tenancy precondition entirely. Apply during the same 365-day remediation window — there is no mitigation SLA at the MEDIUM bucket. - Educate developers: don't paste live secrets into Claude Code prompts — The disclosure impact is proportional to what's in the conversation. Reinforce data-handling policy; pair with a secret-scanner pre-commit hook on dev endpoints.
- WAF, NGFW, network segmentation — vector is local; no packets cross the wire.
- MFA / IdP hardening — bug doesn't traverse the auth plane.
- EDR alone without
/tmpfile-creation telemetry — many EDRs deprioritize/tmpwrite events as noise. - Removing the
/copycommand from workflow — users still on 2.1.59–2.1.127 are not the only attack surface; the underlying directory persists across sessions and other commands may share the pattern in older builds.
Crowdsourced verification payload.
Run on each Linux/macOS dev workstation and CI runner that has Claude Code installed. Invoke as the user that owns the Claude Code install: bash check-cve-2026-46406.sh. No root required; uses npm ls -g and a version comparator. Outputs VULNERABLE, PATCHED, or UNKNOWN.
#!/usr/bin/env bash
# check-cve-2026-46406.sh — detect @anthropic-ai/claude-code versions vulnerable to CVE-2026-46406
# Vulnerable: 2.1.59 <= version <= 2.1.127
# Fixed: 2.1.128+
set -u
PKG="@anthropic-ai/claude-code"
FIXED="2.1.128"
MIN_VULN="2.1.59"
verlt() { # returns 0 if $1 < $2
[ "$1" = "$2" ] && return 1
printf '%s\n%s\n' "$1" "$2" | sort -V -C
}
if ! command -v npm >/dev/null 2>&1; then
echo "UNKNOWN: npm not found"; exit 2
fi
VER=$(npm ls -g --depth=0 --json 2>/dev/null \
| grep -A1 "\"$PKG\"" | grep version | head -1 \
| sed -E 's/.*"version": "([^"]+)".*/\1/')
if [ -z "${VER:-}" ]; then
# try local project install
VER=$(npm ls --depth=0 --json 2>/dev/null \
| grep -A1 "\"$PKG\"" | grep version | head -1 \
| sed -E 's/.*"version": "([^"]+)".*/\1/')
fi
if [ -z "${VER:-}" ]; then
echo "UNKNOWN: $PKG not installed on this host"; exit 0
fi
if verlt "$VER" "$MIN_VULN"; then
echo "PATCHED: $PKG $VER (predates vulnerable range)"; exit 0
fi
if verlt "$VER" "$FIXED"; then
echo "VULNERABLE: $PKG $VER (range 2.1.59-2.1.127, fix in $FIXED)"; exit 1
fi
echo "PATCHED: $PKG $VER (>= $FIXED)"; exit 0
If you remember one thing.
@anthropic-ai/claude-code ≥ 2.1.128 to every developer endpoint and CI image (auto-update channel users are already done), verify fs.protected_symlinks=1 on every Linux host, and audit any shared bastion or multi-tenant runner where Claude Code is installed. Skip the change-board theatrics; this is an npm bump. If you operate shared dev hosts, also enforce per-user /tmp via pam_namespace so the next bug like this doesn't matter.Sources
- GitLab Advisory — CVE-2026-46406
- CIRCL Vulnerability Lookup — GHSA-4vp2-6q8c-pvq2
- Phoenix Security — Three CVEs in Claude Code CLI
- Zscaler ThreatLabz — Claude Code Leak analysis
- Check Point Research — Critical Claude Code Flaws
- CSO Online — Claude Code MCP security problem
- CWE-59 — Improper Link Resolution Before File Access
- CWE-377 — Insecure Temporary File
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.