DevelopmentPlaybook11 min readPublished July 18, 2026

Second major npm compromise in a week · payload fires at import, not install

The AsyncAPI npm Attack: Malware That Fires at Import, Not Install

Four @asyncapi npm packages — including @asyncapi/specs at 2.66 million weekly downloads — were trojanized on July 14, 2026. The payload runs at module-load time, so npm 12's new install-script blocking and the old --ignore-scripts escape hatch do nothing to stop it. Here is the layered defense playbook that actually would have.

DA
Digital Applied Team
Senior strategists · Published Jul 18, 2026
PublishedJul 18, 2026
Read time11 min
Sources10
@asyncapi/specs downloads
2.66M
per week
poisoned Jul 14
Lifecycle scripts used
0
runs at import time
evades --ignore-scripts
Trojanized versions
5
across 4 packages
one publish window
Exposure window
≈4h
publish to unpublish
2h48m minimum

The AsyncAPI npm supply chain attack of July 14, 2026 trojanized four packages — anchored by @asyncapi/specs and its 2.66 million weekly downloads — with a payload that executes the moment the module is imported. No install script. No postinstall hook. Nothing for npm 12's new default protections, shipped just six days earlier, to block.

That timing is the story. On July 8, npm 12 turned off install scripts by default and the ecosystem exhaled — the attack vector behind most of 2025's headline compromises was finally closed at the platform level. Six days later, an attacker demonstrated the counter-move: skip the lifecycle hooks entirely and detonate at module-load time, when a build, test suite, CLI run, or production app first calls require or import on the poisoned code.

This guide covers what happened hour by hour, why install-time controls structurally cannot catch this class of attack, the three-stage anatomy of the malware, the CI misconfiguration that made it possible, and — the part you can act on — a defense-layer matrix mapping every common mitigation against the specific attack stages it would or would not have interrupted.

Key takeaways
  1. 01
    Four packages, five versions, one UTC morning.@asyncapi/specs (2.66M weekly downloads), @asyncapi/generator, generator-components, and generator-helpers were trojanized on July 14, 2026 and unpublished within roughly four hours — fast, but wide enough to matter at this download volume.
  2. 02
    The payload runs at import, not install.No lifecycle script is declared at all. npm install --ignore-scripts and npm 12's new default install-script blocking do nothing — the malware fires when a build, CI job, or app first imports the module.
  3. 03
    Valid provenance, poisoned release.The packages were published through npm trusted publishing with legitimate SLSA attestations. The root cause was a pwn request — a misconfigured pull_request_target workflow that leaked a privileged token weeks after the risk was flagged internally.
  4. 04
    Second major compromise in a single week.Jscrambler's npm packages were compromised on July 11 via a classic preinstall-hook stealer; AsyncAPI followed on July 14 with an import-time payload. Unit 42 tracks at least six major npm campaigns in 2026 before this one.
  5. 05
    Only layered controls interrupt this chain.Lockfile pinning with npm ci, a 24-72 hour dependency cooldown, CI egress allowlisting, and credential rotation each cover a stage that install-script blocking and provenance verification demonstrably do not.

01The IncidentFour packages, one morning, 2.66M weekly downloads.

On the morning of July 14, 2026 (UTC), five malicious versions landed across four packages in the @asyncapi npm namespace: @asyncapi/specs v6.11.2 and v6.11.2-alpha.1, @asyncapi/generator v3.3.1, @asyncapi/generator-components v0.7.1, and @asyncapi/generator-helpers v1.1.1. Socket.dev's registry-metadata inspection puts the first publish at 07:10:42 UTC and the last — the stable specs release — at 08:30:09 UTC. Microsoft's security team published the deepest incident analysis the following day.

The blast radius is what separates this from a typosquat. @asyncapi/specs alone sees 2.66 million downloads a week, and it is also pulled in transitively by @asyncapi/parser (roughly 970,000 weekly downloads) through a ^6.11.1 semver range — meaning the poisoned 6.11.2 satisfied countless installs automatically, with no direct upgrade action by anyone. Combined weekly downloads across the four packages run to two million-plus, by some counts over three million; outlets round differently, so treat the per-package specs figure as the anchor stat.

Anchor stat
@asyncapi/specs weekly downloads
2.66M

Also consumed transitively by @asyncapi/parser (~970K/week) via a ^6.11.1 range — the poisoned patch release satisfied existing installs with zero action from maintainers downstream.

4 packages · 5 versions
Exposure window
First publish to full unpublish
≈4h

First malicious publish 07:10:42 UTC; npm and AsyncAPI maintainers pulled all five versions by roughly 11:18 UTC. StepSecurity puts per-package exposure between about 2h48m and 4h03m.

Jul 14, 2026
Lifecycle scripts
Declared by the malware
0

No preinstall, install, or postinstall hook exists in any trojanized version. The implant executes at module load — which is precisely what hook-focused scanners and install-script blockers never inspect.

import-time execution

The roughly four-hour window sounds reassuring until you multiply it against the download rate. At 2.66 million weekly downloads, the specs package averages hundreds of installs a minute — and every CI pipeline that ran npm install against a floating range in that window, then executed a build that imported the module, ran the payload. Speed of unpublish limits the population; it does not identify who in that population was hit.

Weekly downloads in July's blast radius · reported baselines

Source: StepSecurity, cybersecuritynews.com, July 2026 reported baselines
@asyncapi/specsDirect + transitive via parser · poisoned Jul 14
2.66M/wk
@asyncapi/parserPulls specs via ^6.11.1 range · not itself trojanized
~970K/wk
jscramblerCompromised Jul 11 · preinstall-hook stealer
~15.8K/wk

02The npm 12 GapWhy npm 12's defenses never fired.

npm 12 shipped on July 8, 2026 with the most significant default hardening in the registry's history: allowScripts off by default — blocking preinstall, install, and postinstall lifecycle scripts plus implicit node-gyp builds — and git-dependency and remote-tarball resolution disabled unless explicitly allowed. We covered the full mechanics in our npm 12 install-script defaults guide. Those defaults are real progress against the attack pattern that dominated 2025.

They are also completely orthogonal to what happened on July 14. The AsyncAPI malware declares no lifecycle script of any kind — so there is nothing for npm 12 to block, and npm install --ignore-scripts, the veteran mitigation in every hardening guide, is equally inert. The payload triggers the first time the poisoned module is actually imported: a generator build, a CI test run, a CLI invocation, or an application server booting in production.

Microsoft's framing
Microsoft's researchers put the distinction plainly: "Unlike the more common postinstall-hook supply-chain pattern, this campaign executes at module-load (import/require) time." Their analysis states directly that the common --ignore-scripts mitigation does not neutralize it. Install-time controls and import-time controls are two different defense layers — passing the first says nothing about the second.

This is the paragraph worth internalizing as a trend, not a one-off. Platform defaults are public documents: the moment npm 12 announced that install scripts were blocked by default, every capable attacker knew exactly which technique was depreciating and which adjacent technique — runtime execution inside legitimate module code — was untouched. The AsyncAPI campaign landing six days after the npm 12 release is the clearest demonstration yet that supply-chain defenses and attacker innovation move in the same news cycle. Defenses that respond to last quarter's attack pattern buy you exactly one quarter.

03Attack AnatomyThree stages, from one line to a 744-module framework.

Socket.dev's stage-by-stage breakdown — corroborated by Microsoft and Datadog Security Labs — shows a deliberately minimal footprint inside the npm tarball and a deliberately maximal capability at the end of the chain.

Stage 1
Import-time implant
one obfuscated statement

A single obfuscated JavaScript statement injected into one legitimate source file per package — index.js in specs, src/utils.js in generator-helpers — using hex-like variable names and a base64 string table, with a main() call executed at module load.

Detached hidden Node child process
Stage 2
IPFS loader
hardcoded content identifier

Fetches an encrypted second stage from an IPFS gateway and writes it to a path disguised as Node.js runtime state — sync.js under %LOCALAPPDATA% on Windows, ~/Library/Application Support on macOS, ~/.local/share on Linux.

~8.2MB encrypted on disk
Stage 3
Miasma framework
~3.09MB decrypted JS

A tasking framework bundling roughly 744 modules per Socket.dev's static analysis: multi-channel C2, platform-specific persistence, and credential theft spanning well over 100 environment variables and file types.

Multi-channel C2 · safe-mode config

Stage 1 spawns a detached, hidden Node.js child process — spawn("node", ["-e", stage2], { detached: true, stdio: "ignore", windowsHide: true }) — the moment the poisoned module loads, then returns control to the legitimate package code. The developer or CI job sees a normal, green run. Using IPFS for stage 2 is the clever part: because the npm tarball only ever contained a small downloader, the operator can update or replace the final payload at will without republishing anything to npm.

The Miasma framework itself is built for resilience. Beyond REST/HTTP beaconing to a hardcoded IP, Socket.dev, StepSecurity, and Datadog document code paths for Nostr relays, Ethereum smart-contract lookups, libp2p, BitTorrent DHT bootstrap nodes, and mDNS discovery — most shipped present but disabled in the observed "safe mode" configuration. Credential targeting spans GitHub, npm, AWS, Azure, GCP, Docker, and Kubernetes tokens, .npmrc and .aws/credentials files, SSH keys, browser-saved passwords and cookies across Chrome, Brave, Firefox, and Edge, and the macOS Keychain. StepSecurity additionally reports an ai-tool-poisoner.js component targeting locally installed AI coding assistant sessions and config — a detail from one source, not independently confirmed by Microsoft or Socket.dev, but consistent with the July 11 Jscrambler stealer's documented interest in AI tool configs. To be clear: that is about local config files on an already-compromised machine, not a compromise of any AI tool itself.

04Root CauseA pwn request — and provenance that told the truth about a lie.

No npm account was phished and no maintainer went rogue. The entry point, documented by Chainguard and corroborated by Socket.dev, StepSecurity, and Wiz, was a misconfigured pull_request_target GitHub Actions workflow in the asyncapi/generator repository — a "pwn request." The workflow checked out attacker-controlled PR code while running with base-repo secrets access, exposing a highly privileged personal access token belonging to the asyncapi-bot account. The attacker opened roughly three dozen decoy pull requests in the hours before the exploit PR, muddying which one mattered.

The sharpest detail: the vulnerable pattern was known. Wiz traces a researcher proof-of-concept to April 2026 with a proposed fix left unmerged, and Chainguard describes the pattern as flagged internally weeks before the attack. Sources differ on the precise dates, but they agree on the shape — a multi-week window between internal warning and external exploitation.

"Both attacks are CI/CD pipeline compromises, not stolen npm tokens or malicious maintainers."— Rohan Prabhu, StepSecurity

That framing — covering both AsyncAPI and the July 11 Jscrambler incident — matters because it moves the failure domain. The malicious packages were published through npm's trusted publishing (GitHub OIDC) mechanism and carried valid SLSA provenance attestations. The attestations were not forged. They correctly proved the packages came from the project's authorized CI workflow. What they could not prove — what provenance structurally cannot prove — is that the source commit which triggered that workflow was legitimate.

The provenance limit
Chainguard's one-line summary of the structural gap: "Provenance proves where a package came from, not that its source was trustworthy at the time." Both Chainguard and Unit 42 frame provenance verification as necessary but not sufficient — in this incident, verifying attestations would have returned a clean pass on every poisoned release.

05The 2026 CadenceSeven campaigns in twelve weeks.

AsyncAPI did not happen in a vacuum. Palo Alto Networks' Unit 42 documents at least six major npm mass-poisoning campaigns in 2026 before this one, each sharing the same mechanism: the attacker gains write or publish access, then rides a legitimate CI pipeline to a trusted release. The table below lists the dated campaigns from Unit 42's tracker plus July's two incidents — the same pattern we examined when a supply-chain compromise hit AI infrastructure tooling earlier this year.

2026 npm supply-chain attack cadence: dated campaigns from Unit 42's npm threat-landscape tracker plus the July 2026 Jscrambler and AsyncAPI incidents, with reported scale and the gap in days between listed campaigns. Compiled by Digital Applied from Unit 42, Socket.dev, StepSecurity, and The Hacker News reporting, July 2026.
Date (2026)CampaignReported scaleGap since previous
Apr 22Bitwarden CLI / Checkmarx distributionNot broken out in Unit 42's summary
Apr 29SAP CAP ecosystem~570,000 weekly downloads affected7 days
May 11TanStack373 malicious versions across 169 packages12 days
May 19@antv wave639 malicious packages published in about an hour8 days
Jun 1Red Hat32 compromised packages13 days
Jul 11Jscrambler~15,800 weekly downloads · install-hook infostealer40 days
Jul 14AsyncAPI2.66M weekly downloads (specs alone) · import-time payload3 days

Read the vector column of history, not just the dates. Jscrambler (July 11) used a classic preinstall-hook native binary — the attack class npm 12 now blocks by default. AsyncAPI (July 14) used import-time execution — the class it does not. Three days apart, the ecosystem got a controlled experiment in defense lag: the same week that one vector became harder, the adjacent vector was exploited at well over a hundred times Jscrambler's download volume. The Jscrambler case stings for a second reason — the victim is itself a security and obfuscation vendor, and its plugin ecosystem was affected across multiple versions.

Projecting forward, the honest expectation is that import-time and runtime payloads become the default rather than the exception, because that is where the defensive tooling is thinnest. Registry operators can scan for lifecycle hooks cheaply; distinguishing a malicious module-load statement from the millions of legitimate side effects that fire on import is a materially harder problem. Expect the next platform-level battleground to be behavioral scanning at publish time and sandboxed module execution — and expect attackers to probe whatever those defaults leave open, on roughly the same one-news-cycle lag this incident demonstrated.

06Defense MatrixWould your control have stopped this attack?

Most coverage explains either what happened or what to do, in isolation. The more useful exercise is mapping each common control against the three stages of this specific chain — stage 1, the import-time spawn; stage 2, the IPFS payload fetch; stage 3, the Miasma C2 and credential theft. The verdicts below synthesize the Microsoft and Socket.dev technical breakdowns against the mitigation guidance from Unit 42, Chainguard, and StepSecurity.

Defense-layer matrix for the AsyncAPI npm attack: each common supply-chain control mapped against whether it would have stopped stage 1 (import-time process spawn), stage 2 (IPFS payload fetch), and stage 3 (Miasma command and control), with the reason why. Synthesis by Digital Applied from Microsoft Security Blog and Socket.dev stage analysis cross-referenced with Unit 42, Chainguard, and StepSecurity mitigation guidance, July 2026.
ControlStage 1 · import spawnStage 2 · IPFS fetchStage 3 · Miasma C2Why / why not
Install-time controls
npm install --ignore-scriptsNot stoppedNot stoppedNot stoppedThere is no lifecycle script to block — the trojanized packages declare none. The payload fires when the module is imported.
npm 12 default install-script blockingNot stoppedNot stoppedNot stoppedBlocks preinstall / install / postinstall hooks and implicit node-gyp builds. This campaign never used any of them.
Version, lockfile & publish controls
Lockfile pinning + npm ci in CIStoppedStoppedStoppedA committed lockfile predating July 14 plus npm ci prevents semver drift onto the poisoned 6.11.2 — the chain never starts.
SLSA provenance / Sigstore verificationNot stoppedNot stoppedNot stoppedThe malicious releases carried valid attestations — the project's authorized CI workflow really did build them. Provenance proves origin, not trustworthiness.
Dependency cooldown window (24-72h)StoppedStoppedStoppedEvery malicious version was unpublished within roughly four hours. Even a 24-hour cooldown comfortably outlives the exposure window.
Runtime & network controls
CI-runner egress allowlistingNot stoppedStoppedLargely stoppedThe local process spawn still happens, but the IPFS gateway fetch and C2 beaconing die against a strict outbound allowlist.
Import-time sandboxing / isolated executionContainedStoppedStoppedIsolating what a module can spawn, read, and reach at import time limits the implant even after the poisoned code lands.
Credential rotation + short-lived tokensNot stoppedNot stoppedNot stoppedDoes not interrupt execution — it devalues what the stealer exfiltrates. A blast-radius control, not a prevention control.

The pattern in the matrix is stark. Of the two controls the ecosystem spent 2025-26 celebrating — install-script blocking and provenance attestation — neither interrupts a single stage of this chain. The controls that do are older and less glamorous: a committed lockfile with npm ci, a publication cooldown, and a CI runner that simply cannot reach an IPFS gateway. Defense in depth is not a compliance phrase here; it is the only configuration in which this attack fails.

07The PlaybookThe layered playbook, in adoption order.

Everything below comes from the post-incident guidance of Unit 42, Chainguard, StepSecurity, and Datadog, sequenced by effort-to-payoff. The first two need no new tooling — only discipline in files you already have.

Layer 1 · versions
Lockfile discipline + cooldown

Commit package-lock.json, run npm ci (never npm install) in CI, and block adoption of any version published within the last 24-72 hours. Most malicious 2026 versions were pulled inside that window — AsyncAPI's in about four hours.

Adopt everywhere, this week
Layer 2 · CI config
Kill the pwn request

Replace risky pull_request_target triggers with plain pull_request, or restrict them to explicit base-branch checkouts. Require maintainer approval before any PR-triggered workflow gains secrets access.

Audit workflows now
Layer 3 · network
Egress allowlisting on runners

Limit CI outbound traffic to your private registry and known deploy targets. Alert specifically on connections to IPFS gateways and BitTorrent DHT bootstrap nodes from build infrastructure — both are in this campaign's chain.

Highest-leverage detection
Layer 4 · identity
Assume-breach credential hygiene

Short-lived tokens, scoped publish rights, and rehearsed rotation drills. This does not stop execution — it makes the 100+ credential types Miasma targets worth less when stolen.

Blast-radius insurance

Two additions round out the stack. First, keep verifying provenance anyway — it raises attacker cost and catches other attack classes, even though it passed cleanly here. Second, fold these layers into your pipeline design rather than bolting them on; our CI/CD pipeline design reference treats egress control and secrets scoping as first-class architecture decisions, not security afterthoughts. For teams without a dedicated platform function, this is exactly the kind of hardening our web development engagements build into delivery pipelines by default.

Note also npm's parallel identity-hardening track: alongside the npm 12 script defaults, granular access tokens configured to bypass 2FA are deprecated, with restrictions beginning August 2026 and further publishing limits announced for January 2027. That timeline is worth building into your token inventory now rather than during a forced migration.

08If You're ExposedThe response checklist for the unlucky four hours.

If any environment installed one of the five malicious versions during the July 14 window and subsequently imported the module, treat the machine as compromised. In order:

  • Pin and reinstall clean versions. Purge node_modules and lockfile entries referencing @asyncapi/specs 6.11.2 / 6.11.2-alpha.1, generator 3.3.1, generator-components 0.7.1, or generator-helpers 1.1.1.
  • Hunt the stage-2 artifact. Look for an unexpected NodeJS-named state directory containing sync.js in %LOCALAPPDATA% (Windows), ~/Library/Application Support (macOS), or ~/.local/share (Linux).
  • Check persistence. Microsoft documents a Windows HKCU Run key named miasma-monitor, a Linux systemd user unit (miasma-monitor.service), and macOS shell-RC injection in .zshrc, .bashrc, or .bash_profile.
  • Rotate everything the stealer targets. GitHub, npm, AWS, Azure, GCP, Docker, and Kubernetes tokens; .npmrc and .aws/credentials; SSH keys; browser-saved passwords and session cookies. StepSecurity additionally recommends treating local AI coding assistant sessions and config on affected machines as compromised.
Detection signatures
Microsoft published named detections for this campaign — Trojan:JS/MiasmStealer.SC and Trojan:Script/Supychain.A — plus Defender for Endpoint behavioral rules covering detached Node.js child processes, IPFS retrieval patterns, and the observed persistence writes. If you run Defender, confirm these signatures are current before declaring an environment clean.

09ConclusionDefense that assumes the next vector.

The state of npm security, July 2026

Install-script blocking solved last year's attack. This was this year's.

The AsyncAPI compromise is the cleanest natural experiment supply-chain security has produced: six days after npm turned off the ecosystem's favorite attack vector by default, an attacker shipped 2.66 million weekly downloads' worth of malware through the vector the defaults do not touch. Nothing about the response — unpublish in roughly four hours, vendor analyses within a day — was slow. The gap was structural, not operational.

The durable lesson sits in the defense matrix: the controls that would have interrupted this chain are boring, layered, and mostly free. A committed lockfile with npm ci, a one-day cooldown on new versions, an egress allowlist on CI runners, and credential hygiene that assumes eventual breach. None of them is novel. All of them were rare enough in practice that this campaign was worth an attacker's effort.

The forward bet is equally plain: as install-time doors close, import-time and runtime payloads become the default attacker move, and the pwn-request pattern — riding a legitimate pipeline to a legitimately signed release — keeps beating any control that only verifies where code came from. Build for the vector after this one: constrain what your dependencies can do when they run, not just how they arrive.

Harden your pipeline before the next campaign

Supply-chain defense is a layered system, not a flag.

Our team helps engineering organizations harden delivery pipelines against supply-chain attacks — lockfile and cooldown policy, CI egress control, secrets scoping, and incident response drills — delivered in days, not quarters.

Free consultationExpert guidanceTailored solutions
What we work on

Pipeline-security engagements

  • Dependency policy — lockfiles, npm ci, cooldown windows
  • GitHub Actions audits — pull_request_target and secrets scope
  • CI egress allowlisting and anomaly detection
  • Credential rotation drills and short-lived token migration
  • Incident response runbooks for dependency compromises
FAQ · AsyncAPI npm attack

The questions engineering teams are asking this week.

On July 14, 2026, five malicious versions across four packages in the @asyncapi npm namespace were published: @asyncapi/specs v6.11.2 and v6.11.2-alpha.1, @asyncapi/generator v3.3.1, @asyncapi/generator-components v0.7.1, and @asyncapi/generator-helpers v1.1.1. The specs package alone sees 2.66 million weekly downloads and is pulled in transitively by @asyncapi/parser through a ^6.11.1 semver range, so the poisoned patch release satisfied many installs automatically. The trojanized code spawns a hidden Node.js process the moment the module is imported, fetches an encrypted second stage from IPFS, and ultimately deploys the Miasma tasking framework — a roughly 3MB credential stealer and command-and-control client. npm and the AsyncAPI maintainers unpublished all five versions within roughly four hours of the first publish.
Related dispatches

Continue exploring engineering deep dives.