A developer investigates suspicious code in a software repository using a magnifying glass and security dashboard.
A malicious npm campaign that initially looked like an operator’s testing exercise had already armed 42 real GitHub repositories across 11 legitimate owners, according to JFrog Security Research. The attackers planted developer-tool execution hooks alongside unusually large JavaScript wrappers—roughly 12.9–13.7 MB—that concealed a package-publishing payload from the search and content-retrieval methods investigators initially used. Bigger, in this case, meant easier to overlook.

The findings change the picture of the testmetest campaign, but they need careful interpretation: 42 armed repositories does not establish 42 infected developer computers, nor does it prove every malicious publishing mechanism ran successfully. JFrog’s evidence establishes repository compromise, recovered payloads and related exploitation attempts—not successful execution at every possible downstream stage.

A developer investigates suspicious code in a software repository using a magnifying glass and security dashboard. From apparent tests to compromised repositories​

The Register reported that SafeDep initially identified the npm-side campaign and, as of October 22, had not confirmed compromised third-party packages or victims. JFrog’s October 26 investigation examined the associated GitHub infrastructure and found repositories belonging to established developers and a legitimate organization already prepared to execute malicious code. JFrog characterizes its investigation as complementary to SafeDep’s work, rather than simply a later count of the same evidence.

The timeline also reaches further back than October. JFrog identified genuine smoke-test activity in July 2026 and scripted repository-arming batches on September 28. Some later malicious commits copied historical author dates and legitimate-looking messages, making the visible commit history an unreliable guide to when the changes actually arrived.

Attribution remains narrower than the malware’s family resemblance might suggest. Although the researchers found shared tooling and a Shai-Hulud-descendant payload, they explicitly did not establish that the original Miasma operators were responsible. “Testmetest campaign” is the more defensible label.

How a large file became a detection blind spot​

JFrog’s initial code-search sweep found execution hooks in 26 repositories. Associated .github/init.js files appeared empty through the content paths investigators were using. Direct repository-tree enumeration and Git blob retrieval revealed otherwise, eventually expanding the confirmed set to 42 armed repositories.

The researchers recovered ten distinct wrapper blobs. Despite different encodings and file sizes, all decoded to the same underlying publisher payload. The size variations reflected how character codes were represented, not ten different sets of malicious capabilities.

There is an important qualification to the claim that these files were too large for GitHub’s contents API. GitHub’s official documentation supports retrieving files between 1 MB and 100 MB using the appropriate media type. With the object media type, however, the API returns an empty content field and an encoding value of "none"; retrieving the actual bytes requires the raw media type. Files above 100 MB are unsupported by that endpoint.

That distinction matters operationally. A security integration must not interpret an empty API content field as proof that a file is empty. Investigators should check reported file size and encoding, then retrieve the bytes through a supported route. JFrog used repository trees and blobs to resolve precisely this kind of visibility gap.

The lesson is not that large malware becomes invisible to every scanner. It is that a clean search result—or an empty response field—may reveal a collection limitation rather than a clean repository.

Developer configuration became the execution path​

The compromised repositories contained hooks for Claude, Gemini, Cursor and Visual Studio Code. JFrog identified configuration files including:

  • .claude/settings.json
  • .gemini/settings.json
  • .cursor/mcp.json
  • .cursor/rules/setup.mdc
  • .vscode/tasks.json

Those hooks pointed toward the hidden payload. Some repositories also contained changes to package scripts, composer.json or Makefiles, creating additional execution opportunities during ordinary development activity.

This does not establish a vulnerability in every named application. It shows attackers abusing executable project configuration and the trust developers place in repositories. For VS Code users, Microsoft documents Restricted Mode as a boundary intended to prevent automatic workspace-code execution, including restrictions on tasks, debugging and extensions.

Before inspecting an unfamiliar checkout, keep it untrusted. On Windows, open the Command Palette with Ctrl+Shift+P, select Workspaces: Manage Workspace Trust, and verify its trust state. Microsoft cautions that Workspace Trust cannot stop a malicious extension that ignores Restricted Mode, so this is a protective layer—not a malware-removal tool or an absolute sandbox.

Windows machines had a separate native-loader risk​

The campaign also introduced compiled Node add-ons rather than relying solely on readable JavaScript loaders. JFrog identified a Windows component, addon_win.node, and an adjacent .napi_cache file holding encoded JavaScript. The loader decoded that file and launched it in a detached Node process.

On Windows, the reported drop location was %TEMP%\.nc<PID>.js. JFrog found no loader code that deleted the dropped file after launch, making it a potentially useful investigation artifact. Its presence warrants examination; its absence does not establish that a machine was never affected.

The Windows loader also checked for debuggers and analysis tools, including Process Monitor, Process Explorer and Wireshark, and could stop without executing its next stage. That behavior limits what a quiet sandbox run can tell investigators. Separately, the repository publisher was designed for GitHub Actions: a developer-host loader avoiding analysis and a CI publisher seeking execution are distinct components, not contradictory descriptions of one program.

What affected teams should prioritize​

JFrog recommends treating installation of a full-worm package version as a host compromise, rebuilding affected systems from trusted sources and rotating exposed credentials. It also flags a dangerous sequencing issue: the worm includes a token-revocation wiper, and its guidance calls for removing identified persistence before revoking the stolen GitHub token. The named LaunchAgent and systemd persistence mechanisms are platform-specific and should not be mistaken for Windows removal instructions.

A response should cover more than the package cache:

  • Preserve evidence: retain commit, workflow-run and artifact metadata before cleanup.
  • Audit accessible repositories: inspect execution hooks and retrieve suspicious blobs directly rather than relying exclusively on code search.
  • Review publishing paths: investigate unauthorized chore/ci-* and deps/upd-* branches and malicious workflows.
  • Check credential exposure: JFrog found fake CodeQL workflows that exported environment data into artifacts named scan-output.
  • Scope package versions precisely: campaign-associated names included benign cover releases; association alone does not make every version malicious.

Finally, JFrog linked the same compromised-owner cluster to shellpanel workflows attempting exploitation of cPanel-related systems through CVE-2026-41940. It did not confirm successful downstream cPanel compromises. That boundary belongs beside the headline finding: the evidence supports 42 armed repositories and attempted broader exploitation, not an unlimited victim count.

For development teams, the practical takeaway is straightforward: review the files that decide what runs, not just the application code that explains what a project does. And when a security tool returns nothing, make sure “nothing” means no evidence—not that the evidence exceeded its reading limit.

 

References

  1. Worm armed 42 GitHub repos by hiding a payload too big to fail The Register 2026-10-26T22:29:01+00:00
  2. Beyond testmetest: Native Addon Camouflage and 42 Armed GitHub Repositories Reveal a Hidden npm Publisher - JFrog Security Research research.jfrog.com
  3. REST API endpoints for repository contents - GitHub Docs docs.github.com