bee-js 13.3.0: a hijacked release bot shipped a wallet stealer
Ethersphere's bee-js 13.3.0 on npm and Swarm Desktop 0.56.2 ran a backdoor at install and stole Swarm wallet keys. Who is affected, IOCs and cleanup.
By Yaali. November 10, 2026, 6 min read, Supply chain, Threat intel.
At 00:09:50 UTC on November 9, version 13.3.0 of @ethersphere/bee-js was published to npm. bee-js is the JavaScript SDK for Bee, the node software of the Swarm decentralized storage network, and it was downloaded about 3,500 times in the week before. The release added a postinstall script that fetched code from an attacker's server, sent off Swarm node wallet files and installed a backdoor. At 00:36 UTC an unsigned Swarm Desktop 0.56.2 followed with the same loader in the app itself. npm pulled 13.3.0 at about 01:25 UTC, and Ethersphere replaced the desktop build with 0.56.3, whose release note reads "replace compromised 0.56.2 release".
You are affected if any developer laptop, build server or container image installed bee-js 13.3.0 with install scripts enabled, or if anyone ran Swarm Desktop 0.56.2. That includes projects that never list bee-js directly: @ethersphere/swarm-cli 3.7.0 depends on ^13.2.0 and @ethersphere/bee-dashboard 0.37.1 on ^13.0.0, and both ranges accepted 13.3.0 during that window. The package carried valid npm provenance, so provenance checks did not flag it.

How it works
The bee-js project releases through release-please: a bot opens a pull request that bumps the version, a maintainer merges it, a GitHub release is created, and a GitHub Actions workflow publishes to npm with trusted publishing. Trusted publishing means npm accepts packages from that workflow by its OpenID Connect identity instead of a stored npm token, and attaches a provenance statement saying which repository and commit the package was built from.
On November 8 at 23:59:54 UTC, an unsigned commit titled "chore(upcoming): release 13.3.0" appeared under the bee-worker account, the project's release bot. The real release-please pull request, #1288, was still open and unmerged. Six minutes later the same account published a v13.3.0 GitHub release, which started the normal publish workflow. StepSecurity places the commit on the upcoming branch; SafeDep says it sits on no branch at all, which would explain why branch protection and the test workflow never ran. Both agree the provenance is genuine: the workflow built the source it was handed, and that source was already malicious.
Neither analysis knows how the attacker controlled bee-worker. Both point to a stolen credential or token for the bot account and rule out a stolen npm token as the likely path. Ethersphere has not published a root cause.
The change itself is small. An existing build step, node scripts/generate-version.cjs, moved from the prepare script (which runs only in the package's own repository) to postinstall (which runs on every machine that installs it), and the scripts folder was added to the published files. The script requests /v1/analytics on api.frameworkmetrics[.]xyz with an X-Ethersphere: 1 header and a User-Agent of linux, darwin or win32, then runs whatever comes back through JavaScript's Function constructor with access to require and process. The request has a three-second timeout and errors are swallowed, so a clean install log proves nothing. The code also runs whether or not the app ever imports bee-js.
What the payload does
On every platform, the second stage looks in the Swarm Desktop data folder for config.yaml and data-dir/keys/swarm.key. If both exist it posts them to /v1/swarm/report. The key file is an encrypted Ethereum keystore, but config.yaml can hold its password, so together they give the attacker the node's private key and with it the node's funds and stake.
The third stage installs a backdoor that survives reboots:
- Linux: an x86-64 or ARM64 binary in
~/.solidity/, a.run.pylauncher, and a user systemd service,solidity-langsupport-runner.service, set to restart on failure. - macOS: a binary in
~/.solidity/with a Node.js or Python launcher and a LaunchAgent,com.solidity.langsupport.runner.plist, withRunAtLoadset. - Windows: a PowerShell and VBScript chain staged in
%TEMP%as.stuffed-*files, a temporary Microsoft Defender exclusion for a hidden folder under%LOCALAPPDATA%, and a silent MSI install of a ScreenConnect remote access client.
StepSecurity's static look at the Linux binaries found function names for command execution and interactive shells. Nobody has published how many machines ran the payload, and no theft of funds has been confirmed. The attack domain was registered on November 6 at 15:08 UTC, and by about 00:31 UTC on November 9 it was returning decoy content or 404 errors, so the payload host going quiet tells you nothing about your own exposure.
What to do

- Find exposure. In each project,
npm ls @ethersphere/bee-js --all(orpnpm why @ethersphere/bee-js) shows every resolved copy, including ones pulled in by swarm-cli or bee-dashboard;grep -rn -A2 'bee-js' package-lock.json pnpm-lock.yaml yarn.lockfinds 13.3.0 in repositories you have not installed. Check CI job logs from November 9 onwards, cachednode_modules, and container images built that day. For Swarm Desktop, check which version each user has installed. - Look for the payload. Search DNS and proxy logs for
frameworkmetrics.xyz. On Linux, runsystemctl --user status solidity-langsupport-runnerandls -la ~/.solidity. On macOS, check~/Library/LaunchAgents/for thecom.solidity.langsupport.runnerplist. On Windows, look for.stuffed-files in%TEMP%, a ScreenConnect client service nobody installed on purpose, and Defender exclusion changes (event ID 5007 in the Microsoft-Windows-Windows Defender/Operational log). StepSecurity lists SHA-256 hashes for the Linux binaries:9b73e7cd4e1425e770392549d8df46c706139ef476f4f7b9ac405165dc8d9696(amd64) andb601776817363b96295119f7221338a122d5247a35a77a64b04f286e1bbcc565(arm64). - Treat a hit as a compromised host. GitHub's advisory GHSA-hh7j-5m3j-23pf (MAL-2026-18773 in OSV) says any machine with the package installed should be considered fully compromised. Isolate it, keep a disk image if you need evidence, and rebuild it from a known good image. Deleting the folder and the service is not enough on its own, because the backdoor gave the attacker a shell.
- Move the money first. If a Swarm node's
config.yamlandswarm.keywere on the machine, create a new key on a clean system and move the node's funds and stake to it. - Rotate secrets the install user or CI job could read: npm and GitHub tokens, cloud keys, SSH keys and
.envfiles. Do it from a different machine. - Pin clean versions. Use bee-js 13.2.0, which npm's
latesttag points to again, and Swarm Desktop 0.56.3. Regenerate lockfiles that resolved 13.3.0 and clear CI caches.npm ci --ignore-scriptsblocks this kind of hook, but it can break packages that need install scripts and does nothing for a machine where the code already ran.
The wider lesson
Trusted publishing and provenance answer one question: did this package come from this repository's workflow? They say nothing about whether a reviewed commit started that workflow. Here the bot account that could create releases was the whole trust boundary. If your project publishes on a GitHub release event, check who can create releases and tags, require the publish job to run only from a protected branch, and add a deployment environment with required reviewers so a release cannot reach npm without a human approval.
We review exactly these release paths during red team code review, and platform and cloud engineering covers hardening CI and publishing workflows. If you are unsure whether a build of yours pulled bee-js 13.3.0, open the chat and Yaali, our AI agent, will pass the question to an engineer.
Sources: StepSecurity: @ethersphere/bee-js 13.3.0 compromised, SafeDep: compromised bee-js and Swarm Desktop steal Swarm wallet keys, Ostorlab: malicious @ethersphere/bee-js 13.3.0, OSV MAL-2026-18773, GitHub advisory GHSA-hh7j-5m3j-23pf, Swarm Desktop releases, npm registry: @ethersphere/bee-js, Crypto Briefing.
Read next
- Crédit Mutuel Arkéa heist went through a service provider
- Fake GitHub security tools infect 2,000+ PCs on build
- cPGuard update hijack: check servers, then rotate secrets
Back to the blog, or tell us about your system in the chat. Yaali, our AI agent, answers first and brings in an engineer.