|9 min read|Yvann Lièvre

npm: --ignore-scripts No Longer Enough, the Payload Fires at Import

The @asyncapi package compromise runs its payload at module load, not at install. Why --ignore-scripts fails and how to detect it with YARA.

YARASupply chainnpmDetectionDevSecOps
npm: --ignore-scripts No Longer Enough, the Payload Fires at Import

For years, the standard advice to protect against malicious npm packages fit in a single flag: npm install --ignore-scripts. It disables lifecycle script execution, notably postinstall, the path most supply-chain compromise payloads travel through. npm 12 goes further and blocks these scripts by default. On July 14, 2026, Microsoft Threat Intelligence documented an attack that makes this advice obsolete: the compromise of the @asyncapi organization, where the payload does not run at install, but at module load.

This is a paradigm shift worth internalizing. --ignore-scripts only blocks install hooks. If the malicious code fires when a build, a continuous integration job, or an application first imports the module, none of those protections come into play. The payload wakes up at require or import, right in the middle of legitimate execution.

What was compromised

Microsoft reports that five package versions, across four names, were republished within roughly ninety minutes, each carrying the same injected loader. The affected packages are @asyncapi/specs (in both the 6.11.2-alpha.1 prerelease and the 6.11.2 stable release), @asyncapi/generator@3.3.1, @asyncapi/generator-components@0.7.1, and @asyncapi/generator-helpers@1.1.1. Together these packages total more than two million weekly downloads, which gives the measure of the exposed surface.

The attack leveraged trusted publishing pipelines in GitHub Actions, letting the malicious code slip through without relying on traditional npm install hooks. This is T1195.002 (Supply Chain Compromise: Compromise Software Supply Chain). The payload, related to the Miasma framework already seen in the Shai-Hulud attacks, combines credential theft, AI tool poisoning, worm-like propagation, and a multi-protocol command and control channel using HTTP, Nostr, IPFS, BitTorrent, libp2p, and Ethereum.

Why import-time execution changes detection

A postinstall payload runs once, at install time, in a relatively identifiable context. An import-time payload runs every time the module is loaded, inside the application process itself. That shifts the detection point: it is no longer about watching npm install, but about spotting abnormal behavior from a running Node.js process, and having signatures on the dropped artifacts.

The payload writes a sync.js file under %LOCALAPPDATA%\NodeJS\ for persistence, which falls under T1547, and performs bulk credential collection (T1555). These file artifacts are a solid anchor for a YARA rule, which stays valid even if the injection vector evolves.

A YARA rule on artifacts and C2 markers

The following rule targets the loader's characteristic strings: the persistence path, and markers for command and control channels atypical of a legitimate npm package. A code generation package has no reason to reference IPFS, Nostr, or an Ethereum address.

rule Npm_Asyncapi_ImportTime_Loader
{
    meta:
        description = "Detects the loader injected into compromised @asyncapi packages (July 2026)"
        reference = "https://www.microsoft.com/en-us/security/blog/2026/07/15/unpacking-asyncapi-npm-supply-chain-compromise-import-time-payload-delivery/"
        author = "ThreatClaw"
        date = "2026-07-19"
    strings:
        $persist = "\\NodeJS\\sync.js" ascii wide nocase
        $ipfs = "ipfs" ascii nocase
        $nostr = "nostr" ascii nocase
        $eth = "ethereum" ascii nocase
        $b64eval = "Buffer.from" ascii
        $childproc = "child_process" ascii
    condition:
        $persist or
        ( 2 of ($ipfs, $nostr, $eth) and $b64eval and $childproc )
}

This rule fires either on the known persistence path, or on the improbable combination, in an npm module, of several exotic command and control protocols with dynamic code execution and child process spawning. The second branch aims to catch variants that would change the persistence file name but keep the C2 channel.

Complement with build-time detection

On the prevention side, a Nuclei-style scanner or a static inspection of the node_modules directory before build lets you flag the exact compromised versions. Pin your dependencies by exact version and integrity hash, and block the versions listed above in your dependency manager. Also watch for unexpected outbound connections from a continuous integration runner to IPFS addresses or Nostr relays, which have no business in a documentation-generator build pipeline.

The operational lesson is clear: the "install scripts are the only risk" mental model is out of date. A payload can lie dormant in the body of a module and wake at import, inside the most legitimate process there is. Detecting these threats requires maintained signatures on the real artifacts, not just rules on the npm lifecycle.

That is the purpose of the ThreatClaw YARA feed: detection rules built from real samples, tested against a corpus of legitimate code to limit false positives, and refreshed at the pace of supply-chain compromises.

Related articles