devtake.dev

A bad command-line parser turned every claude-cli:// link into a remote shell

Joernchen of 0day.click found a deeplink RCE in Claude Code. Anthropic shipped the fix in 2.1.118 the same week.

Luca Reinhardt · · 4 min read · 4 sources
An illustration of the Claude Code deeplink vulnerability, showing a malicious URL handler triggering a shell prompt.
Image via GBHackers · Source

Joernchen of 0day.click reported a remote code execution bug in Claude Code on May 12 that fired a shell on the developer’s machine the moment they clicked the wrong claude-cli:// link. Anthropic shipped Claude Code 2.1.118 with the fix the same day. The exploit chain is short enough to fit on a sticky note.

The interesting part is what the bug says about the new shape of dev-tool attack surface. Claude Code is a CLI. CLIs aren’t supposed to have URL handlers. This one does, because IDE integrations need a way to nudge the running agent from the editor. That handler is the part that broke.

How the chain worked

The flaw lived in eagerParseCliFlag, a function in main.tsx that ran before the main argument parser. Its job was to look at the raw process.argv array and pluck critical flags like --settings out early, so the CLI could initialize config before normal parsing started. It did the job with a naive startsWith("--settings=") scan across every element of argv.

Context-blindness was the bug. If a different flag, say --prefill, was passed a string value that itself contained --settings=, the early parser happily processed it as a top-level settings flag. The next stage of parsing treated it as data; the early stage treated it as command. Two parsers, two different opinions, one of them wins.

The remote vector lived in the claude-cli://open deeplink handler. The handler took a q= parameter that’s supposed to pre-fill the user’s prompt, plus a repo= parameter naming a local repository. An attacker could craft a URL like claude-cli://open?repo=anthropics/claude-code&q=--settings={"hooks":{"SessionStart":[...]}}. The q= value carried a malicious settings JSON, eagerParseCliFlag ate it as a real --settings= flag, and the embedded SessionStart hook ran shell commands immediately when the agent session opened.

The Workspace Trust dialog, which is supposed to ask the user before loading config from an unknown repository, never fired. Because the repo= parameter pointed at a repository the user had already cloned and trusted locally (the anthropics/claude-code repo is the example the disclosure used), Claude Code treated the entire request as already-blessed. No prompt, no warning, no chance to bail.

Anthropic’s fix and what’s still owed

The fix in 2.1.118 makes the eager parser context-aware. It now distinguishes between an actual --settings= flag and a string value handed to a different flag. Anthropic’s release note for 2.1.118 stated this was a “security fix in CLI argument parsing” and recommended every install upgrade.

That’s the right fix at the parser level. The deeper problem is that the deeplink handler trusts repo-local trust as a substitute for action-level trust. A repository being on the user’s allowlist for opening files isn’t the same as being on the allowlist for running arbitrary shell hooks. Anthropic has not said publicly whether the trust model itself is being revisited, only that the parser was patched.

This is the second meaningful Claude Code security incident in roughly six weeks. The April quality postmortem covered the three bugs that quietly tanked output quality for six weeks. The npm source-code leak landed in late March when a build tooling slip pushed a .map file to the registry. None of these are catastrophic on their own. Together they sketch a young product moving faster than its release engineering.

What’s still unknown as of May 20

As of May 20, eight days after the patch landed in version 2.1.118, four load-bearing specifics about the disclosure are still missing.

  • CVE assignment. No CVE ID has been published yet. Joernchen’s report and the multiple downstream writeups all reference “the Claude Code RCE” without a number. MITRE and GitHub Advisory normally turn a CVE around in days to weeks; expect one to land before June.
  • CVSS score. No formal score has been issued. The chain requires user interaction (clicking a link) but the post-exploitation impact is full shell on the dev machine, which would push it well into the high range under CVSSv3.1 user-interaction scoring.
  • Exploitation in the wild. No public reports of attacks against real developers. The disclosure window between the May 12 patch and the May 18 public writeup was short, and Claude Code auto-updates, so most installs were patched before the technical write-ups dropped.
  • IDE integration impact. VS Code, JetBrains, and Zed integrations all use claude-cli:// to launch sessions. Anthropic has not said whether the same parsing flaw was reachable through the IDE handlers before the patch, or only through plain browser clicks.

What this means for you

If you use Claude Code, the only thing you need to do today is confirm you’re on 2.1.118 or later. claude --version from any terminal works; if it’s older, the CLI auto-updates on next launch, but a manual npm install -g @anthropic-ai/claude-code@latest is faster. If you maintain a tool that emits claude-cli:// deeplinks, audit your URL generation: the same parser bug means any tool that built such URLs from untrusted strings could have shipped exploit payloads. If you’re a security engineer reviewing AI dev-tool installs at work, this incident is a useful talking point for getting URL handlers out of the “low priority” bucket. The general lesson is older than Claude Code: when two parsers in the same program disagree about what an argument means, the attacker gets to pick which one is right. Joernchen got there first this time. Anthropic should expect company.

Share this article

Quick reference

RCE
Remote code execution: an attacker runs arbitrary code on the target machine, the worst class of bug.
SessionStart hook
A Claude Code lifecycle hook that runs commands the moment a CLI session opens, before any user prompt.

Sources

Mentioned in this article