Incident
In December 2025, the maintainers of the AI coding tool added an automatic helper to the public place where their code lives and where anyone can file a bug report. The helper read each new report and replied to it. It was set up so that any account could set it off, and it was given the ability to run commands on the machine that handled the report.
Early in the new year, the researcher who had found a way to abuse this filed a formal security report and emailed the tool's security address. Weeks later, odd failures appeared around the nightly build job and the shared store of reused build files. Whether that was another researcher or someone hostile is unknown.
When the researcher published the findings, the maintainers removed the automatic helpers within 30 minutes and stopped the release job from reusing that shared store. The next day they said the keys had been replaced. The day after, following a report that some keys might still work, they replaced them again.
Eight days after the findings were published, an unauthorized version 2.3.0 of the tool's command-line package appeared on npm, published with an npm key that had not been properly revoked. Who published it is unknown. The program inside was identical to the earlier legitimate 2.2.3 release; the change was a single added line that installed a separate general-purpose AI assistant, one that can run commands, read files and browse the web, on every machine that updated.
The unauthorized version was live for roughly eight hours before the maintainers published version 2.4.0, marked 2.3.0 as deprecated, and revoked the key. How many of the tool's 5+ million users installed it in that window is not known. The maintainers said their own audit found no unauthorized releases on the editor extension marketplaces. Afterwards they moved npm publishing to a method that does not depend on long-lived keys.
Primary source — S1: https://snyk.io/blog/cline-supply-chain-attack-prompt-injection-github-actionsSnapshot
Cause of loss
- The automatic helper on the public project treated the words inside a filed report as work to carry out, and it could run commands on the machine that built the software The text a stranger typed arrived at the helper through the same channel as the maintainers' own instructions, and nothing in the helper's design separated the two. Because the helper could also run commands, a sentence in a bug report became a command on the build machine.Technical name: indirect prompt injection
- The only thing deciding which words counted as orders was the same AI that could be talked around by those words The rules about what the helper should and should not do were written into its own instructions, so the text that misled it also passed its own check on that text. A limit that lives inside the thing being manipulated is not a limit.Technical name: self-adjudicated instructions
- The job any stranger could start and the nightly job that held the publishing keys reused the same store of saved build files That store had a 10 GB limit per project, and once it filled up the oldest items were dropped to make room, so what one job left behind could take the place of what the other expected to find. The nightly builds also went out under the same publishing identity and package name as the real released version, so reaching the low-trust side reached the side that ships the product.Technical name: shared prod-dev infrastructure
- The replacement of the keys was declared finished without a check, key by key, that each old key had stopped being accepted These keys do not expire by themselves; they stop working only when someone cancels them and the company that issued them confirms it. Because completion was recorded rather than confirmed for each key, a key that was still accepted survived the cleanup and was later used to publish.Technical name: unverified credential revocation
Finding
You can keep two workers apart and give them different keys, and they will still both go to the same shelf for parts. One of them will accept a job from anyone who walks in. The other one publishes your product. Whatever the first leaves on that shelf, the second picks up later and uses, without looking at where it came from.
Verification
What only you can answer
- Did you assume that the automatic helpers attached to your public project could only be set off by people you already work with, and had you ever decided what a stranger's typed text should be allowed to make them do?
- When you were told after a report that the keys had been replaced, what did you take that sentence to mean, and had you agreed in advance with your team what proof of a finished replacement would look like?
- Would you knowingly accept your release job reusing saved files that a job any outsider can start is also allowed to write to, in exchange for faster builds?
What your AI can check
Report only; do not change any code or configuration. For every answer, cite file or console references so the finding can be checked by someone else.
- List every workflow in this repository that passes externally supplied text (issue titles, issue bodies, pull request titles or descriptions, comments, fetched web pages) into an AI agent prompt. For each, state whether that text is interpolated directly into the prompt template and who is permitted to trigger the workflow; cite file or console references.
- For each AI agent invoked in CI, report the exact tool list it is granted (shell, write, edit, fetch, search) and whether any allow/deny policy exists outside the model itself, such as a runner sandbox, a network egress rule, or a token scope, that limits which instructions it may act on. State plainly if the only constraints are wording inside the system prompt; cite file or console references.
- Report whether any workflow triggerable by a person without write access can read or write the same Actions cache scope, runner, or artifact store used by the release and publish workflows, and whether the publish workflows restore cached dependencies such as node_modules; cite file or console references.
- Report whether nightly, pre-release, and production builds publish under the same marketplace publisher identity, the same package name, and the same tokens, or whether each has a distinct identity and distinct token; cite file or console references.
- List every publishing credential this project holds for npm and for the extension marketplaces, including any created for one-off use. For each, report its creation date, whether it expires on its own, and whether there is a recorded check against the issuing registry showing that a superseded token is now refused rather than merely assumed revoked; cite file or console references.
Prevention
Treat anything a stranger can type as something that will be acted on. Ask whoever maintains your build setup for a one-page list of every automatic helper attached to your public project: who can set it off, and what it is allowed to do on the machine. Anything on that list that both accepts text from strangers and can run commands should be switched off until you have decided, in writing, that it is worth the trade.
Assume the instructions you give an AI helper are advice, not a fence. Where a helper genuinely needs to act, put the limit in the account and the keys it uses, so the worst it can do is fixed no matter what it reads that day. Ask your supplier to state plainly what that helper can reach if it is talked into doing something you did not ask for.
Keep the job that ships your product away from anything shared. Require written confirmation that your release job does not reuse saved files that a lower-trust job can write to, and that your nightly or test builds go out under a different identity, a different package name, and different keys from the version your customers install.
Treat "the keys have been replaced" as a claim to be checked, not a result. After any incident, ask for the full list of keys that existed before the cleanup and, for each one, evidence from the company that issued it that the old key is now refused. Where the service offers keys that expire on their own, prefer them, so a missed one runs out instead of waiting.
A key you have replaced is still live until the company that issued it says no to it.