Abstract technical illustration on a dark navy field: a small flat-vector package cube at centre-left, with a thin amber line hooking down into a compact representation of a developer workstation; a fan of fine cyan and steel-blue filaments spreads right from the workstation toward many small circular nodes on a loose grid, two of them curving back up to the package cube, and one node on the far right sits alone and disconnected. No people, logos or text appear. The OrcaRouter logo is composited in the bottom-right corner.
AI Safety Incidents

Tensorlake's npm SDK Was Poisoned to Steal AI-Agent Credentials — and to Wipe Your Home Directory If You Revoked the Token

Author

Alistair Wren

Date Published

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

On 8 October 2026, a poisoned release of tensorlake — the npm SDK for Tensorlake's platform, which runs untrusted, LLM-generated code inside isolated sandboxes — reached the npm registry. Version 0.5.144 was published at 01:12:07 UTC; Socket's researchers say they flagged it about eleven minutes later. It was not an authorised release. Tensorlake states that the attacker pushed code directly into the company's GitHub repository and published it through the project's own release workflow, so the package carried valid npm provenance — signed attestation showing where it was built, for source that had already been poisoned. The company says the version was live for roughly 1 hour 40 minutes, from 01:12 to 02:54 UTC, before npm removed it.

The payload needed no import and no running agent. A preinstall hook in the package manifest ran node lib/setup.mjs, an obfuscated loader that dropped the Bun runtime onto the machine and used it to run lib/Math_Symbol.js, an 856,501-byte credential stealer and self-propagating worm. Anyone who ran a plain install of that one version executed it. Because it was the package's latest tag, that version is what a default install resolved to while it was live.

What makes this record worth keeping is not the payload's size — the Shai-Hulud family has been documented before. It is the target list. Alongside npm and GitHub tokens, AWS credentials, Kubernetes and Vault secrets, SSH keys, .env files, browser passwords and cryptocurrency wallets, this build specifically collected AI development tooling configuration and MCP files — those associated with Claude Code, Cursor, Kiro, Windsurf and Zed, per Socket. It also wrote its own .claude/settings.json and .vscode/tasks.json into repositories it could reach, so the code would run again the next time a developer opened that project in Claude Code or VS Code. It is a supply-chain attack aimed squarely at the machines people build agents on.

How the release got out of a maintainer's account

Socket, SafeDep and Aikido each published their own analysis on 8 October, and their accounts of the mechanism agree. The archive's confidence grade for this record is A, it is not marked disputed, and nothing found in the sources contradicts any of them.

SafeDep's write-up traces eight commits to the repository's main branch between 01:20 and 03:57 UTC on 7 October, attributed to a repository administrator account and made through the GitHub web interface. The commit that the archive cites as the first rogue one is 41b38f0, a four-file revision from 03:44:48 UTC on 7 October whose message is "Add files via upload" and which carries GitHub's verified signature — GitHub signs commits made in its own web editor, so "verified" records the editing surface, not the person. That commit touched no files beyond typescript/lib/Math_Symbol.js and typescript/lib/setup.mjs, according to the GitHub API: the payload, and nothing else.

SafeDep offers a reading of the operator's behaviour, labelled as its own inference: the first edit of the preinstall hook broke the file's JSON syntax and a repair commit followed nineteen minutes later, which it describes as consistent with a person editing files in a browser rather than a scripted pipeline. Nothing published by Socket, SafeDep, Aikido, StepSecurity or Tensorlake names the method by which the maintainer's account was taken over.

The attacker did not need an npm token. SafeDep reports that the account started the project's publish_npm.yaml workflow by hand on 8 October at 00:08 UTC; the build log shows the payload files inside the published tarball. The release used npm trusted publishing, and the sibling tensorlake-native-* packages published from the same run carry valid SLSA provenance — provenance that is genuinely valid for a build of poisoned source. The release landed twelve minutes after the workflow started.

The archive records that the repository was compromised for roughly 20 hours before the publish fired — the gap the timeline below lays out commit by commit — which is consistent with Tensorlake's own statement that the attacker published "through our own release workflow".

What the payload did once it ran

A screenshot of a card titled 'A credential stealer with an AI-tooling list', kicked 'tensorlake@0.5.144 · what the payload collected', subtitled 'Per Socket, SafeDep and Tensorlake's advisory — grouped by what each collector reached for'. Three columns of paired labels sit side by side. 'Publishing and source control' lists npm tokens from .npmrc, the token API and the OIDC exchange; GitHub tokens with a repository and workflow permission check; GitHub Actions secrets read from Runner.Worker memory; and cryptocurrency browser wallets across 14 extensions. 'AI agent tooling' lists ~/.claude.json for Claude Code settings; ~/.kiro/settings/mcp.json for Kiro MCP config; .cursor, Windsurf and Zed config and MCP files; and .claude/settings.json and .vscode/tasks.json written back into reachable repositories. 'Cloud, cluster and endpoint' lists AWS via STS, SSM Parameter Store and Secrets Manager; Kubernetes service-account tokens and kubeconfigs; HashiCorp Vault at 127.0.0.1:8200; SSH and GPG keys, .env files and Docker and CLI files; the Wallets row, wallet.dat, Exodus, Electrum and MetaMask; and saved passwords via a downloaded HackBrowserData binary. A footer reads 'Collector list per Socket's initial assessment, SafeDep's decoded payload analysis and the Tensorlake advisory (GHSA-8g63-53c9-65j2). Socket flags AI development tooling for Claude Code, Cursor, Kiro, Windsurf and Zed by name.'

Socket's assessment identifies harvesting across local files, CI environments, Kubernetes and Vault sources, plus exfiltration, persistence and execution of remotely supplied code. SafeDep reproduced the decoded payload: lib/setup.mjs downloads Bun 1.3.13 from the official GitHub release and uses it to run the worm, and it exits on CI runners — the target is a developer machine, not a build agent. SafeDep also found the payload carries a plain copy of that same loader, which it plants into other packages and repositories.

The payload runs three obfuscation layers, which SafeDep decoded without executing the sample: an obfuscator string array, a custom cipher using the salt svksjrhjkcejg — identical to the one reported in the earlier keyv compromise — and AES-256-GCM over eleven embedded files, including scripts, hooks and two RSA public keys. Its first line carries the build tag WORMTAG='tensrlake', misspelled, which is how SafeDep distinguishes this build from an ongoing reinfection.

Beyond the AI-tooling configuration, the collection list is broad and specific: ~/.claude.json and ~/.kiro/settings/mcp.json for agent settings; SSH keys, cloud CLI files, .npmrc, .env, Docker and Kubernetes files, and shell history; wallet.dat plus Exodus, Electrum and Ledger Live data and browser wallet extensions such as MetaMask and Phantom; AWS STS, SSM Parameter Store and Secrets Manager; GitHub Actions secrets read from Runner.Worker process memory; and Kubernetes service-account tokens and Vault tokens. It fetched a per-platform binary from iseekaigogo[.]com/hbd/<platform> and ran it with the arguments "dump -b all -c password -f json" — arguments that match the HackBrowserData tool, though SafeDep notes the binary itself was not recovered and labels the identification an inference. The worm, SafeDep adds, exits on Russian locales.

A screenshot of a card titled 'Persistence, resolution, and the wipe trigger', kicked 'tensorlake@0.5.144 · staying power', subtitled 'The release was removed in under two hours; the implant it installed was built to outlive that.' Three columns sit side by side. 'Persistence: gh-token-monitor' lists a systemd user service on Linux, a LaunchAgent com.user.gh-token-monitor on macOS, a scheduled task with a PowerShell monitor on Windows, and files left behind in ~/.local/bin, ~/.config and %LOCALAPPDATA%. 'Endpoint resolution order' lists, in numbered order, the primary endpoint iseekaigogo[.]com:443/router; an Ethereum contract 0xb614155Fd88...1Df091B9; the public RPC endpoints that contract is read through, given as ~30 by Socket and 35 by SafeDep; signed GitHub commits found by the string thebeautifulmarchoftime; and public dead-drop repositories described as 'Shai-Hulud: Here We Go Again'. 'The dead-man switch' lists a poll of api.github.com/user every 60s, for 24 hours, firing on a 40x response to that token, with the effect rm -rf ~/ (advisory: home dir), armed only if the account has no orgs, and the order that matters: remove monitor, then revoke. A footer reads 'Per Socket's blog and SafeDep's analysis, with the C2 endpoint, the contract and the gh-token-monitor service confirmed in Tensorlake's advisory. Socket and SafeDep give different counts of the public RPC endpoints the contract is read through.'

Persistence is deliberate. Socket wrote of the payload: "That combination extends the risk beyond a single stolen API key," adding that "persistence can retain attacker access after the affected dependency is removed." SafeDep reports the worm commits Claude Code and VS Code hook files as the git author claude, and plants a "Run Copilot" workflow that dumps all repository secrets and then deletes the run and the branch. The Tensorlake advisory tells developers checking their logs to look for a branch named dependabot/github_actions/format/setup-formatter and commits authored under that same identity.

Spread runs on stolen tokens rather than on a second vulnerability. Socket says the worm enumerates the packages associated with a victim's publishing identity, builds Sigstore provenance, and republishes poisoned versions. SafeDep adds the detail that inside CI it adds a git dependency and signs its output with Fulcio and Rekor — so the provenance on the package it publishes next is as valid as the provenance on 0.5.144 was.

The twenty hours before the publish

The gap between the first rogue commit and the release is the part of the timeline that explains how a poisoned package came to carry valid provenance. All times are UTC; commit times are SafeDep's and the GitHub API's, and the publish, removal and advisory times are Tensorlake's.

A screenshot of a card titled 'Compromised source in the repo, then one manual publish', kicked 'tensorlake@0.5.144 · the ~20-hour window', subtitled 'All times UTC. Commit times per SafeDep's timeline and the GitHub API; publish and removal times per the Tensorlake advisory.' Three columns sit side by side. '7 October 2026 — source, main branch' lists 01:20 setup.mjs and Math_Symbol.js added; 01:24 the preinstall script added; 01:43 package.json JSON repaired; 02:13 to 03:44 four loader and payload revisions; 03:44 commit 41b38f0, the payload commit; 03:57 the version set to 0.5.144. '8 October 2026 — release and removal' lists 00:08 publish_npm.yaml started by hand; 01:12:07 version 0.5.144 published to npm; 01:22:02 issue #1014 opened by StepSecurity; 01:23:10 flagged by Socket, about eleven minutes in; 02:54 the version removed, after roughly 1h 40m live; 20:24 version 0.5.145 published next. '9 October 2026 — disclosure closed' lists advisory GHSA-8g63-53c9-65j2; pull request #1016, source reverted and 0.5.145 cut; issue #1014 closed as completed; 0.5.143 as the last version stated unaffected; and no usage seen, per Tensorlake's user-agent telemetry. A footer reads 'The ~20-hour gap between the first rogue commit and the publish is SafeDep's commit timeline, and matches the archive's own upstream record.'

Read across: the payload files landed in the repository's main branch on 7 October, the version was set to 0.5.144 at 03:57 that morning, and nothing was published until an account started the release workflow by hand at 00:08 the next day. The compromised source sat in the branch for roughly twenty hours before the project's own pipeline built it. That is the whole of the attack on the release path — no forged attestation, no republished tarball. The provenance was accurate about a build of code that should never have been in the branch.

The dead-man's switch

The part of this incident that has no precedent in earlier npm compromises is what happens after a victim reacts correctly. If the stolen GitHub token belongs to an account with no organisations, SafeDep reports, the worm installs gh-token-monitor: a service that polls api.github.com/user with that stolen token every 60 seconds, for 24 hours. When GitHub returns a 40x — as it does the moment the token is revoked — the monitor runs an attacker-supplied handler and deletes the user's home directory.

Socket describes the same component as using a PowerShell monitor that persists through an ONLOGON scheduled task, executing an attacker-supplied handler through Invoke-Expression, and notes the code carries the literal string "IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner", seen in earlier Shai-Hulud waves. SafeDep names the handler as rm -rf ~/. Socket's wording is explicit that this fires regardless of where the revocation happens; the monitor is local, and the revocation is not. All four sources — Socket, SafeDep, StepSecurity and Tensorlake's own advisory — give the same order of operations: remove the monitor first, then revoke.

What is established, and what is only inferred

The archive records no confirmed harm for this entry, and the sources support that rather than merely failing to contradict it.

• Tensorlake states no usage of the version. Its advisory says the company monitors SDK usage through user-agent headers and has "not seen any usage of this version since it was released". That is a vendor statement about its own telemetry, not an audited count.

• The download figure is not an infection count. Socket reports the package at roughly 12,000 weekly downloads and notes that this describes the package's overall usage, not downloads of the malicious version; SafeDep cites about 106,000 monthly downloads on the same package. Neither is evidence that 0.5.144 ran anywhere. Socket's flagged-release window and Tensorlake's two-hour window are consistent with a version that was removed before it spread.

• The payload's own bugs may have limited it. SafeDep found two: the npm propagation path needs ./dist/Math_Symbol.js on disk and stops without it, and the planted loader looks for ai_init.js while the worm writes math_init.js. SafeDep labels these findings as limitations on spread, and the article treats them as one analysis's reading of the code, not as a guarantee that nothing propagated.

• The provenance attestation is real. This is not a disputed claim: npm provenance records where a package was built and by which workflow, and in this case it recorded the truth — the project's own release workflow, building poisoned source. Tensorlake's advisory states it in as many words: "the package carries valid npm provenance". Socket puts the same point plainly when it notes that provenance "says where a package was built. It doesn't say the code is safe."

Several details in circulation are single-analysis claims and should be read as such. The specific mechanism by which the attacker obtained the maintainer's account has not been published by any of the investigating firms or by Tensorlake. The Ethereum contract address and its role in resolving the endpoint appear in SafeDep's and Socket's accounts — Socket says the payload resolves its endpoint through an Ethereum contract using approximately 30 public RPC endpoints with a GitHub fallback, while SafeDep puts the RPC count at 35 and reports that on 8 October the contract returned iseekaigogo.com — and Tensorlake confirms the endpoint and the contract in its advisory. The exact RPC count differs between the two write-ups; the design does not.

One gap in the public record is worth stating plainly. None of the sources reviewed for this article reports a confirmed downstream infection — another package or repository poisoned by the worm using credentials stolen from this one. The propagation code exists and SafeDep has read it; whether it produced a second victim is unknown.

What changed afterwards

The response was quicker and more coordinated than the incident.

• The version is gone. It was live from 01:12 to 02:54 UTC on 8 October, according to Tensorlake; SafeDep reports that npm removed it. Querying the npm registry directly on 10 October for this article, 0.5.144 no longer appears among the package's published versions, and the package's latest tag is 0.5.148. Tensorlake's advisory points developers at 0.5.143 as the last unaffected version.

• The advisory is published with a fix. Tensorlake's GHSA-8g63-53c9-65j2 advisory states that the PyPI and crates.io tensorlake packages and the tensorlake CLI were not affected — only the npm package was — and that versions 0.5.143 and earlier are unaffected. SafeDep reports the maintainers reverted the source in pull request #1016 and released 0.5.145.

• The disclosure thread is unusually complete. StepSecurity's researcher Ashish Kurmi opened GitHub issue #1014 at 01:22:02 UTC on 8 October — about ten minutes after the publish — pointing at StepSecurity's own write-up of what it calls the "hostage token" worm. Aikido's Oliver Smith commented that the payload was introduced in commit 41b38f0 and identified it as a new Shai-Hulud information stealer. Maintainer David Lorenzo — the maintainer identity listed on the npm package as calavera — replied on 9 October confirming the security advisory and closing the issue as completed the same day. The issue title itself tracks the evidence: it began as a "possibly malicious" report and was renamed, by the time it closed, to describe the payload as malicious without qualification.

• The campaign this belongs to is larger than this package. Socket, SafeDep and Aikido all place the Tensorlake release in the ChainDrop / Mini Shai-Hulud wave first documented in early August 2026, whose npm victims included keyv, cacheable and related packages. Tensorlake's advisory says the same, citing Socket, SafeDep and Aikido.

Socket's guidance to anyone who installed the version is that the host should be treated as compromised: check lockfiles, manifests and build logs for the version; remove the gh-token-monitor service before revoking any token; then rotate credentials from a clean machine. Tensorlake's advisory adds the same ordering and suggests checking package registries and repositories the exposed tokens could write to for unexpected releases. Aikido's write-up advises setting ignore-scripts=true in .npmrc to block install hooks, and treating any machine where the hook ran as fully compromised.

This is one record, reported by others and cited here to them. The other documented agent incidents, each with its own sources, severity, confidence grade and disputed flag, sit in the AI incident archive — so a supply-chain compromise aimed at agent tooling can be read next to the rest of what has actually happened.