Tech 9 min read

Chainguard npm greyware: 52,000+ blocked past release-age gates and npm v12

IkesanContents

TL;DR

What happened On June 9, 2026, Chainguard announced its scanner reviews 100,000+ npm packages a day and has already blocked 52,000+ as malware or greyware.
Greyware here means packages that do exactly what their README says — read Chrome passwords, collect API keys, wait for remote commands — yet still pass traditional malware detection.

What’s different This is a separate layer from release-age gates (which delay malicious versions right after publish) and npm v12 (which stops install-time scripts).
It blocks packages that work as documented but ship credential-harvesting or persistent-remote-access functionality.

What to check

  1. Dependencies pulled directly from the npm registry
  2. Distribution archives still cached in your internal npm proxy
  3. CLI-ish dependencies that an AI agent added automatically
  4. Small packages that read Chrome, cloud, token, or wallet data

Chainguard’s npm upstream fallback is off by default, so a separate npm fallback in your own registry bypasses this protection.


Chainguard has started blocking npm packages as “greyware” — packages that pass conventional malware scanning.
Its announcement says the scanner reviews 100,000+ packages a day and has already blocked 52,000+ as malware or greyware.

Greyware here is not a package hiding an obfuscated payload (the actual malicious code).
It documents its functionality in the README and the published code, and it behaves exactly that way.
But the functionality itself — reading Chrome passwords and cookies, collecting API keys, waiting for commands from a third-party server — is hard to allow inside a corporate development environment.

The numbers and concrete examples are collected in Chainguard’s The expanding threat landscape.
Rather than “it passed every security check,” it reads more accurately as: published packages that traditional malware detection (which looks for obfuscation and known IoCs, i.e. indicators of compromise) struggles to flag, blocked under Chainguard’s own policy.

Not hidden, so existing detection misses it

Most npm supply-chain incidents covered here so far have been compromises of legitimate packages.
In the Mini Shai-Hulud @antv wave, a hijacked publish credential pushed malicious versions, and the payload ran through preinstall and a Git dependency.
In the Microsoft-related Miasma writeup, the entry point was not npm but the config loading of AI tools and VS Code.

Chainguard’s greyware is neither of those.
It is not typosquatting (fake packages with names made to look like real ones) and it is not a maintainer takeover.
Small packages like “a CLI that handles Chrome data,” “a CLI for an AI assistant,” or “an autonomous pentest framework” — reaching into credentials or remote control — sit openly on npm.

The examples cited in the announcement are chrome-tools, @robinpath/cloud-cli, leobot-cli, noesis-miner, and drogonclaw.
chrome-tools openly exposes functionality to read Chrome passwords, cookies, credit cards, and autofill data.
@robinpath/cloud-cli acts as a persistent backdoor connecting to a third-party server, per Chainguard.

Checking npm metadata on June 18, 2026 JST, all five examples still resolved.
Keep the version ranges Chainguard named in the announcement separate from latest as of 2026-06-18.

PackageRange at Chainguard’s announcementlatest as of 2026-06-18
leobot-cli1.0.0–1.1.61.1.6
@robinpath/cloud-cli0.1.1–0.3.10.3.1
noesis-miner0.1.3–0.1.230.1.23
drogonclaw0.3.5–1.0.21.0.2
chrome-tools1.0.3–1.1.21.1.5

chrome-tools also published 1.1.4 and 1.1.5 on June 17, 2026 UTC, after the announcement.
Don’t stop at the versions an article or a scanner cites as examples — check the current resolution against your actual lockfile and npm metadata.

For this class of package, “not hiding” is what makes detection hard.
If a static scan looks for obfuscation, known IoCs (indicators of compromise), suspicious C2 (command-and-control) strings, or external traffic at install time — when the README and code match, it tends to pass as a “tool that works as specified.”

Some packages survive even a 7-day wait

The earlier piece on the pnpm 11, Yarn 4.10, npm v11.10 release-age gate was about lowering the odds of hitting a malicious version right after publish.
Waves that get pulled within hours to a day — like Mini Shai-Hulud or the axios poisoning — can be excluded from resolution.

But in Chainguard’s examples, the five published packages were still downloadable after npm’s common 7-day cooldown.
A “wait N days after publish” rule excludes poisoned versions that vanish quickly.
For greyware that sits around for a long time, the publish date no longer separates a safe version from an unsafe one.

This is also a different target from the npm v12 allowScripts piece.
npm v12 stops unapproved preinstall, install, postinstall, and the implicit node-gyp rebuild.
But if the greyware runs nothing at install time and only touches Chrome or cloud credentials at use time as a CLI, it operates outside allowScripts.

Lining up the defense layers, the roles split like this.

LayerStopsLeaves
Release-age gatePoisoned versions deleted soon after publishLong-lived greyware, existing lockfiles
npm v12 allowScriptsAutomatic execution at install timeUse at load time / CLI run / via AI agents
Workspace config reviewAuto-execution in .claude/, .vscode/, etc.Normal dependencies pulled from npm
Chainguard’s scannerMalware and greyware, before serving from the repositoryDependencies an org pulls directly from npm, approved exceptions

If you use Chainguard Libraries, you receive scanned packages through an npm-compatible repository endpoint.
Packages pulled from upstream npm via npm upstream fallback (a feature that fetches packages absent from the Chainguard repository from real npm) are also scanned before being added to the Chainguard Repository, per the announcement.

That said, Chainguard’s JavaScript upstream fallback is off by default, and even when configured, packages proxied directly from npm are not Chainguard-rebuilt artifacts.
Chainguard docs position cooldown and malware scanning as supplementary defenses and state that the responsibility to evaluate upstream distributions stays with the consumer.
If you put a separate fallback to npmjs.org alongside Chainguard in an internal Artifact Registry or Artifactory, it doesn’t go through this scan path.

Four signals combined for the verdict

Chainguard lists four signals the scanner uses: maintainer behavior, package contents, publish-time signals, and dynamic execution.

For maintainer behavior, it looks at account transfers, release history, metadata, unusual publish times, and changes to the publishing process or toolchain.
This is close to the signs of a compromised maintainer account or an ownership change.

For package contents, it inspects the published distribution archive itself.
Obfuscated code, embedded C2 domains, modified binaries, new dependencies, and sudden jumps in code or binary size are in scope.

For publish-time signals, it checks the correspondence between the published package and the source code.
Releases with no tag, releases signed with an unknown key, force-pushed tags, commit hashes absent from the event log, and so on show up here.

For dynamic execution, it runs install-time scripts in a network-isolated sandbox and examines external traffic, system file reads, and hidden payload launches.
This overlaps with npm v12’s allowScripts, but Chainguard uses it as a signal before serving to the repository.

For greyware specifically, on top of package contents and publish-time signals, it judges whether the functionality itself belongs in a business application.
Even when the code is public, matches the README, and has sat on npm for a long time, the question remains: do you allow this functionality as a dependency in a business app?

flowchart TD
    A["npm package"] --> B["Publish history and<br/>maintainer behavior"]
    A --> C["Distribution archive contents and<br/>source diff"]
    A --> D["Install-time behavior<br/>in a sandbox"]
    B --> E["Malware / needs review / safe"]
    C --> E
    D --> E
    E --> F["Served or blocked by<br/>Chainguard Libraries"]
    C --> G["Greyware verdict<br/>works as specified but touches credentials"]
    G --> E

Dependencies an AI agent adds slip past review

The reason Chainguard’s announcement touches AI coding is that the path for adding dependencies has changed.
The workflow where a human reads the README, checks permissions and side effects, then runs npm install was already breaking down with large dependency trees.
Add an AI agent, and “find and add a package that fits the requirement” mixes into natural-language work.

You build a small internal automation with AI coding.
The agent suggests an npm package for cloud CLI, browser control, token management, or scraping.
That package reads credentials exactly as the README says.
Here, passing the scan and being dangerous coexist.

The Shai-Hulud and Miasma cases followed on this blog had the attacker looking for an ignition point.
Install-time scripts, Git dependencies, .pth, an AI tool’s SessionStart, a VS Code folderOpen task.
Chainguard’s greyware story is the case where — before any ignition point — choosing the dependency already reaches into credentials or remote control.

Trace from lockfiles to internal mirrors

For an org not using Chainguard Libraries, don’t close this announcement as “Chainguard means safe” — turn it into a review of your own npm path.

Start the review at the lockfile.
Look at internal npm proxies, Artifact Registry, Nexus, Artifactory, CI package caches, and distribution archives left in Docker layers.
Even if something is later removed from public npm, CI pulls from the internal cache if it’s still there.

When checking by dependency name, narrow by functionality type rather than only searching for the package names the announcement cited.
For small packages whose names carry words like Chrome, browser credentials, cookie, token, wallet, cloud, CLI, agent, remote control, pentest, or mining, confirm the reason for the addition and the execution privileges.
For PRs an AI agent authored, the explanation for adding a package is often short.

In a package.json diff, treat additions like these as a separate category.

{
  "dependencies": {
    "some-browser-cli": "^1.0.0",
    "some-cloud-agent": "^0.3.0"
  }
}

Look at what the package touches, not the name.
Which credentials it can reach on a dev machine or CI runner.
If it touches GitHub tokens, npm tokens, cloud CLI credential files, browser-stored data, SSH keys, Kubernetes kubeconfig, or Vault tokens, leave the reason for it as an app dependency on the PR.

References