JH← Back to blog

The AsyncAPI npm Attack Shows Why pull_request_target Is So Dangerous

Attackers compromised AsyncAPI's npm packages via a GitHub Actions pull_request_target flaw, hitting 2.9 million weekly downloads with credential-stealing malware. Here's the attack chain and fix.


On July 14, an attacker opened 37 pull requests against the AsyncAPI generator repository at 05:08 UTC. By 07:10 UTC — less than two hours later — the first trojanized npm package was live, eventually reaching packages with a combined 2.9 million weekly downloads. The entire attack chain, from opening a malicious pull request to publishing backdoored packages under a trusted namespace, took under two hours and required no stolen credentials to start. It exploited a single, well-documented, still-common GitHub Actions misconfiguration: a workflow using pull_request_target that checked out the pull request's own code instead of the base branch. If your organization has any GitHub Actions workflow using pull_request_target, this incident is worth reading in full before your next release, not after your own supply chain gets hit the same way.

The attack chain, step by step

The mechanics here are worth understanding precisely, because the vulnerability class is common and the fix is simple once you understand what went wrong. The asyncapi/generator repository had a workflow configured to trigger on pull_request_target — an event type that, unlike the more common pull_request trigger, runs with full access to the base repository's secrets, specifically because it's designed for legitimate use cases like commenting on PRs from forks. The critical mistake: the workflow checked out the code from the incoming pull request itself, rather than the trusted base branch, before executing.

That combination is a well-known anti-pattern often called a "pwn request." Because pull_request_target grants secret access, and the workflow was running attacker-controlled code from the PR instead of the repository's own trusted code, the attacker's PR #2155 — one of 37 opened that morning — contained obfuscated JavaScript that exfiltrated the repository's npm publish token to a paste site. The workflow completed at 05:16 UTC, eight minutes after it started, with the token now in the attacker's hands. From there, the attacker pushed malicious commits directly to the project's next branch at 06:58 UTC and published the first compromised package at 07:10 UTC.

What the malware actually did

The affected packages — @asyncapi/generator-helpers@1.1.1, @asyncapi/generator-components@0.7.1, @asyncapi/generator@3.3.1, and two versions of @asyncapi/specs — deployed a credential-stealing remote access trojan researchers have named Miasma. Unlike the more common postinstall-hook supply chain pattern, where malware runs once during package installation and can sometimes be caught by install-time monitoring, this campaign executed at module-load time — meaning the payload ran whenever the compromised package was imported or required, not just when it was first installed. That's a meaningfully harder detection surface, since it means the malicious code activates on ordinary usage, not just a discrete install event.

The first-stage payload was obfuscated and downloaded an encrypted second-stage payload from IPFS — a decentralized storage network that makes takedown considerably harder than a conventional web-hosted command-and-control server, since there's no single hosting provider to notify. Once active, Miasma targeted browser-stored passwords, SSH keys, npm and GitHub authentication tokens, cloud provider credentials, and cryptocurrency wallets, while maintaining an active command-and-control channel for the attacker.

Why this specific package family made it a bigger deal

@asyncapi/specs alone accounts for roughly 2.7 million of the combined 2.9 million weekly downloads across the compromised packages, meaning the vast majority of the exposure came from a single dependency that many projects likely pull in transitively without deliberately choosing it — a common pattern in the npm ecosystem where a widely used tool becomes a dependency of many other tools, multiplying blast radius well beyond the direct download count of the immediately compromised packages.

Why "pwn requests" keep happening despite being a known pattern

GitHub has documented the risks of pull_request_target misuse for years, and security researchers have published guidance on the pattern repeatedly. The persistence of this vulnerability class despite clear documentation says something important about the state of CI/CD security broadly: workflow configuration is treated as plumbing, not attack surface, by most engineering teams, and it rarely gets the same security review scrutiny as application code itself. A workflow file sitting in .github/workflows/ is functionally as security-critical as anything in your production codebase — it runs with real secrets and real permissions — but it's usually written once by whoever set up CI initially and never revisited unless something breaks.

How this compares to other recent npm supply chain incidents

The AsyncAPI compromise lands in the same month as npm's own v12 release, which disables install scripts by default and blocks git or remote-URL dependencies without explicit configuration — a defensive change specifically designed to reduce exactly the category of risk that install-time supply chain attacks represent. There's a useful, slightly uncomfortable lesson in the timing: npm v12's defenses would not have stopped this particular attack, because Miasma's payload executed at module-load time rather than through a postinstall hook. Ecosystem-level defenses are necessary but never sufficient on their own — attackers adapt to whichever specific technique gets closed off, and the AsyncAPI campaign is itself a live example of that adaptation, choosing an execution method that sidesteps the very fix the ecosystem just shipped.

This also isn't an isolated incident in 2026's broader threat landscape. npm has seen a steady cadence of supply chain compromises this year, and the pattern across most of them is the same: attackers target the publishing pipeline — stolen tokens, compromised maintainer accounts, or misconfigured CI/CD — rather than trying to find and exploit vulnerabilities in the published code itself. That's a rational strategic shift for attackers, because compromising a publishing pipeline once yields control over every future version an organization's users install, rather than requiring a fresh exploit for each target.

Why open source maintainers are the actual pressure point here

It's worth naming directly that this incident isn't really an AsyncAPI failure in the sense of bad-faith negligence — it's a demonstration of a structural problem across open source infrastructure broadly. Volunteer and small-team-maintained projects with millions of downloads carry enterprise-scale blast radius but rarely have enterprise-scale security budgets or dedicated security review processes for their CI/CD configuration. The asyncapi/generator repository is exactly this kind of project: widely depended upon, maintained by a relatively small team, and until this incident, running a workflow configuration that had likely gone unreviewed since it was first written. Any organization that depends heavily on open source infrastructure — which is to say, virtually every organization writing software today — has a genuine interest in supporting the maintainers of its most-depended-upon packages, whether through direct funding, security audit contributions, or simply flagging configuration issues like this one when they're spotted, rather than treating open source dependencies as a free, zero-maintenance resource.

What to actually check in your own repositories this week

  1. Audit every workflow in your organization that uses pull_request_target. For each one, confirm explicitly whether it checks out code from the incoming pull request or from the trusted base branch. If it checks out PR code while running with secret access, that's the exact pattern that enabled this attack, and it needs to be fixed immediately, not scheduled for a future sprint.

  2. Prefer pull_request over pull_request_target unless you have a specific, documented reason not to. The safer default trigger doesn't grant secret access to code from forks in the first place, eliminating this entire vulnerability class for the majority of workflows that don't genuinely need elevated permissions.

  3. If you must use pull_request_target, never check out and execute the PR's own code within that context. Split the workflow so that any step requiring secrets operates only on the base branch's trusted code, and any step that needs to touch PR-submitted code runs in a separate, secret-free job.

  4. Audit your dependency tree for the specific compromised AsyncAPI packages and versions, and rotate any credentials that may have been exposed on systems where those packages were installed or imported between July 14 and the disclosure.

  5. Treat module-load-time execution as a detection gap in your current tooling. If your security monitoring only watches for suspicious behavior during package installation, this incident is a direct demonstration of why that's insufficient — build monitoring that can catch anomalous behavior during normal runtime execution of dependencies, not just at install time.

  6. Extend this audit beyond npm. The pull_request_target misconfiguration pattern is language- and registry-agnostic; any CI/CD pipeline using GitHub Actions with this trigger type across Python, Ruby, Go, or any other ecosystem your organization maintains carries the identical risk.

The takeaway that matters beyond AsyncAPI

This wasn't a sophisticated zero-day or a novel attack technique — it was a well-documented misconfiguration that a five-minute code review could have caught, executed with enough speed and precision to compromise packages with millions of weekly downloads in under two hours. That combination — known vulnerability class, fast execution, high-value target — is exactly the profile of attack that keeps succeeding across the software supply chain, not because defenders don't know the fix, but because CI/CD configuration audits rarely make it onto anyone's security review checklist until after an incident like this one happens to your own repository.