CVE
Not assigned
CWE
CWE-494, CWE-506, CWE-522, CWE-829
Affected Surface
- `tensorlake@0.5.144`, the malicious npm release published on 8 October 2026 and later removed from the public version list
- Developer workstations, build hosts, and local test environments that installed `tensorlake@0.5.144` outside CI, or that cloned the compromised `tensorlakeai/tensorlake` repository state from commits `e90c47b` through `6386121` and ran `npm install` in `typescript/`
- GitHub, npm, cloud, Kubernetes, Vault, SSH, browser, and AI-tool credentials reachable from those hosts, plus repositories the stolen GitHub token could modify through `.claude/settings.json` and `.vscode/tasks.json` persistence
The strongest new package-registry story in the last three days is the compromise of tensorlake@0.5.144, published on 8 October and removed from the public npm version list soon after. This one matters because the malicious code was not smuggled in through a lookalike package name or a fake maintainer scope. It was committed directly to tensorlakeai/tensorlake on main, published from the project’s own release flow, and signed with provenance. The attestation stayed green because the source tree itself had already been weaponized.
That makes this a useful case study for AppSec teams that increasingly trust provenance, OIDC publishing, and repo-level release automation. Those controls still matter. They just do not help once an attacker can push hostile source and trigger the real publish workflow.
Confirmed affected package and timeline
The confirmed malicious package is tensorlake@0.5.144.
Public reporting and the upstream incident records line up on the main sequence:
| Time | What happened |
|---|---|
| 6 Oct 2026 | cli-v0.5.143 was the last clean public release tag |
| 7 Oct 2026 | Attackers pushed a chain of direct commits to main, starting at e90c47b and ending at 6386121 |
| 8 Oct 2026 01:12 UTC | The release workflow published tensorlake@0.5.144 to npm |
| 8 Oct 2026 | Security researchers flagged the package and opened Tensorlake issue #1014 |
| 8 Oct 2026 | Tensorlake merged PR #1016, reverted the hostile commits, hardened the workflow, and bumped the next clean line to 0.5.145 |
One detail is easy to miss if you only look at npm today: npm view tensorlake versions --json no longer returns 0.5.144. That does not mean the version was never live. The issue, PR, GitHub advisory record, and independent writeups all document the malicious publish, and the maintainers’ response PR explicitly says npm removed tensorlake@0.5.144.
The same PR also notes that six tensorlake-native-* 0.5.144 packages still needed to be unpublished. The public code-level malicious evidence in current sources centers on the main tensorlake package, so that is the package consumers should scope first.
The package manifest changed the trust boundary in one line
The most important source-level change is boring on purpose. In the malicious tree, typescript/package.json gained a new lifecycle hook:
"scripts": {
"build:sdk": "tsup",
"build:runtime": "node ./scripts/build-function-executor-capsule.mjs && node ./scripts/build-typescript-function-runner-capsule.mjs && node ./scripts/check-package-compatibility.mjs",
"build:native": "node ./scripts/build-native.mjs",
"build": "npm run build:sdk && npm run build:native && npm run build:runtime",
"check:proto": "node ./scripts/check-function-executor-proto.mjs",
"typecheck": "tsc --noEmit",
"test": "vitest run --exclude tests/integration.test.ts",
"prepack": "npm run build",
"preinstall": "node lib/setup.mjs"
}
That one line is enough to move the execution boundary from “after the package is installed and intentionally imported” to “while the package manager is still resolving dependencies.” The rest of the package can remain plausible. The install step is already compromised.
The attackers paired that hook with two files under typescript/lib/:
lib/setup.mjslib/Math_Symbol.js
That matches the file layout researchers previously saw in the August keyv / cacheable Shai-Hulud wave. Tensorlake matters because the same operator pattern landed in a package used to build and run AI-agent infrastructure, not just general JavaScript utilities.
setup.mjs tried not to burn obvious CI, then fetched Bun and launched stage 2
The loader shipped as heavily obfuscated JavaScript, but several control points are still easy to recover from the file itself.
First, the install hook tried to stay off some of the noisiest CI surfaces:
if (process.env.CI === "true" || process.env.CI === "1") return true
if (process.env.GITHUB_ACTIONS === "true") return true
if (process.env.GITLAB_CI === "true") return true
if (process.env.RUNNER_ENVIRONMENT === "github-hosted") return true
That does not make CI safe. It only means the operator did not want the simplest install path to detonate everywhere immediately. StepSecurity, Aikido, and Socket all describe developer workstations and local build hosts as the primary target surface, which lines up with the code.
Second, the loader assembled a Bun download URL and used the downloaded runtime to execute the second stage:
const V = "1.3.13"
const url = ".../bun-v" + V + "/" + target + ".zip"
await dl(url, archivePath)
xb(archivePath, target + "/" + bunBinaryName, tmpDir)
runStage2(payloadPath)
This matters for two reasons.
- The release did not rely on the victim already having Bun installed.
- The execution path is not just “run more Node.js.” The package fetched another runtime and handed control to it.
That is the same general loader shape independent researchers described from package analysis: install-time stub, runtime drop, obfuscated second stage, then broader secret theft and propagation logic.
The blast radius is wider than one npm tarball
The directly affected package is still tensorlake@0.5.144, but the follow-on risk extends beyond that tarball.
Public reporting agrees on three secondary surfaces:
- Host compromise after install. Researchers describe theft of GitHub, npm, AWS, Kubernetes, Vault, SSH, browser, and AI-tool credentials.
- Repository persistence. StepSecurity reports the malware wrote
.claude/settings.jsonand.vscode/tasks.jsoninto reachable repositories so the payload could run again when a project was opened. - Token hostage logic. StepSecurity and GMO Flatt both describe a
gh-token-monitorpersistence path that watched a stolen GitHub token and reacted destructively when the token stopped working.
The repo-side markers are worth calling out because they change who is at risk. A workstation could miss the npm install path and still get hit later if someone opened a repository where the stolen token had already planted a malicious editor or agent configuration.
The named public indicators are also unusually specific:
- package:
tensorlake@0.5.144 - files:
lib/setup.mjs,lib/Math_Symbol.js - host persistence:
~/.config/gh-token-monitor/ - network:
iseekaigogo.com - repo description used during exfiltration:
Shai-Hulud: Here We Go Again - fake commit author observed in follow-on repo changes:
claude@users.noreply.github.com
Signed provenance stayed valid because the source was already hostile
This incident is a clean reminder that provenance answers a narrower question than many teams want it to answer.
Provenance can prove which workflow published a package.
It cannot prove the source tree fed into that workflow was still trustworthy.
StepSecurity says the malicious release was built from the real main branch and published with npm provenance. Tensorlake’s own incident PR says the attacker committed payload files directly to main through the GitHub web UI, then manually dispatched publish_npm.yaml, which built and signed the result.
That sequence explains why a provenance check would still pass:
malicious commits land on main
-> maintainer workflow publishes from main
-> Sigstore / npm provenance attests the real workflow identity
-> consumer sees a valid attestation for a malicious source state
The maintainers’ hardening diff is worth reading because it turns that lesson into code. The repaired workflow now forces dependency installs through --ignore-scripts:
- name: Check for forbidden lifecycle scripts
run: node ./scripts/check-no-install-scripts.mjs
- name: Install dependencies
run: npm ci --ignore-scripts
They also added a repo-local tripwire script with a comment that names the failure mode directly:
// Supply-chain tripwire. tensorlake@0.5.144 was compromised by adding a
// `preinstall` hook to package.json that ran an obfuscated loader from lib/.
That script fails CI if any published manifest declares preinstall, install, postinstall, prepare, or related lifecycle hooks, or if typescript/lib/ contains anything beyond the expected runtime shims. It is a narrow control, but it is exactly the kind of narrow control that would have stopped this release shape.
What to check in a real codebase
Start with dependency inventory and lockfiles:
npm ls tensorlake
pnpm why tensorlake
yarn why tensorlake
rg -n '"tensorlake"|tensorlake@0\\.5\\.144|0\\.5\\.144' \
package.json package-lock.json pnpm-lock.yaml yarn.lock npm-shrinkwrap.json
Then hunt the host and any cloned repositories for the named persistence and exfiltration markers:
rg -n 'gh-token-monitor|iseekaigogo\\.com|Shai-Hulud: Here We Go Again|claude@users\\.noreply\\.github\\.com|\\.claude/settings\\.json|\\.vscode/tasks\\.json' \
"$HOME" . .github 2>/dev/null
If your team cloned the source repository instead of consuming the npm package, check whether anyone built or installed from the compromised commit range:
git log --oneline e90c47b^..6386121
rg -n 'preinstall|setup\\.mjs|Math_Symbol\\.js' typescript/package.json typescript/lib
Because the version has already been pulled from the public npm version list, historical logs and caches matter more than a live registry query. Check developer shell history, CI logs, registry mirrors, and any internal artifact cache that could still hold the tarball.
Response guidance
If tensorlake@0.5.144 ever ran on a machine, treat that machine as compromised.
The order of operations matters here.
- Back up the host first if you still need forensic or business data from it.
- Remove
gh-token-monitorpersistence before revoking GitHub tokens. - Rotate GitHub, npm, cloud, Kubernetes, Vault, SSH, browser, and AI-tool credentials from a different clean machine.
- Inspect reachable repositories for unexpected
.claude/settings.json,.vscode/tasks.json, new public repos with theShai-Huluddescription, or commits attributed toclaude@users.noreply.github.com. - Rebuild the affected workstation or runner from a known-good image if there is any doubt about cleanup.
For dependency remediation, pin away from the malicious version immediately. Public sources consistently name 0.5.143 as the last known safe npm release. Tensorlake’s incident PR bumped the next clean line to 0.5.145, but the same PR also says human follow-up was still required before the cleaned package set was fully republished and the remaining 0.5.144 native packages were removed.
The practical lesson is simple. Trusted publishing and provenance are still worth having, but they do not replace controls that keep hostile source out of the publish path in the first place. In this incident, the malicious diff was small, the workflow identity was real, and the package install boundary was enough to turn a normal SDK update into full host compromise.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.
References
- StepSecurity: Tensorlake npm Package Compromised
- Aikido: tensorlake NPM package compromised with Shai Hulud worm
- Socket: TensorLake npm SDK Compromised in ChainDrop Shai-Hulud Credential-Stealing Attack
- GMO Flatt: Software Supply Chain Attack on tensorlake
- GitHub issue #1014: npm package tensorlake@0.5.144 contains malicious preinstall payload
- GitHub PR #1016: Revert the 0.5.144 supply-chain compromise and harden the npm release path
- GitHub release: CLI v0.5.143
- GitHub Advisory: GHSA-rqxj-g25x-4v9v
- npm package: tensorlake