Nov 10, 2026·9 min read·2 visits
A TOCTOU symlink race condition in Claude Code (< 2.1.251) on macOS allows attacker-controlled code running within the Bash sandbox to escape workspace restrictions and overwrite arbitrary user-writable files on the host via the unsandboxed Edit tool.
Claude Code prior to version 2.1.251 contains a Time-of-Check to Time-of-Use (TOCTOU) race condition in its internal file manipulation tools (Edit, Write, Read). Because path validation was performed using string-based pathname and symbolic link resolution without binding to the underlying directory inodes or open file descriptors, a process running concurrently inside the macOS Bash sandbox can atomically replace an in-workspace directory with a symbolic link after validation passes. This causes the unsandboxed main process to follow the symlink and overwrite user-writable files outside the permitted workspace boundary.
Claude Code (@anthropic-ai/claude-code) is an agentic command-line tool that executes automated coding tasks, repository analysis, and build workflows inside a developer terminal. To manage risk while executing external code, Claude Code employs a split privilege architecture. Shell commands generated or triggered during agent tasks run within a restricted Bash sandbox environment, while built-in native file operations (Edit, Write, and Read tools) execute directly inside the unsandboxed main host process with the full privileges of the invoking user.
Prior to version 2.1.251, the security boundary separating the sandboxed execution environment from the unsandboxed host file tools relied on point-in-time path validation. Before modifying a file via the Edit tool, the host process resolved the target file path and verified that the resulting canonical path resided within the user-approved workspace directory.
This validation logic suffered from a Time-of-Check to Time-of-Use (TOCTOU) race condition (CWE-367) combined with Improper Link Resolution Before File Access (CWE-59). Because the path check operated solely on string paths without pinning the underlying directory inodes or maintaining open directory file descriptors, a concurrent process running inside the macOS Bash sandbox could alter the directory structure after validation completed.
By atomically swapping a validated in-workspace parent directory with a symbolic link pointing outside the workspace before the actual file write occurred, code inside the sandbox could redirect the unsandboxed Edit operation. This allows an attacker to overwrite arbitrary user-writable files on the macOS host, bypassing the workspace isolation boundary and achieving full host code execution.
The root cause of CVE-2026-88968 is the decoupling of path authorization from file descriptor creation during cross-privilege file operations. In POSIX-compliant operating systems, any security check that validates a file path via string resolution functions (such as realpath(), stat(), or lstat()) and subsequently passes that same string path to a file access call (such as open() or fs.writeFile()) creates an inherent race window.
During the Time-of-Check (TOC) phase, Claude Code's unsandboxed controller inspects the target path requested by the Edit tool. It traverses the path components, resolves existing symbolic links, and confirms that the target path starts with the canonical workspace root prefix. At this stage, no kernel-level lock or reference is held on the intermediate directories.
On macOS ( utilizing APFS or HFS+ file systems), low-level system calls such as renameatx_np() with the RENAME_SWAP flag allow a process to atomically exchange two file system objects—including exchanging a standard directory with a symbolic link—in a single kernel operation. Because the Bash sandbox retains write access to the project workspace directory, a process running inside the sandbox can execute this atomic swap without interruption.
During the Time-of-Use (TOU) phase, the unsandboxed Edit implementation issues a standard file open call using the previously validated path string. The macOS kernel VFS layer re-evaluates the path string from root to leaf. When it encounters the newly inserted symbolic link in place of the former parent directory, it transparently follows the link outside the workspace and truncates or overwrites the external target file using the host user's credentials.
Because @anthropic-ai/claude-code is distributed as compiled/bundled JavaScript via npm and only the release tracking repository is public, the exact source diff is encapsulated within the release artifact for v2.1.251 (tracked in commit f1af9b1f4b1fd4c776135381606edada82ef638e). However, the vulnerability mechanics and release notes clearly define both the vulnerable design pattern and the necessary architectural remediation.
In the vulnerable implementation (< 2.1.251), the Read, Write, and Edit tools performed asynchronous or sequential user-space validation before invoking standard path-based file system APIs:
// Conceptual representation of the vulnerable TOCTOU pattern (< 2.1.251)
async function vulnerableEditFile(workspaceRoot, targetRelativePath, newContent) {
const fullPath = path.resolve(workspaceRoot, targetRelativePath);
// TIME OF CHECK: Resolve symlinks to verify path stays inside workspace
const realTarget = await fs.promises.realpath(fullPath);
const realWorkspace = await fs.promises.realpath(workspaceRoot);
if (!realTarget.startsWith(realWorkspace + path.sep)) {
throw new Error("Permission denied: Target outside workspace");
}
// [RACE WINDOW]: Attacker in Bash sandbox atomically swaps a parent directory
// in 'fullPath' with a symlink pointing to /Users/victim/.zshrc
// TIME OF USE: Standard write follows the newly swapped symlink
await fs.promises.writeFile(fullPath, newContent, "utf8");
}To eliminate this vulnerability class across Read, Write, and Edit operations, version 2.1.251 binds path resolution directly to underlying directory inodes and file descriptors:
// Conceptual representation of descriptor-bound resolution (v2.1.251 fix pattern)
int safe_open_in_workspace(int workspace_dir_fd, const char **path_components, int depth) {
int current_fd = dup(workspace_dir_fd);
for (int i = 0; i < depth - 1; i++) {
// Traverse each directory component without following symlinks
int next_fd = openat(current_fd, path_components[i], O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
close(current_fd);
if (next_fd < 0) return -1; // Blocks swapped symlinks immediately
current_fd = next_fd;
}
// Open final file component relative to the pinned parent directory inode
int file_fd = openat(current_fd, path_components[depth - 1], O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0600);
close(current_fd);
return file_fd;
}> [!NOTE]
> By opening the workspace root directory descriptor (O_DIRECTORY) and stepping through each subdirectory using openat() with O_NOFOLLOW, or by verifying device and inode (st_dev and st_ino) identity via fstat() on the opened file descriptor before writing, the remediation ensures that any mid-operation directory swap causes the operation to fail safely rather than traverse outside the workspace.
Exploiting CVE-2026-88968 requires two primary conditions: the victim must run an unpatched version of Claude Code (< 2.1.251) on macOS, and the attacker must achieve initial code execution inside the Claude Code Bash sandbox. In typical developer workflows, sandbox execution occurs when a user instructs Claude Code to build, test, or inspect an untrusted repository containing malicious build scripts, Makefile targets, git hooks, or npm lifecycle scripts (preinstall/postinstall).
Once the attacker's code is active inside the Bash sandbox, the exploitation proceeds through the following stages:
workspace/sub/target.txt) and prompt instructions or code errors designed to induce the agent to invoke the Edit tool on workspace/sub/target.txt.workspace/sub_link) that points to a sensitive directory outside the workspace, such as /Users/<username>/.renameatx_np(AT_FDCWD, "workspace/sub", AT_FDCWD, "workspace/sub_link", RENAME_SWAP) to rapidly oscillate the directory and the symlink, or monitors file access events to time the swap immediately after the realpath stat check completes.Edit tool's validation check inspects workspace/sub/target.txt while sub is a real directory, the check passes. If the atomic swap places the symlink at workspace/sub before the subsequent open() syscall, the unsandboxed host process writes the edited file contents directly to /Users/<username>/target.txt.By naming the target file .zshrc, .bashrc, or authorized_keys (inside a symlink targeting ~/.ssh), the attacker injects arbitrary shell commands or SSH keys into the host user's profile. The next time the user opens a terminal shell or triggers an unsandboxed hook, the attacker's payload executes with full host privileges outside the sandbox.
CVE-2026-88968 carries a CVSS v4.0 base score of 7.7 (High) with the vector CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N. The Attack Requirements (AT:P) metric reflects the necessity of winning a file system race condition while simultaneously executing code within the local Bash sandbox.
The primary security boundary violated by this flaw is the workspace isolation and Bash sandbox confinement on macOS. While the Bash sandbox is designed to prevent untrusted project scripts from modifying the host system or accessing sensitive user files outside the project directory, the Edit tool operates without sandbox constraints.
Further analysis of the changelog for v2.1.251 indicates that the underlying symlink swapping issue affected the Read and Write tools in addition to Edit. Consequently, an attacker winning the race during a Read tool invocation could disclose sensitive host files (such as ~/.ssh/id_rsa, ~/.aws/credentials, or environment files) into the agent context (VC:H), while Edit and Write operations allow arbitrary file corruption (VA:H) and modification (VI:H).
According to CISA Vulnrichment and EPSS data, the vulnerability currently has an EPSS score of 0.00317 (24.86th percentile), with no known active exploitation in the wild and no inclusion in the CISA Known Exploited Vulnerabilities (KEV) catalog.
Anthropic resolved CVE-2026-88968 in @anthropic-ai/claude-code version 2.1.251. Standard installations of Claude Code with automatic updates enabled receive this patch automatically. Users who manage installations manually or pin package versions in CI/CD environments must update their installations explicitly.
To remediate the vulnerability, upgrade the global npm package to 2.1.251 or later:
npm update -g @anthropic-ai/claude-codeAfter updating, verify the installed runtime version in the terminal:
claude --version> [!IMPORTANT]
> Ensure the output reports version 2.1.251 or higher before running Claude Code on untrusted third-party repositories.
For security engineering teams monitoring macOS developer endpoints, detection rules should focus on behavioral indicators associated with symlink race exploitation and post-exploitation persistence:
ES_EVENT_TYPE_NOTIFY_RENAME or EXCHANGEDATA events originating from processes inside the Claude Code Bash sandbox where directory paths are rapidly exchanged with symbolic links.~/.zshrc, ~/.bashrc, ~/.bash_profile), SSH configuration (~/.ssh/authorized_keys, ~/.ssh/config), or global Git configuration (~/.gitconfig) performed directly by the node / claude host binary during an active workspace session.CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@anthropic-ai/claude-code anthropics | < 2.1.251 | 2.1.251 |
| Attribute | Detail |
|---|---|
| Vulnerability Class | TOCTOU Race Condition (CWE-367) / Symlink Following (CWE-59) |
| Affected Component | Claude Code File Tools (Edit, Write, Read) |
| CVSS v4.0 Score | 7.7 High (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) |
| EPSS Score | 0.32% (24.86th Percentile) |
| Primary Platform | macOS (Bash Sandbox Environment) |
| Security Impact | Sandbox Escape & Arbitrary User-Writable File Overwrite |
| Fixed Version | @anthropic-ai/claude-code 2.1.251 |
| CISA KEV Status | Not Listed |
The software checks the state of a resource before using that resource, but the resource's state can change between the check and the use in a manner that invalidates the results of the check.
CVE-2026-88982 (GHSA-2xwh-426q-42c5) is an authorization bypass and data integrity vulnerability affecting Kubeflow Pipelines versions prior to 2.18.0. The flaw resides in the legacy V1 and raw-Argo execution caching mutating admission webhook and the associated cache-server storage backend. In multi-tenant deployments where namespaces share a centralized cache service, execution cache keys were derived solely from container specification fields without incorporating the workload's Kubernetes namespace. Additionally, the cache watcher trusted mutable pod annotations when persisting execution outputs. Consequently, an authenticated user with workload submission privileges in one tenant namespace can poison cached step outputs consumed by other tenants or read cached outputs generated by workloads in separate namespaces.
CVE-2026-88983 is an authenticated Server-Side Request Forgery (SSRF) vulnerability affecting the Kubeflow Pipelines API server in versions prior to 2.17.0. When an authenticated user creates a pipeline or pipeline version using a remote specification URI, the backend server retrieves the document via Go's default HTTP client without validating the target scheme, hostname, port, or IP address. Because the API server reflects remote connection errors and HTTP status codes in its response, an attacker with pipeline creation permissions can exploit the server as a status-code and port-scanning oracle against Kubernetes cluster-internal services, private networks, and cloud metadata endpoints.
CVE-2026-88985 is a SQL injection vulnerability (CWE-89) in the Kubeflow Pipelines (KFP) API server prior to version 2.17.0. The flaw stems from the untrusted deserialization of base64-encoded JSON pagination tokens (page_token) and the direct concatenation of metric names during run list sorting. Authenticated users with permissions to invoke REST or gRPC list endpoints can inject arbitrary SQL syntax into structural query identifiers and JOIN predicates, enabling unauthorized cross-tenant read access to all database records accessible by the API server.
A reflected Cross-Site Scripting (XSS) vulnerability exists in the Kubeflow Pipelines frontend server prior to version 2.15.0. Specifically, the Express route handlers for '/pipeline/k8s/pod' and '/pipeline/k8s/pod/events' reflect unvalidated 'podname' and 'podnamespace' query parameters, along with backend Kubernetes API error messages, directly into HTML-capable HTTP error responses. An unauthenticated attacker can exploit this flaw by convincing an authenticated user to visit a crafted URL, resulting in arbitrary JavaScript execution within the context of the Kubeflow Pipelines frontend origin.
CVE-2026-88984 (GHSA-ghgh-57qv-h687) is an incorrect authorization vulnerability affecting Kubeflow Pipelines (KFP) prior to version 2.16.1 in multi-user deployments. The multi-user API server treats the empty Kubernetes namespace associated with shared or global pipelines as an unconditional authorization exception across all API verbs. Consequently, any authenticated user can modify, overwrite, or delete shared pipeline definitions and pipeline versions without possessing the required Kubernetes RBAC permissions, compromising the integrity and availability of global machine learning workflows.
Prior to version 2.31.0, the Telepresence Traffic Manager exposed an unauthenticated gRPC API on cluster-internal port 8081 that lacked caller identity verification and Kubernetes RBAC authorization checks. An attacker operating any pod with network access to the Traffic Manager can register a session in an arbitrary managed namespace, establish service intercepts, redirect live production traffic, and exfiltrate the complete container environment of victim workloads, including mounted secrets and service account tokens.