Poisoned Pipeline

Poisoned Pipeline / Incidents / AS302

AS302 · 2026-10

tensorlake 0.5.144 steals tokens, then wipes the home directory if they are revoked

npmGit / GitHubGitHub ActionsDeveloper workstationAI coding agentsVS Code / IDELinux distro / packagesWindows software

On 8 October 2026 StepSecurity reported tensorlake@0.5.144 on npm,
published 01:12 UTC, with a preinstall of node lib/setup.mjs. At that
collection the registry still listed 0.5.144 as latest. Version 0.5.143
was published 6 October and is the pin StepSecurity recommends. On
9 October the registry version endpoint for 0.5.144 returned 404.
latest was 0.5.145, published 20:24 UTC on 8 October.

The payload files were committed to main of tensorlakeai/tensorlake.
StepSecurity names the range e90c47b to 6386121 and says the first
commit landed at 01:20 UTC on 7 October, with seven more over the
following hours, none through a pull request. GitHub shows 13 commits
in that range, all authored as Shanshan Wang (login cooleel). The first,
"Add files via upload", was committed through the web UI (committer
web-flow). The last is 23:41 UTC the same day. Neither the blog nor
the commits establish that the named person was the operator.

At 00:08 UTC on 8 October, cooleel started a workflow_dispatch of
"Release - TypeScript SDK (npm)" on 6386121 (Actions run 37706134202).
It succeeded. npm recorded the 0.5.144 publish at 01:12 UTC, matching
StepSecurity. The package carries a provenance attestation because it
was built from that repo's main. The attestation names the build. It
does not say the commit was safe.

setup.mjs skips CI, so developer machines are the target. It downloads
the Bun runtime and runs an obfuscated 856 KB file, which StepSecurity
decoded without executing. The payload collects GitHub and npm tokens,
cloud keys, Kubernetes and Vault secrets, SSH keys, saved browser
logins, and config for Claude, Cursor, and Windsurf. It encrypts that
data and sends it to a public GitHub repo it creates in the victim's
account, or to iseekaigogo.com. The repo description the payload sets
is attacker branding. It is not, by itself, evidence of which wave
this build belongs to.

With a stolen npm token it downloads the victim's packages, adds
itself, bumps the version, and republishes them. With a stolen GitHub
token it commits .claude/settings.json (a SessionStart hook) and
.vscode/tasks.json (a folderOpen task) into repos it can write, as
claude@users.noreply.github.com, message "chore: update dependencies".

If it has a GitHub token it also installs gh-token-monitor. For up to
24 hours the service checks that token against the GitHub API every
60 seconds. If GitHub rejects it, the service runs rm -rf ~/ on Linux
or macOS, or a PowerShell delete of the user profile on Windows.
Revoking the token is what starts the wipe. StepSecurity told
maintainers in tensorlakeai/tensorlake issue 1014. No OSV or GHSA
record existed at that collection. MAL-2026-17650 was published at
02:54 UTC the same day and covers 0.5.144 only. No report names a
downstream victim.

SafeDep, Aikido, and Socket wrote up the same publish on 8 October.
Maintainers merged pull request 1016 at 04:32 UTC that day to revert
the commits on main. SafeDep reads the payload as a Mini Shai-Hulud
build: cipher salt svksjrhjkcejg, which it also reports for the keyv
wave, plus Ethereum contract
0xb614155Fd88114d40549b259457Bcf921Df091B9. Aikido records a
package-specific worm tag and says the PyPI and Cargo packages were
not published. That family read is not a reason to merge this publish
into the earlier keyv story. The package, the account, and the date
are this incident.

Mapped techniques

supply-chain Poisoned main and the release workflow

T0111 · Resource Development · Malicious code contribution to an open-source repository

Thirteen commits on main, e90c47b through 6386121, authored as
Shanshan Wang (cooleel). The first was a web-UI upload. No pull
request. Compromise of that person is not established.

T0121 · Resource Development · Compromised legitimate artifact

tensorlake is the project's own npm package. 0.5.144 replaced
0.5.143 as latest. The files on npm match main.

T0166 · Execution · Trigger pipeline execution

workflow_dispatch of the TypeScript SDK npm release by cooleel,
Actions run 37706134202, started 00:08 UTC on 8 October on
commit 6386121 and succeeded.

T0109 · Resource Development · Publish malicious artifact

tensorlake@0.5.144 published 01:12 UTC on 8 October and was
still npm latest at collection. preinstall runs node lib/setup.mjs.

post-supply-chain Install on a developer machine

T0151 · Execution · Installation scripts

The preinstall hook runs setup.mjs. It skips itself on CI.

T0159 · Execution · Malicious artifact execution

setup.mjs downloads Bun and runs an obfuscated 856 KB file.
StepSecurity decoded that file without running it. Bun here is
a downloaded interpreter for the payload, not a compromised
compiler.

T0184 · Credential Access · Harvesting sensitive information from files

GitHub and npm tokens, cloud keys, Kubernetes and Vault secrets,
SSH keys, saved browser logins, and Claude, Cursor, and Windsurf
config. These are capabilities in the decoded file, not a named
victim theft.

T0210 · Persistence · Poison AI coding-agent config or workspace

Stolen GitHub tokens are used to commit a SessionStart hook in
.claude/settings.json and a folderOpen task in .vscode/tasks.json.

T0169 · Lateral Movement · Push implants across repositories

The same GitHub token commits those files into repos the token
can write. A stolen npm token is used to republish the victim's
own packages with the payload added.

T0195 · Defense Evasion · Spoofed Commits

Spread commits use author claude@users.noreply.github.com and
the message "chore: update dependencies".

T0216 · Impact · Wipe developer profile if a stolen token is revoked

gh-token-monitor polls the stolen GitHub token every 60 seconds
for up to 24 hours. If GitHub rejects it, the service deletes
the home directory (Linux, macOS) or the user profile (Windows).

Framework gaps noted

Sources