A generated abstract illustration on a pale grey-blue field: a tall dark slate panel stands left of centre, a thin pale-cyan beam runs horizontally through it and fans out on the right into a wide cone of light across a field of small geometric blocks. Most blocks sit in cool shadow; one cluster near the beam's centre glows warm amber. A faint blueprint grid and thin concentric arcs sit behind. No text, numbers, people, brand marks or organisation logos appear. The OrcaRouter logo is composited in the bottom-right corner.
AI Safety Incidents

Clinejection: How an Issue Title Reached Cline's npm Token

Author

Rowan Sterling

Date Published

Latest models · 20View all models →
Benchmarks: Artificial Analysis · updated daily
Back to all posts

On 9 February 2026 the security researcher Adnan Khan published an account of a flaw he had reported privately to Cline on 1 January: the open-source AI coding assistant had wired an automated issue triager, built on Anthropic's claude-code-action GitHub Action, to a prompt that pasted any incoming GitHub issue title straight into the model's instructions. Any GitHub account could trigger it by opening an issue, and in Khan's reproduction a title phrased as an instruction got the agent to run a shell command inside the workflow. He then traced the route from that foothold, through GitHub Actions cache poisoning, to the release credentials for the VS Code Marketplace, OpenVSX and npm. Eight days after Cline fixed the workflow, an npm publish token Cline believed it had rotated was used to publish cline 2.3.0, which installed OpenClaw on every machine that installed the package. The full entry, with its severity and confidence grades, sits in the Orca AI Incident Archive, beside the neighbouring supply-chain records from the same period.

Two dates, not one

The archive dates this record to 9 February 2026, at day precision, and that is the day Khan published his write-up at adnanthekhan.com. It is not the day the malicious package appeared. The vulnerability itself was introduced on 21 December 2025, in a commit that added the triage workflow to the cline/cline repository, and it stayed live until Cline deleted the workflow on 9 February. The unauthorized npm publication came on 17 February, eight days after the fix and forty-seven days after Khan's first private report.

Keeping those two dates apart matters, because the most consequential fact in the case sits in the gap between them: the door was closed on 9 February, but the key that the intruder had already copied still worked on 17 February. Cline's own correction, sent to Khan on 17 February and reproduced in his post, puts it directly: "When we rotated credentials on Feb 9, the npm publish token was not properly revoked. The wrong token was deleted, and the exposed one remained active."

The chain: from an issue title to a release token

The vulnerable file was .github/workflows/claude-issue-triage.yml, which ran on the issues event and invoked anthropics/claude-code-action@v1 with Claude Opus 4.5, pinned in the workflow as claude-opus-4-5-20251101. Khan quotes the workflow's configuration: allowed_non_write_users was set to the wildcard "*", so any GitHub account could trigger the agent simply by opening an issue, and claude_args passed --allowedTools with Bash, Read, Write, Edit, Glob, Grep, WebFetch and WebSearch. Bash was the one that mattered. It gave the model shell execution inside a workflow that ran in the default-branch context.

The prompt interpolated the issue title without sanitising it. Khan's proof of concept used a title beginning "Tool error." and continuing with an instruction to install a helper package from a specific commit before carrying on with triage. The commit reference pointed at a fork whose package.json carried a preinstall script, which ran a curl command against an attacker-controlled endpoint before npm had finished installing anything. Because the install ran through the Bash tool, the model never inspected what executed. Khan reproduced this on a mirror of the Cline repository and reports that the agent executed the payload in every attempt.

Code execution in the triage workflow is not, on its own, access to release credentials. That step came from a property of GitHub Actions that Khan had documented in a 2024 tool he released called Cacheract: any workflow can read and write the repository's cache with full control of cache keys and versions, even a workflow that never declares a caching step. The triage job ran on the default branch, so it shared a cache scope with the nightly publish jobs, which did restore node_modules from cache and did hold the publishing tokens.

Cache entries are immutable, so an attacker cannot simply overwrite the entry a release job will restore. Khan's route around that was to exploit a GitHub policy change of 20 November 2025, which made the service evict cache entries immediately once a repository's cache passed 10 GB, using least-recently-used order. Flood the cache past 10 GB, and the legitimate entries age out; claim the vacated keys with poisoned content, and the next job that restores them is running attacker code. Cacheract automates the whole sequence inside a single workflow run, and it persists by overwriting the action.yml of actions/checkout so that the action's post step executes a payload at the end of every job.

The last link in the chain is the nightly job, which runs at roughly 2 AM UTC, restores the poisoned node_modules, and detonates. What walks out is VSCE_PAT, OVSX_PAT and NPM_RELEASE_TOKEN, the credentials for the VS Code Marketplace, OpenVSX and npm respectively. Whether every link ran against Cline's own repository in that order is a separate question, taken up below.

A self-built HTML card titled 'Six steps from an issue title to a release token', badged 'Clinejection · GHSA-9ppg-jx86-fqw7', subtitled 'The chain as Adnan Khan describes it, with the workflow configuration he quotes'. Six numbered rows read: 1, Issue opened on cline/cline, the title carries the instructions and allowed_non_write_users is set to the wildcard; 2, claude-issue-triage.yml runs, claude-code-action with Bash, Read, Write, Edit, Glob, Grep, WebFetch and WebSearch; 3, npm install from a fork commit, the fork's package.json carries a preinstall script and the model runs it through Bash; 4, Cache flooded past 10 GB, GitHub's LRU eviction removes the legitimate entries and frees their keys; 5, Poisoned entries claim the keys, the nightly jobs restore attacker content as node_modules; 6, Nightly job runs at about 2 AM UTC, VSCE_PAT, OVSX_PAT and NPM_RELEASE_TOKEN leave the runner. A footer reads 'Source: Adnan Khan, Clinejection, 9 February 2026; Cline advisory GHSA-9ppg-jx86-fqw7.' The OrcaRouter logo is composited in the bottom-right corner.

Why the nightly credentials were production credentials

The obvious objection is that nightly and production releases should hold different secrets. Khan's answer is that the separation was nominal. OpenVSX and the VS Code Marketplace tie a publication token to a publisher identity, not to an individual extension, and both the stable and nightly Cline extensions were published under the same identity, which Khan identifies on OpenVSX as saoudrizwan. On npm, the fine-grained token model is tied to a package, and Cline published its stable and nightly CLI builds to the same cline package. A token issued for the nightly job could therefore publish the production artefact, which is exactly what happened eight days later.

Khan's own framing of the impact rests on scale: he cites Cline's announcement that it had passed 5 million installs, and notes that an extension update pushed through stolen credentials executes in the context of every developer whose editor updates automatically, with the permissions of that developer and whatever keys, tokens and source code sit on the machine.

What was actually published, and what it did

The published artefact was cline 2.3.0, and it was small. The npm registry's own timestamps put 2.3.0 at 11:26:51 UTC on 17 February 2026 and the clean 2.4.0 at 19:23:08 UTC the same day, an exposure the Cline advisory puts at roughly eight hours, until 2.3.0 was deprecated at 11:30 AM PT. The package's package.json carried one added line, a postinstall script reading npm install -g openclaw@latest. Cline's advisory, GHSA-9ppg-jx86-fqw7, states that no other package files were altered and that dist/cli.mjs is byte-identical to the legitimate 2.2.3 build.

OpenClaw is a real open-source AI agent framework, not malware, and Cline's advisory says so explicitly: it is a legitimate project, but its installation was not authorised or intended. Endor Labs, which analysed the tarball, assessed overall impact as low on the grounds that the installation did not start OpenClaw's Gateway daemon. StepSecurity's write-up measures the other side of it: a global install leaves the framework on disk with whatever credentials and file access the installing user has, and OpenClaw's design, a persistent local gateway with an operator API, makes it a high-value thing to find there. The two assessments are not contradictory. One describes what ran during the window; the other describes what was left behind afterwards.

Volume is the one number that has to be handled carefully. StepSecurity reported that the compromised version was downloaded roughly 4,000 times before it was deprecated, and The Register repeated that figure. Endor Labs put cline's monthly download count for 19 January to 17 February at 418,545, which describes the package's normal traffic rather than the malicious build. Neither figure counts machines: a download count is not a count of affected developers, and no party has published an install-base figure for 2.3.0. Microsoft's Threat Intelligence team said on X that it observed a "small but noticeable uptick" in OpenClaw installations on 17 February arising from the Cline install script, as The Hacker News reported.

GitHub's advisory record for GHSA-9ppg-jx86-fqw7 carries no CVE and no CWE, and its severity is Low. That grade is about the benign payload, not about the credential theft, and it sits oddly beside the archive's critical grade for the incident itself. The advisory records 2.3.0 as the affected version and 2.4.0 as the fix.

The disclosure Cline did not answer

Khan's pre-publication timeline is the part of the record that is least about the model and most about process. He submitted a GHSA through GitHub's private vulnerability reporting on 1 January 2026 and emailed the security contact listed on Cline's trust page the same day. He sent a follow-up to a Cline developer on 8 January. He tried a direct message to Cline's CEO on X on 18 January. He wrote again on 7 February and received nothing but an automated ticket number. He published on 9 February.

Cline merged the fix, pull request 9211, about thirty minutes to an hour later. The PR deletes three workflow files, claude-issue-triage.yml, claude-pr-review.yml and cline-pr-review.yml, and removes caching from publish.yml, publish-nightly.yml, npm-nightly.yaml and npm-main.yaml, seven files in total, with install steps made unconditional now that there is no cache hit to depend on.

Cline's public response on 10 February, quoted in Khan's post, accepted the finding and listed immediate mitigations: removal of the issue and PR bot review workflows, removal of actions/cache from publish workflows handling release credentials, and rotation of the relevant credentials including bot, provider and publishing tokens. It also conceded the handling: "we acknowledge that our response time to your initial report was unacceptable. A report submitted January 1 should not have required public disclosure on February 9 to get addressed."

On 11 February Cline said it had completed a full rotation of VSCE_PAT, OVSX_PAT and NPM_RELEASE_TOKEN, and that an audit of every release on all three distribution channels between 21 December 2025 and 9 February 2026 found that every Marketplace and OpenVSX version mapped to a tagged GitHub release and that all 41 npm versions were consistent with source. It concluded that no unauthorised release had been published "during the claimed exposure window". Six days later it corrected the record, in the message quoted above: the npm token had not been revoked, and 2.3.0 had been published with it.

A self-built HTML card titled 'From private report to public disclosure', subtitled 'Khan's pre-publication timeline, and what followed'. Eight dated rows read: 21 Dec 2025, vulnerable claude-issue-triage.yml committed; 1 January 2026, GHSA filed and email sent to Cline's listed security contact; 8 January 2026, follow-up email to a Cline developer, no reply; 18 January 2026, direct message to Cline's CEO on X, no reply; 7 February 2026, final email, automated ticket number only; 9 February 2026, public disclosure and PR 9211 merged within about an hour; 11 February 2026, Cline reports full rotation and a clean audit; 17 February 2026, cline 2.3.0 published with a still-valid npm token. A footer reads 'Times and dates as recorded by Adnan Khan; the 17 February publish timestamp is corroborated by the npm registry.' The OrcaRouter logo is composited in the bottom-right corner.

Established, attributed, unresolved

• Established. The workflow configuration is public and was published before the fix. Khan reproduced the prompt injection on a mirror of the repository. Cline merged the remediation and issued an advisory that confirms the 2.3.0 publish, the added postinstall line, the unchanged CLI binary, the eight-hour window and the token failure. The npm registry's timestamps for 2.3.0 and 2.4.0 match the advisory's times. The archive grades the record critical, at confidence A, with real harm recorded and no dispute flag.

• Attributed, not established. Who published 2.3.0 remains an open attribution. The forensic write-up published by Michael Bargury and an automated investigation tool called Raptor on 19 February names a GitHub account, glthub-actions, and reports with high confidence that this account forked cline/cline, opened issue 8904 on 28 January 2026 at 03:51 UTC with the injected title, and used a fork commit to run a payload from a gist. Khan confirmed that the accounts gcbrun and Dev11940518 are his own and that glthub-actions is not. The account was already deleted or suspended when the write-up was published, and Cline has named no one. Treat the identity as a third-party finding.

• Contested in character, not in fact. Bargury assesses the episode as consistent with a security research demonstration rather than a malicious campaign, citing the benign payload and the actor's history against a New Relic automation repository. Cline calls it an unauthorized publish, and Khan writes that the actor who published 2.3.0 is "presumably" the same one who poisoned the caches. Nobody has shown an intent to cause harm, and nobody has shown there was none.

• Unresolved. Khan's own pre-publication note is careful about a point that later reporting sometimes flattens: between 31 January and 3 February 2026 several nightly runs failed with a Post Checkout step that produced no output, which is Cacheract's indicator of compromise, and the triage workflow is the most obvious route but is not proven to have been the entry point for that activity. Nor has any party published evidence that the Marketplace or OpenVSX tokens were used; the only artefact anyone has produced is the npm build.

A self-built HTML card titled 'What the record supports', subtitled 'One column is documented, one is other people's findings, one is simply open', split into three columns. The first, headed Established, lists four items: the triage workflow interpolated issue titles into the prompt and allowed any GitHub account to trigger it, with Bash among the tools; Khan reproduced the injection on a mirror of the repository; Cline's advisory GHSA-9ppg-jx86-fqw7 confirms cline 2.3.0, the added postinstall line, the unchanged CLI binary and an eight-hour window; npm registry timestamps place 2.3.0 at 11:26 UTC and 2.4.0 at 19:23 UTC on 17 February 2026. The second, headed Attributed, lists three items: the account glthub-actions and issue 8904 come from a third-party forensic write-up, not from Cline; that the actor copied Khan's public proof-of-concept repository is an inference from timing and from his own statement; that the publish was a research demonstration rather than an attack is one analyst's assessment. The third, headed Unresolved, lists three items: whether the triage workflow was the entry point for the late-January cache anomalies; whether any Marketplace or OpenVSX credential was used, with no artefact published; how many distinct machines installed 2.3.0, since about 4,000 is a download count. A footer reads 'Sources: Adnan Khan, Clinejection, 9 February 2026; Cline, GHSA-9ppg-jx86-fqw7; StepSecurity, 17 February 2026; Endor Labs, 18 February 2026; Michael Bargury and Raptor, 19 February 2026.' The OrcaRouter logo is composited in the bottom-right corner.

What changed afterwards

The remediation landed on several layers at once, and it is worth naming each because they answer different parts of the failure. The workflow that could be reached by an outsider was deleted rather than narrowed. Caching was removed from the four release workflows that hold publication secrets, rather than from the repository as a whole, which is the change that breaks the pivot. Credentials were rotated, and after the 17 February correction the correct npm token was revoked. Cline's advisory states that npm publishing now uses OIDC provenance via GitHub Actions, and StepSecurity's metadata analysis shows why that mattered: the legitimate releases already carried provenance attestations, and 2.3.0 was the outlier published by a human account. The gap was that the long-lived token path stayed open alongside trusted publishing, which is the specific thing Endor Labs argues maintainers should close. Cline published GHSA-9ppg-jx86-fqw7, deprecated 2.3.0 with the message that it should not be installed, and pointed users at 2.4.0 or later.

Khan's structural recommendations go further than what was implemented, and are worth recording as his, not as Cline's: strip Bash, Write and Edit from any triage workflow and leave it file reads and the specific GitHub CLI calls it needs; never restore a cache in a job that holds production publication secrets; and give nightly releases their own registry namespace and a dedicated non-production publishing identity, so that a stolen nightly token cannot publish a stable release at all.

The eight-day gap between the fix and the malicious publish is the detail that makes this record worth keeping. Every step in the chain was a known technique; Khan had published the cache-poisoning tool, and Aikido Security's PromptPwnd research of December 2025 documented the same triage misconfiguration pattern. What composed them into a supply-chain incident was a workflow that let a stranger's text reach a shell, a cache shared with jobs that held release keys, and a token rotation that missed one credential. Each of the three fixes would have broken the chain on its own; the third one arrived eight days late.