A dangerous supply chain attack struck the AsyncAPI project on the npm registry, putting developers and automated build systems at risk.
Attackers republished five package versions in about ninety minutes on July 14, 2026. Each version carried the same hidden loader that runs as soon as the code is imported.
The malware does not rely on the usual install scripts that many teams already block. Instead it springs to life when any application or build tool loads the poisoned module.
That design means a common safety setting called ignore-scripts offers no real protection against this threat.
Analysts from Microsoft identified the campaign and traced how a weak GitHub Actions workflow opened the door.
Microsoft said in a report shared with Cyber Security News (CSN) that the second stage delivers a modular runtime called Miasma. That framework can talk to remote servers, stay on the machine, and hold modules ready to steal credentials.

Because @asyncapi/specs sits under many other AsyncAPI tools, the blast radius reached developer laptops, CI pipelines, containers, and even some production services.
Anyone who resolved the bad versions during the short exposure window could have run the payload without noticing anything unusual.
The overall impact is serious for teams that handle cloud keys, source control tokens, and AI platform secrets.
Even though some stealing modules were switched off in this build, the code was already present and could be turned on later through persistence channels.
AsyncAPI Malware Contains Modules
The Miasma runtime includes broad credential access code aimed at secrets that developers and CI systems often keep nearby.
It looks for more than one hundred environment variable names spanning source control, package registries, major cloud providers, container platforms, secret managers, and popular AI services.
Targets include GITHUB_TOKEN and GITLAB_TOKEN for code hosting, NPM_TOKEN and NODE_AUTH_TOKEN for package publishing, and AWS, Azure, and Google Cloud keys.
The same code also hunts Anthropic and OpenAI API keys, along with Docker and Kubernetes tokens that many pipelines store in plain environment settings.
On disk the framework searches familiar files such as .npmrc, AWS credentials, kubeconfig, SSH private keys, Vault tokens, .netrc, Docker config, and Google service account JSON.
When a GitHub token is found it can list reachable repositories and CI context through public APIs, much like earlier npm packages credential theft waves that hit the ecosystem.

In the analyzed sample the harvesting modules stayed dormant, yet the full logic shipped inside the encrypted bundle.
Persistence and command channels were already active, so a later remote update could flip those switches without another package release. Similar patterns appeared in a prior credential stealer malware campaign against popular developer tooling.
Microsoft urged teams to remove the five bad versions, purge npm and Yarn caches, and rotate every secret reachable from any host that imported the packages.
Defenders should also hunt for a file named sync.js under directories that masquerade as NodeJS support folders on Windows, macOS, and Linux.
Import-Time Delivery and Pipeline Abuse
The intrusion began with a malicious pull request against the asyncapi/generator repository. A misconfigured workflow that used pull_request_target ran untrusted code and exposed a privileged bot token.
Attackers then pushed poisoned commits and let the project’s own trusted publishing path release the malware under a valid npm identity.
This path resembles other cases of GitHub Actions workflow abuse where automated pipelines become the delivery truck.
The legitimate release workflows published @asyncapi/generator 3.3.1, generator-components 0.7.1, generator-helpers 1.1.1, and two versions of @asyncapi/specs, all carrying valid provenance from unauthorized source commits.
When a consumer imported any of those packages the loader spawned a hidden Node process. That child fetched an encrypted second stage from IPFS, wrote sync.js into an OS-specific NodeJS folder, and unlocked the Miasma runtime.
Primary control servers sat at a single IP on ports 8080, 8081, and 8091, with fallback discovery over several decentralized networks.
Security teams should pin known-good versions, rebuild from clean lockfiles, and block the listed IPFS content identifiers at the perimeter when IPFS is not required for business work.
Updating the npm CLI and reviewing workflow token scopes can reduce the chance of a repeat of this npm supply chain attack style incident.
After cleanup, rebuild shared images and golden runners so poisoned caches do not quietly return.
Confirm that compromised hashes are gone from artifact stores and watch endpoint telemetry for lingering sync.js files or odd detached Node processes.
Indicators of compromise (IoCs):-
| Type | Indicator | Description |
|---|---|---|
| Package Version | @asyncapi/specs@6.11.2-alpha.1 | Compromised package with injected loader in index.js |
| Package Version | @asyncapi/specs@6.11.2 | Compromised stable package with injected loader in index.js |
| Package Version | @asyncapi/generator@3.3.1 | Compromised package with injected loader in validator.js |
| Package Version | @asyncapi/generator-components@0.7.1 | Compromised package with injected loader in ErrorHandling.js |
| Package Version | @asyncapi/generator-helpers@1.1.1 | Compromised package with injected loader in utils.js |
| SHA-256 | d425e4583cc6185d41e95c45eda00550045a5d1919b9a012236a4520d009dbd7 | Tarball hash for @asyncapi/specs@6.11.2-alpha.1 |
| SHA-256 | 9b2e65db653ca8575c9b10eefb9a80c6006404812c2ec212bf5675e3c690233b | Tarball hash for @asyncapi/specs@6.11.2 |
| SHA-256 | bfaeb987faa6de2b5a5eb63b1233d055215b09b0349a9394f2175fd7cdf385e4 | Tarball hash for @asyncapi/generator@3.3.1 |
| SHA-256 | 082d733db0687dcd768104972b065d4b58cb1e6043688c6c20fa3702337f36ab | Tarball hash for @asyncapi/generator-components@0.7.1 |
| SHA-256 | 34014776d3d3ff11bc4439b02fd7ac0f02a887eb3a052eeafff236e2f6db8ad1 | Tarball hash for @asyncapi/generator-helpers@1.1.1 |
| SHA-256 | 8351d251cf0b5a0bd82242deaa0a14e3e1394418d55c0f4259dac4303b79fc0c | Injected index.js hash (specs alpha and stable) |
| SHA-256 | b9993a8ad0518849416798cf29668256ccb96598fc4423501ccab5312812653a | Injected validator.js hash (generator) |
| SHA-256 | b270bdf8e2274ea1af0a6eed74d8f10e5fe61012d6cc226a43cc7cc7fd9f6292 | Injected ErrorHandling.js hash (generator-components) |
| SHA-256 | 6e78713b75bd34828d49896176627f7face7aa9036cd874f2e02d9f23a9a9c71 | Injected utils.js hash (generator-helpers) |
| SHA-256 | 24b9ee242f21a73b55f7bb3297eafb33c60840907386b542ed79fc6b72365168 | Wrapper sync.js (generator-family IPFS object) |
| IPFS CID | Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyf | Second-stage payload CID for specs packages |
| IPFS CID | QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9 | Second-stage payload CID for generator-family packages |
| URL | hxxps://ipfs[.]io/ipfs/Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyf | IPFS gateway URL used to fetch second stage |
| IP Address | 85.137.53[.]71:8080 | Primary C2 endpoint |
| IP Address | 85.137.53[.]71:8081 | Upload service endpoint |
| IP Address | 85.137.53[.]71:8091 | Management configuration endpoint |
| File Path | %LOCALAPPDATA%\NodeJS\sync.js | Windows drop path for second-stage payload |
| File Path | ~/.local/share/NodeJS/sync.js | Linux drop path for second-stage payload |
| File Path | ~/Library/Application Support/NodeJS/sync.js | macOS drop path for second-stage payload |
| File Path | ~/.config/NodeJS/sync.js | Fallback drop path for second-stage payload |
| File Path | ~/.config/.miasma/run/node.lock | Runtime lock file |
| File Name | sync.js | Second-stage payload filename |
| Publisher | npm-oidc-no-reply@github[.]com | Automated publisher identity used for malicious releases |
| mDNS Service | miasma._tcp | Local discovery service name used by runtime |
Note: IP addresses and domains are intentionally defanged (e.g., [.]) to prevent accidental resolution or hyperlinking. Re-fang only within controlled threat intelligence platforms such as MISP, VirusTotal, or your SIEM.