---
title: "Mini Shai-Hulud in Tensorlake: A Token Monitor That Can Wipe Your Home Directory"
date: "2026-10-20T19:03:00+00:00"
url: "https://checkmarx.com/zero-post/mini-shai-hulud-in-tensorlake-a-token-monitor-that-can-wipe-your-home-directory/"
---

# Mini Shai-Hulud in Tensorlake: A Token Monitor That Can Wipe Your Home Directory

On October 7, 2026, at about 01:20 UTC, an attacker used a compromised repository administrator account to push malicious code directly to the main branch of `tensorlakeai/tensorlake`. The 13 commits, `e90c47b` through `6386121`, added a preinstall hook, a loader (`lib/setup.mjs`), and a payload (`lib/Math_Symbol.js`) that steals credentials and spreads itself.

On October 8, the attacker manually triggered the project’s release workflow, which built, signed, and published `tensorlake@0.5.144` at 01:12:07 UTC. npm removed the version about three hours later, and the maintainers reverted the code in [pull request (PR) #1016](https://github.com/tensorlakeai/tensorlake/pull/1016) and released a clean 0.5.145. StepSecurity, Socket, SafeDep, Aikido, and Endor Labs published analyses of this compromise starting October 8, and we link their work where it overlaps ours.

This activity belongs to the campaign known as Mini Shai-Hulud. The payload tags itself with `WORMTAG = "tensrlake"`, a [package-specific marker](https://aikido.dev/blog/tensorlake-npm-package-compromised) that, as Aikido noted, suggests a newly seeded compromise rather than reinfection from an earlier wave. According to Endor Labs, Tensorlake gets [about 12,000 weekly downloads](https://endorlabs.com/learn/tensorlake-npm-package-compromised-by-shai-hulud-in-latest-software-supply-chain-attack) and provides an SDK for AI agent sandboxes, environments where cloud, GitHub, npm, and AI tool credentials are often within reach. The payload runs at install time, whether or not the SDK is ever used.

One behavior changes how responders need to act. Under a specific fallback path, the payload installs a token monitor that checks GitHub every 60 seconds for up to 24 hours. If GitHub returns an HTTP status from 400 to 409, including the 401 that follows token revocation, the monitor deletes the user’s home directory. Remove the monitor before you revoke any token.

## TL;DR

- **Affected:** `tensorlake@0.5.144` on npm, plus [six native platform packages](https://endorlabs.com/learn/tensorlake-npm-package-compromised-by-shai-hulud-in-latest-software-supply-chain-attack)published at the same version. We found no payload in the native packages, as Endor Labs also reports, but treat them as part of the affected release. We haven’t identified any malicious Python Package Index (PyPI) or Cargo release.
- **Exposure:** npm recorded [15,664 tensorlake downloads](https://api.npmjs.org/downloads/range/2026-10-01:2026-10-08/tensorlake)across 100 versions from October 1 to 8. Because npm removed 0.5.144, its own count isn’t available, so these figures can’t show how many times the malicious version was installed.
- **Behavior:** The payload goes after GitHub, npm, cloud, Kubernetes, and Vault secrets, along with browser passwords and wallet extension data. It can run JavaScript sent by its operator, plant a workflow that dumps GitHub Actions secrets, republish npm packages with Sigstore provenance attached, and add `.claude` and `.vscode` files to repositories. What it actually obtains depends on what’s present on the host.
- **Response:** Isolate affected hosts and remove the token monitor before revoking any GitHub token. Then rotate everything the host could reach and rebuild it from a trusted image.

## What we confirmed

We analyzed the `tensorlake@0.5.144` package files, the `tensorlakeai/tensorlake` commit history, and npm registry metadata.

The malicious code entered main through 13 commits from one named maintainer, starting October 7. Nine of them show GitHub’s Verified badge. Commit `c79bc47`, titled “Add preinstall script to package.json,” adds `"preinstall": "node lib/setup.mjs"`, and the last clean commit is `52f19c8`. The attacker also bumped the version in `pyproject.toml` and `Cargo.toml` to 0.5.144 but changed nothing else in them.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image01.webp)

Figure 1: Malicious commits on tensorlakeai/tensorlake. GitHub groups commits by the viewer’s local time zone, so the last three appear under October 8. In UTC, all 13 landed on October 7.

[The published package adds two files](https://socket.dev/blog/tensorlake-compromise). `lib/setup.mjs` is the loader. It exits if it detects common CI markers, reuses Bun if it’s already installed, and otherwise downloads Bun 1.3.13 from Bun’s official GitHub release into a temporary folder, runs the payload, and deletes the folder. The loader contacts no attacker infrastructure itself; its only network call is that Bun download. `lib/Math_Symbol.js` is the obfuscated payload. It sets `globalThis.WORMTAG = 'tensrlake'`and decrypts its embedded strings, scripts, and configuration at runtime. The decoded payload carries two RSA public keys used by its cryptographic routines, including commit signature verification.

Version 0.5.144 declares no new npm dependencies. Bun arrives at install time, outside the declared dependency graph, so tools that inspect only declared dependencies won’t see it. The previous release, 0.5.143, has no install-time lifecycle script.

## How the malware executes

When `tensorlake@0.5.144`installs with lifecycle scripts enabled, its preinstall hook runs `lib/setup.mjs`, which launches the payload.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image02.webp)

Figure 2: The 0.5.144 package.json adds the preinstall hook.

The payload picks one of two profiles, `chaindrop` or `tanstack`, from the `WORM_PROFILE` environment variable, and defaults to `chaindrop`. Each profile uses different payload filenames and execution paths, and in `chaindrop` the malware can stay active in a continuing command loop. Everything described below is something the code attempts. What it actually achieves depends on what exists on the host, the permissions of the credentials it finds, and whether the relevant services are reachable.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image03.webp)

Figure 3: WORM\_PROFILE selects the chaindrop or tanstack profile and the matching payload filenames.

The collectors search local files, environment variables, process data, and cloud APIs. They target AWS credentials, AWS Secrets Manager, AWS Systems Manager Parameter Store, Kubernetes service account tokens and namespace secrets, and HashiCorp Vault tokens and key-value data, along with files belonging to a long list of developer tools and infrastructure services.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image04.webp)

Figure 4: The malware lists AWS Secrets Manager secrets and retrieves each value, binary or string.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image05.webp)

Figure 5: The malware reads the mounted Kubernetes service account token and namespace, then searches the local kubeconfig for token, ID token, or access token values.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image06.webp)

Figure 6: Part of the Linux target list, covering cloud, CI, AI tool, SSH, and wallet credential paths, plus Chrome and Chromium wallet extension storage. The full list is considerably longer.

The malware validates any GitHub token it finds against the GitHub user API and reads the returned OAuth scopes to see whether the token has repository or workflow access. That result decides which GitHub operations it attempts next.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image07.webp)

Figure 7: The token check calls the GitHub user endpoint and reads OAuth scopes to determine repository and workflow access.

With workflow access, the malware goes beyond listing secret names. It creates the branch `dependabot/github_actions/format/setup-formatter`and commits `.github/workflows/run-copilot.yml`as `github-copilot[bot]` with the message `Add Copilot workflow`. The workflow writes `toJSON(secrets)` to `format-results.txt` and uploads it as an artifact named `format-results`. The malware then polls for the run, downloads the artifact, and tries to delete the run and the branch.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image08.webp)

Figure 8: The routine creates the branch and commits the workflow file as github-copilot\[bot\].

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image09.webp)

Figure 9: The f9 routine decrypts the embedded workflow body, v595, using AES-256-GCM.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image10.webp)

Figure 10: The decoded workflow serializes every repository secret and uploads it as the format-results artifact.

For command and control (Command and Control (C2)), the malware opens an HTTPS channel and evaluates any nonempty JavaScript returned in the `code` field of the JSON response. It can also resolve a destination hostname through an Ethereum contract queried over public JSON-RPC services. As a fallback, it creates a public repository in the victim’s GitHub account with the description `Shai-Hulud: Here We Go Again` and writes result envelopes containing host and user details alongside an encrypted payload.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image11.webp)

Figure 11: applyRemoteCode parses the C2 response and evaluates the JavaScript in its code field.

When token inclusion is enabled, the malware Base64 encodes the GitHub token twice, adds it to the outgoing envelope, and installs a monitor. The monitor checks the GitHub user endpoint every 60 seconds for up to 24 hours. If the response status is anywhere from 400 to 409, it runs a stored handler. On Windows, that handler recursively deletes the directory in `USERPROFILE`.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image12.webp)

Figure 12: The malware installs the monitor and encodes the token twice. The Windows handler runs only when the monitor receives a status from 400 to 409.

On Linux and macOS, the malware pipes an embedded Bash script into a process started with `Bun.spawn`, passing the token and handler as arguments. The script installs the monitor in `~/.local/bin` and stores the token and handler in `~/.config/gh-token-monitor`. On macOS it registers a LaunchAgent, and on Linux it enables a systemd user service and tries to enable lingering. The handler is `rm -rf ~/`, which deletes the user’s home directory.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image13.webp)

Figure 13: The Bash installer sets the monitor paths, registers a LaunchAgent or systemd user service, and runs the handler on any status from 400 to 409.

The string `IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner`isn’t the trigger. The malware uses it as the prefix of dead-drop commit messages that carry a stolen token, and a separate routine searches GitHub for commits with that prefix, decodes the suffix, and keeps any recovered token that has repository scope. The real trigger is the HTTP status. Because revocation produces a 401, revoking a token while the monitor runs can wipe the host.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image14.webp)

Figure 14: The recovery routine searches GitHub commits for the revocation phrase, decodes the token in each matching message, and keeps it if it has repository scope.

The malware can also [republish npm packages](https://checkmarx.com/zero-post/inside-shai-huluds-maw-how-the-npm-worm-exploits-and-propagates/). It downloads a package’s metadata and tarball, adds a preinstall script and its payload files, increments the patch version, rebuilds the archive, and attempts publication. A separate path adds `@opensearch/setup` as an optional dependency to another package.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image15.webp)

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image16.webp)

Figure 15: The two updateTarball routines. The first adds the preinstall hook and payload files and increments the patch version. The second adds @opensearch/setup as an optional dependency.

Before publishing, the malware tries to attach Sigstore provenance. It hashes the rebuilt tarball with SHA-512, builds a statement (in the in-toto format) that claims a GitHub Actions build produced the package, requests an OpenID Connect (OIDC) token from GitHub Actions, gets a signing certificate from Fulcio, signs the statement as a Dead Simple Signing Envelope (DSSE), and records it in Rekor, Sigstore’s transparency log. If any of these steps fails, it publishes anyway. [Valid provenance on a package doesn’t prove its code is safe, a point StepSecurity and Endor Labs also make](https://stepsecurity.io/blog/tensorlake-npm-compromised-hostage-token-worm).

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image17.webp)

Figure 16: The provenance routine obtains a GitHub Actions OIDC token, requests a certificate from Fulcio, and submits a DSSE entry to Rekor.

Finally, the malware can push VS Code and Claude configuration files to eligible branches of repositories the token can write to: `.vscode/tasks.json`, `.vscode/setup.mjs`, `.claude/settings.json`, `.claude/setup.mjs`, and a copy of the payload in `.claude`. These files contain editor and assistant setup content that invokes further setup scripts, which gives the malware another route to execution in those repositories.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image18.webp)

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image19.webp)

Figure 17: The repository routine prepares the VS Code and Claude files, filters eligible branches, and pushes them in commits attributed to claude.

## Impact and blast radius

The only malicious release is tensorlake 0.5.144. The [six native platform packages](https://endorlabs.com/learn/tensorlake-npm-package-compromised-by-shai-hulud-in-latest-software-supply-chain-attack)published in the same run contain no install script and no payload we’ve found, but you should still avoid them. Versions 0.5.143 and earlier are clean. Version 0.5.145 has no install-time scripts and is now a small compatibility package that forwards to `@tensorlakeai/tensorlake`, where the SDK has moved.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image20.webp)

Figure 18: npm registry publish times. The 0.0.1-security entry at 04:15:44 UTC is consistent with npm removing 0.5.144 at that time.

npm recorded [15,664 tensorlake downloads](https://api.npmjs.org/downloads/range/2026-10-01:2026-10-08/tensorlake)from October 1 to 8. The zeros for October 6 and October 8 likely reflect gaps in npm’s reporting rather than an absence of downloads. Most downloads in the period were for older versions, led by 0.5.97 with 10,820. npm reports no figure for 0.5.144 because npm removed the version, so its data can’t show how many times the malicious version was installed.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image21.webp)

Figure 19: Daily downloads for tensorlake, October 1 to 8, 2026.

You’re potentially affected if any install resolved 0.5.144 between 01:12:07 UTC on October 8 and its removal at about 04:15 UTC. That includes installs without a lockfile or with caret or tilde ranges, automated dependency updates, and fresh container or image builds. Anyone who pulled main between the malicious commits and the revert, then ran npm install in `typescript/`, also ran the hook. pnpm 10 and later blocks dependency lifecycle scripts by default, so pnpm users were exposed only if they allowed scripts for this package. Because the loader exits when common CI variables are set, hosted and self-hosted GitHub Actions and GitLab runners skip the payload, which leaves developer workstations most exposed. Build systems that don’t set those variables, and container builds that don’t pass them through, would still run it.

On an infected host, treat every secret the installing process could reach as exposed. That includes npm publish rights and GitHub access, which enable further supply chain compromise, as well as cloud credentials, Vault and Kubernetes secrets, SSH and TLS keys, AI tool tokens and Model Context Protocol (Model Context Protocol (MCP)) configuration, browser sessions and saved passwords, cryptocurrency wallets, and messaging data. A planted workflow can also expose the Actions secrets of any repository the stolen token can write to. If the worm used its GitHub dead drop, it publishes the victim’s token (Base64 encoded) in a public commit message, and the token monitor may be running.

## Remediation

Remove `tensorlake@0.5.144` and the six native packages at 0.5.144 from lockfiles, caches, images, and build artifacts. Upgrade to 0.5.145 or pin to 0.5.143, verified against the registry.

Order matters when rotating credentials because of the [dead man’s switch](https://checkmarx.com/zero-post/shai-huluds-second-coming-npm-malware-attack-evolved/):

- **First,** isolate the host and preserve evidence.
- **Second,** remove the token monitor persistence (paths in the indicators table below). Revoking the GitHub token while the monitor runs can trigger the wipe.
- **Third,** revoke the GitHub token and rotate every credential the host could reach, including npm, cloud, Vault, Kubernetes, SSH, Docker, CI, and AI tool secrets.
- **Finally,** invalidate browser sessions and move any cryptocurrency to new wallets created on a clean device.

Then rebuild the host from a trusted image. Uninstalling the package doesn’t remove persistence or undo credential theft.

Audit GitHub for unexpected commits to `.claude`, `.vscode`, or `.github/workflows`, for fake Copilot workflows, and for new repositories described as `Shai-Hulud: Here We Go Again`. Review npm publish history, Actions runs, and Vault audit logs for the exposure window. Going forward, disable lifecycle scripts in CI by default, prefer short-lived OIDC credentials, require human approval for publishing, and install from committed lockfiles with `npm ci`, with critical SDKs pinned to exact versions. We cover centralized package controls in more depth in “Protecting yourself against malicious open-source packages.”

To check for exposure, run `npm ls tensorlake` and search lockfiles for 0.5.144. Look for `lib/setup.mjs` and `lib/Math_Symbol.js` matching the hashes below. Check for persistence in `~/.config/systemd/user`, `~/.local/bin`, `~/Library/LaunchAgents`, or Windows scheduled tasks. In endpoint detection and response (EDR) telemetry, watch for `node lib/setup.mjs` spawning Bun during npm install, traffic to `iseekaigogo[.]com`, and unexpected Ethereum RPC connections from developer machines.

## Incident timeline

All times UTC.

- **October 7, 01:20:** The first malicious commit, `e90c47b`, adds `lib/setup.mjs` and `lib/Math_Symbol.js` to main.
- **October 7, 01:24:** Commit `c79bc47` adds the preinstall hook.
- **October 7, 01:43 to 23:41:** Another 11 commits update the payload and set all package versions to 0.5.144, ending with `6386121`.
- **October 8, before 01:12:** The attacker manually runs the project’s release workflow.
- **October 8, 01:12:07:** `tensorlake@0.5.144` is published to npm. SafeDep, StepSecurity, and Socket flag it within 11 minutes, and StepSecurity reports it to the maintainers in GitHub issue #1014.
- **October 8, about 04:15:** npm removes 0.5.144.
- **October 8, 04:32:** The maintainers merge [PR #1016](https://github.com/tensorlakeai/tensorlake/pull/1016), which reverts the malicious commits, blocks install scripts in CI, and bumps all packages to 0.5.145. They also block direct pushes to main, require signed commits, and require a second reviewer for every npm publish.
- **October 8, 20:24:05:** tensorlake 0.5.145 is published as the clean release.
- **October 9:** 0.5.145 is the latest version, 0.5.144 is no longer available, and we’ve found no malicious PyPI or Cargo release.

![](https://checkmarx.com/wp-content/uploads/2026/10/cxzero_2026-20-cxzero-tensorlake-campaign_image22.webp)

## Indicators of compromise

[The table lists the indicators most useful for detection](https://safedep.io/tensorlake-npm-compromise-mini-shai-hulud/). The payload also targets 14 browser wallet extensions, including MetaMask, Phantom, Coinbase Wallet, and Rabby.

| **Type** | **Indicator** |
|---|---|
| **npm package** | `tensorlake` 0.5.144, plus six native platform packages at 0.5.144 |
| **Install hook** | `"preinstall": "node lib/setup.mjs"` |
| **SHA256, lib/setup.mjs** | `25a0735d0db7dc40e5d45ce42d9c106067e6a66e184d967cfecfab17c3bcb5ef` |
| **SHA256, lib/Math\_Symbol.js** | `b50a00900399ba99fb6ce1fc151519cb99d44320ef2a631f2237e1aea0ad6fec` |
| **C2 domain** | `iseekaigogo[.]com` |
| **Ethereum resolver contract** | `0xb614155Fd88114d40549b259457Bcf921Df091B9` |
| **Contract wallet** | `0x779f83aE56309682beDb04816c19d358c4B21040` |
| **Linux persistence** | `~/.local/bin/gh-token-monitor.sh`, `~/.config/gh-token-monitor/`, `~/.config/systemd/user/gh-token-monitor.service` |
| **macOS persistence** | `~/Library/LaunchAgents/com.user.gh-token-monitor.plist` |
| **Windows persistence** | Scheduled task `gh-token-monitor` running `monitor.ps1`, `%LOCALAPPDATA%\gh-token-monitor` |
| **Repository files** | `.claude/settings.json`, `.claude/setup.mjs`, `.claude/math_init.js`, `.vscode/tasks.json`, `.vscode/setup.mjs` |
| **Malicious workflow** | `.github/workflows/run-copilot.yml`on branch `dependabot/github_actions/format/setup-formatter`, committed as `github-copilot[bot]` with the message `Add Copilot workflow` |
| **Strings** | `WORMTAG = "tensrlake"`, `IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner`, `Shai-Hulud: Here We Go Again` |
| **Dead-drop repositories** | Public repositories named from two Dune terms and a number, such as `sardaukar`, `mentat`, or `fremen` combined with `sandworm`, `melange`, or `stillsuit` |
| **Malicious commits** | `e90c47b` through `6386121` in `tensorlakeai/tensorlake` (last clean commit: `52f19c8`) |

[linkedin-app Share on LinkedIn](https://linkedin.com/sharing/share-offsite/?url={url}) [Share on Bluesky](https://bsky.app/intent/compose?text=I%20just%20read%20%22{title}%22%20from%20Checkmarx%20Zero%20{url})

Follow [Checkmarx Zero](https://checkmarx.com/zero/): [linkedin-app](https://linkedin.com/showcase/checkmarx-zero) [](https://bsky.app/profile/checkmarxzero.bsky.social) [](https://x.com/CheckmarxZero)
