Executive Summary:
Several packages in the @asyncapi npm namespace were maliciously republished after an attacker abused AsyncAPI's release process. The malicious code did not rely on npm lifecycle scripts. It executed when affected runtime modules were imported or required, which means npm install --ignore-scripts would not have prevented execution once a compromised package was loaded by an application, build process, or developer tool.
The impact extended beyond the directly republished packages because @asyncapi/specs is used across AsyncAPI tooling. Parser-based validation, generators, CI jobs, container builds, and developer workstations may have been exposed if they resolved and loaded the affected versions during the publication window.
The releases moved through legitimate publishing channels, including GitHub Actions OIDC trusted publishing and valid provenance attestations. Provenance confirmed the publishing path. It did not prove that the release content was safe.
The second-stage payload has been linked to a Miasma-branded or Miasma-related modular runtime. Observed behavior included command and control activity, persistence, and fallback discovery through IPFS, Nostr, Ethereum data, BitTorrent DHT, and libp2p-related components. Some details around initial access and activation remain unresolved, but the risk is clear: the campaign combined CI/CD trust abuse, import-time execution, and cross-platform post-import behavior in a way that created exposure for software producers and downstream consumers.
Securonix ThreatWatch assesses this incident as a supply chain compromise with practical impact across development and build environments, especially where affected AsyncAPI packages were automatically resolved, cached, imported, or bundled during the malicious publication window.
Key takeaways:
-
The malicious
@asyncapipackages executed when affected modules were loaded, not during installation. Controls focused only on npm lifecycle scripts would not have stopped the payload after an application or build process imported the compromised package.
-
The packages were published through legitimate GitHub Actions workflows that used trusted publishing and produced valid provenance. That provenance helps confirm the release path, but it does not prove the source state or package contents were authorized.
-
The available reporting points to GitHub Actions abuse tied to
pull_request_targetrisk inasyncapi/generator. The exact credential theft path remains unresolved from the available logs.
-
The exposure window was short, but the affected packages sat inside commonly used AsyncAPI workflows.
@asyncapi/specscreated additional downstream risk as a transitive dependency.
-
Multiple vendors observed overlapping infrastructure and drop paths, including
85[.]137[.]53[.]71, IPFS-hosted sync.js, and per-user NodeJS masquerade directories. These overlaps give defenders useful hunting anchors across workstations, CI runners, build containers, and internal package caches.
Campaign overview:
AsyncAPI is an open-source project for defining, validating, and generating assets for asynchronous and event-driven APIs. Its ecosystem includes packages used to validate AsyncAPI documents, parse schemas, and generate documentation or code from API definitions. That dependency chain is why the compromise of @asyncapi/specs and @asyncapi/generator created risk beyond a single repository. AsyncAPI documentation identifies the parser as the component used for document validation and the generator as the package used to produce documentation and other assets from AsyncAPI definitions.
The malicious publication set included five versions across four package names:
| Package | Malicious version | Known-good rollback point reported publicly | Notes |
| @asyncapi/generator | 3.3.1 | 3.3.0 | Published at approximately 07:10 UTC through the release workflow on next. |
| @asyncapi/generator-helpers | 1.1.1 | 1.1.0 | Published in the same wave as @asyncapi/generator. |
| @asyncapi/generator-components | 0.7.1 | 0.7.0 | 0.7.0 was identified as the safe rollback floor. StepSecurity later observed the latest dist-tag resolving to clean 1.0.0 after cleanup. |
| @asyncapi/specs | 6.11.2-alpha.1 | 6.11.1 | Prerelease version published first during the second repository compromise. |
| @asyncapi/specs | 6.11.2 | 6.11.1 | Stable version later promoted to latest. |
This campaign followed a modern npm and CI/CD supply chain pattern. The attacker did not rely only on a stolen npm publish token. Instead, they abused trusted automation, used a legitimate release workflow, and produced packages with valid provenance metadata. The malicious code also ran from ordinary runtime modules, so normal package use could trigger execution without a visible postinstall script.
Technical analysis:
Initial compromise vector:
A likely compromise path involved GitHub Actions abuse through pull_request_target. GitHub notes that these workflows run with the base repository's GITHUB_TOKEN and can access repository or organization secrets. If the workflow also checks out untrusted pull request code, that combination can expose the repository to compromise.
GitHub actions and CI/CD abuse:
The pre-publication activity shows how a CI job could be turned into a credential theft step. The malicious pull request commit fetched and ran JavaScript from rentry[.]co/elzotebo999. A second stage then wrote an observer script under /tmp, scanned /proc/*/environ for INPUT_GITHUB-TOKEN, NETLIFY_AUTH_TOKEN, and NETLIFY_SITE_ID, patched actions/checkout@v5 by modifying /home/runner/work/_actions/actions/checkout/v5/dist/index.js, and sent harvested values to rentry[.]co/elzotebo. This activity likely exposed a GitHub token associated with asyncapi-bot and may have exposed Netlify credentials as well.
That helps explain why the later npm publications went through the project’s normal release process. The malicious packages were published through GitHub OIDC trusted publishing and carried valid provenance attestations. The attacker did not need to publish from outside the trusted path. After gaining enough repository-side access, the legitimate release pipeline became the delivery mechanism.
npm publication path:
For asyncapi/generator, the sequence is relatively clear. Commit: “3eab3ec9304aa26081358330491d3cfeb55cc245” was pushed to the next branch, which triggered the repository's release-with-changesets.yml workflow. Around 07:10 UTC, three malicious packages were published. Sigstore and provenance records captured the repository, branch, workflow, and commit used to build those packages. The workflow run referenced in the provenance data was no longer available during later review, which may point to cleanup or deletion after the packages were published.
The asyncapi/spec-json-schemas path is less straightforward. The project issue identifies an alpha branch compromise and key injection commit: “61a930fca724” other analysis tied the stable 6.11.2 release to refs/heads/master, final commit: “689f5b96693ab1f82a825b6d7c4ee566b0afc4c6”, and the if-nodejs-release.yml workflow. A workable timeline connects both views: alpha workflow activity occurred between 07:56 and 08:04 UTC, the malicious lineage reached master around 08:14 UTC, the final child commit landed at 08:28 UTC, and the stable package was published at about 08:30 UTC. The publication sequence is clear enough, but the exact branch path remains partially unsettled.
Malicious code placement and import-time execution:
The attacker did not use npm lifecycle hooks. Instead, the bootstrap code was placed in normal runtime files that would run during ordinary package use. The affected paths were:
| Package | Affected file |
| @asyncapi/specs | index.js |
| @asyncapi/generator | lib/templates/config/validator.js |
| @asyncapi/generator-helpers | src/utils.js |
| @asyncapi/generator-components | lib/utils/ErrorHandling.js |
The malicious code was also padded with whitespace, which pushed it out of view in diffs and made manual review easier to miss.
Because the payload ran on require() or import, simply loading the package could start a detached Node process. That made the import-time trigger one of the more important parts of the compromise. Defenses focused only on postinstall or other lifecycle scripts would not have stopped execution in CI, local development, testing, service startup, or any environment where the package was already installed and later loaded.
Payload delivery and behavior:
The first-stage loader launched node -e with an obfuscated downloader. That downloader retrieved sync.js from IPFS and wrote it to an OS-specific per-user directory named NodeJS. Two IPFS CIDs were associated with the payloads: QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9 for the generator-family packages and Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyf for @asyncapi/specs. The observed drop paths were consistent: ~/.local/share/NodeJS/sync.js on Linux, ~/Library/Application Support/NodeJS/sync.js on macOS, %LOCALAPPDATA%\NodeJS\sync.js on Windows, with ~/.config/NodeJS/sync.js used as a fallback.
sync.js was an encrypted, multi-megabyte Node.js bundle. The loader contained the key material needed to recover it offline. The decryption flow used HKDF-SHA256, AES-256-GCM, and a printable ASCII rotation step before execution. Those layers made analysis slower, but they did not prevent static recovery.
At runtime, the payload communicated with HTTP C2 infrastructure at 85[.]137[.]53[.]71. It also included fallback discovery through IPFS, Nostr relays, Ethereum-recorded data, BitTorrent DHT, and libp2p-related peer discovery. The implant beaconed roughly every 30 seconds. Because the C2 channel used plain HTTP, an on-path attacker could potentially inject commands into the response path.
Credential theft, targeting logic, and runtime limits:
The analyzed build had active persistence and C2, but several automated modules appear to have been disabled in the shipped configuration. Code for credential harvesting, propagation, AI-tool poisoning, and a deadman switch was present, yet those features were not all active by default.
That does not make the payload low risk. The implant still provided persistence, beaconing, and remote shell access, which could allow an operator to collect data manually. It also contained broad credential targeting logic, including more than 100 environment variable names and multiple credential file locations.
Cleanup and public-response status:
By 11:18 UTC on July 14, all five malicious versions had been removed from npm metadata, and fresh installs no longer resolved to the affected packages. That did not automatically clear systems that installed them during the exposure window. Existing node_modules directories, lockfiles, package manager caches, CI caches, artifact repositories, and mirrored registries still needed review.
One additional issue was direct tarball access. @asyncapi/specs@6.11.2-alpha.1 was reportedly still reachable by direct tarball URL after it no longer appeared in registry metadata. That points to a possible delay between registry cleanup and backing storage or CDN purge. For defenders, the cleanup point should not be treated as the end of exposure. Cached or mirrored copies can keep the malicious artifact available after public removal.
The available records still leaves several gaps. The exact credential theft sequence has not been fully proven. The relationship between the generator and spec-json-schemas compromises also remains partly unresolved. Downstream infection counts are unknown and would require repository audit logs, retained workflow logs, npm publication metadata, and endpoint telemetry from affected consumers.
Attack chain:
| Step | Attacker objective | Observed action | Affected asset | Defensive visibility |
| Recon and cover activity | Create noise and hide a malicious PR | Donation-themed PR flood, with hidden PR #2155 among the activity | asyncapi/generator | GitHub PR analytics, rate spikes, contributor reputation, branch provenance |
| Initial workflow abuse | Execute attacker code in privileged context | pull_request_target workflow processed attacker-controlled content | GitHub Actions / Netlify preview workflow | Workflow logs, audit logs, unsafe checkout detection, token scope review |
| Credential access | Obtain repository-side access sufficient for pushes or release abuse | Public evidence points to Rentry-based second stage and likely token exposure, but exact credential path remains partly unresolved | GitHub / possibly Netlify | Egress logs, workflow environment access, token use anomalies |
| Release-path abuse | Publish malware through trusted release process | Unauthorized commits landed on release-relevant branches; legitimate workflows published packages with valid provenance | asyncapi/generator, asyncapi/spec-json-schemas, npm | GitHub audit logs, branch protections, package provenance review, publish monitoring |
| Runtime trigger | Ensure execution beyond install time | Loader placed into normal runtime modules executed during import | Affected npm packages | Code review, package diffing, runtime child-process telemetry |
| Payload delivery | Fetch second stage without obvious static packaging | IPFS-hosted sync.js fetched from public gateway and written to per-user path | Developer workstation / CI runner | Proxy logs, DNS logs, EDR file-create telemetry, IPFS monitoring |
| Post-compromise control | Maintain execution and operator access | C2 beacons, persistence attempts, remote shell capability, fallback channels | Endpoint or runner | Process trees, autorun persistence, service creation, registry/shell profile changes |
Securonix ThreatWatch summary:
Impact assessment: Known impact includes five malicious package versions, import-time retrieval of a second-stage payload, active persistence logic, active C2, and remote shell capability. The pre-publication activity also indicates that the attacker gained repository-side access or tokens sufficient to perform unauthorized pushes or releases. The exact credential sequence is still not fully proven from the available logs.
Impact depended heavily on where the package was loaded. On a developer workstation, the risk included user-space persistence, exposed tokens, SSH key access, browser data exposure, and interactive command execution. In CI/CD, the blast radius could reach source-control credentials, cloud deployment secrets, signing material, shared caches, base images, and downstream build integrity. For downstream consumers, the higher-risk condition was importing the package during the exposure window, not simply having it listed as a dependency.
Mitigation and response recommendations: Start by confirming whether affected versions were resolved, cached, imported, or bundled. Review dependency trees, lockfiles, build manifests, private mirrors, package manager caches, CI caches, and artifact repositories for the five malicious versions. Replace affected versions with known-good releases and purge npm or Yarn caches where applicable. Search for sync.js in the reported NodeJS drop directories across developer endpoints, CI runners, build containers, and shared build images.
Treat any workstation or CI/CD runner that imported an affected package as potentially compromised. Isolate the host where possible, preserve evidence, and check for detached node processes, unexpected sync.js execution, modified shell RC files, systemd user services, Windows Run keys, and other persistence artifacts. Rotate credentials from a clean system, including GitHub tokens, npm credentials, SSH keys, cloud access keys, CI/CD secrets, registry tokens, browser sessions, and signing material that may have been available in the affected environment.
Review release infrastructure in parallel. Audit GitHub Actions workflows that use pull_request_target or other privileged triggers. Reduce default GITHUB_TOKEN permissions, verify branch and environment protections, and confirm who can approve, modify, or trigger release workflows. Review audit logs for unexpected pushes, deleted workflow runs, unusual bot activity, release automation changes, or commits made outside the normal maintainer path.
Keep provenance controls in place, but do not rely on them alone. Add release approvals for sensitive packages, quarantine newly published upstream dependencies before internal use, apply release-age holds where possible, and compare SBOMs against known-good baselines. Trusted publishing reduces some token exposure. Release-age controls reduce exposure to newly published backdoored packages. Both controls are stronger when paired with source integrity checks and release authorization gates.
Lessons for defenders: npm supply chain defense cannot stop at blocking postinstall scripts. In this incident, the attacker placed the loader in normal runtime paths, so execution occurred when the package was imported. Detection should account for what packages do at runtime, not only what happens during installation.
Provenance is useful, but it is not the same as release authorization. The malicious packages were built by legitimate workflows, and the provenance reflected that path. The weak point appeared earlier, where unauthorized source state reached a trusted build and release process. Teams using SLSA-style provenance should pair it with branch protection, workflow separation, protected release environments, and policy checks that evaluate the actor, branch, commit, and review state.
Developer tooling should be part of the detection surface. SOC and threat hunting teams should correlate package versions, process activity, file writes, and network connections across endpoints and runners. DevSecOps teams should focus on pull_request_target usage, OIDC trust paths, runner isolation, and publish gates. Security leaders should treat build and release systems as production infrastructure and govern them accordingly.
Detection opportunities:
The strongest detection opportunities go beyond package manager logs. This campaign touched source control, CI/CD, runtime process creation, file writes, and network egress. Hunts should pivot across developer endpoints, build runners, source-control audit logs, package metadata, and artifact repositories.
Practical detection concepts:
| Detection area | What to look for |
| Package installation anomalies | Newly published versions in sensitive namespaces that are resolved and imported within hours of upstream publication, especially when the version was not present in the prior lockfile baseline. npm min-release-age can help enforce a cooling-off period for new upstream versions. |
| Unexpected package version changes | Lockfile, SBOM, or build manifest movement from @asyncapi/specs@6.11.1 to 6.11.2, or from @asyncapi/generator@3.3.0 to 3.3.1, during the July 14 exposure window. |
| Suspicious import-time execution | node spawning detached child node processes shortly after application start or package import, especially command lines that reference node -e, sync.js, or the reported IPFS CIDs. |
| Outbound network activity | Connections from developer systems or CI/CD runners to 85[.]137[.]53[.]71 on ports 8080, 8081, and 8091; IPFS gateway retrievals; Nostr relay traffic; DHT bootstrap activity; or unexpected Ethereum RPC access from build runners. |
| GitHub Actions workflow abuse | pull_request_target workflows that check out untrusted PR code, unexpected write-capable GITHUB_TOKEN use, and bot-attributed pushes to protected or release branches outside normal maintainer workflows. GitHub warns against these patterns. |
| Anomalous npm publish events | Releases with valid provenance where the triggering commit, actor, branch, or review state does not match the expected release path. In this case, provenance reflected the build path, but the source state behind that path was not trustworthy. |
Threat hunting guidance:
| Hunt question | Data sources | What to look for |
| Which systems resolved or imported the malicious package versions during the exposure window? | Lockfiles, SBOMs, package manager logs, build logs, artifact repository access logs | Any direct or transitive reference to the five malicious versions. |
| Which systems created or accessed sync.js under NodeJS-named directories? | EDR file telemetry, filesystem monitoring, endpoint triage | File create/write/execute events for sync.js in the reported drop paths. |
| Did any developer workstation or runner make outbound connections to the reported C2 or fallback infrastructure? | DNS logs, proxy logs, firewall logs, EDR network telemetry | 85[.]137[.]53[.]71, ipfs.io, Nostr relays, DHT bootstrap domains, Ethereum RPC. |
| Were there detached node child processes spawned by package-manager or application processes? | EDR process telemetry, auditd, Sysmon-like telemetry | node -e, hidden detached processes, node executing sync.js. |
| Did any build environment expose tokens or secrets shortly before suspicious pushes or publishes? | GitHub audit logs, CI/CD job logs, secret scanning, ephemeral runner artifacts | Use or leakage of GitHub tokens, Netlify tokens, npm tokens, cloud credentials. |
| Were release branches pushed by unexpected actors or placeholder identities? | GitHub audit logs, repository events, branch protection logs | next, alpha, master, placeholder identity "Your Name" <you@example.com>, invalid-email-address, bot-attributed pushes. |
| Are internal package mirrors or caches still serving malicious tarballs? | Artifact repositories, npm cache directories, container base image scans | Residual copies of the bad versions, particularly prerelease 6.11.2-alpha.1. |
| Did persistence artifacts survive on endpoints? | EDR, triage collections, autorun checks | miasma-monitor.service, shell RC modifications, HKCU Run values, lock files. |
Appendix A: Indicators of compromise
| Indicator | Type | Description or context |
| 85[.]137[.]53[.]71:8080 | IP:port | Primary HTTP C2 / beacon service. |
| 85[.]137[.]53[.]71:8081 | IP:port | Upload or exfiltration service. |
| 85[.]137[.]53[.]71:8091 | IP:port | Proxy management or management configuration service. |
| hxxps[://]ipfs[.]io/ipfs/QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9 | URL | Generator-family second-stage IPFS object. |
| QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9 | IPFS CID | Generator-family stage-two object. |
| Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyf | IPFS CID | specs stage-two object. |
| ipfs[.]io | Domain | Public IPFS gateway used for payload delivery. |
| rentry[.]co | Domain | Used in pre-publication CI-stage retrieval and exfiltration. |
| hxxps[://]rentry[.]co/elzotebo999 | URL | Rentry stage retrieved by malicious PR workflow code. |
| hxxps[://]rentry[.]co/elzotebo | URL | Rentry location used for credential exfiltration per Datadog. |
| wss://relay.damus.io | Nostr relay | Fallback/update channel. |
| wss://relay.nostr.com/ | Nostr relay | Fallback/update channel. |
| router.bittorrent.com:6881 | DHT bootstrap | Peer discovery support infrastructure. |
| dht.transmissionbt.com:6881 | DHT bootstrap | Peer discovery support infrastructure. |
| 0x12c37A86a0Ed0beBe5d1d6a43E42f07860eAc710 | Ethereum contract | Main or fallback C2 dead-drop contract. |
| 0x1969ab05d67b67fdcaa26240f738ccb077e1cd84 | Ethereum contract | Backup contract reported by Wiz. |
| 0x92d4C5413e4F7B258a114964101F9e1C6d64C6Ba | Ethereum wallet | Deployer wallet reported by Wiz. |
| hxxps[://]ethereum-rpc.publicnode[.]com | Domain/URL | Ethereum RPC endpoint embedded in config. |
| 85.137.53.0/24 / AS43641 / VSYS-AMS | Network block metadata | Reported RIPE context for C2 IP. |
| 9b2e65db653ca8575c9b10eefb9a80c6006404812c2ec212bf5675e3c690233b | SHA-256 | Tarball hash for @asyncapi/specs@6.11.2. |
| d425e4583cc6185d41e95c45eda00550045a5d1919b9a012236a4520d009dbd7 | SHA-256 | Tarball hash for @asyncapi/specs@6.11.2-alpha.1. |
| bfaeb987faa6de2b5a5eb63b1233d055215b09b0349a9394f2175fd7cdf385e4 | SHA-256 | Tarball hash for @asyncapi/generator@3.3.1. |
| 34014776d3d3ff11bc4439b02fd7ac0f02a887eb3a052eeafff236e2f6db8ad1 | SHA-256 | Tarball hash for @asyncapi/generator-helpers@1.1.1. |
| 082d733db0687dcd768104972b065d4b58cb1e6043688c6c20fa3702337f36ab | SHA-256 | Tarball hash for @asyncapi/generator-components@0.7.1. |
| b9993a8ad0518849416798cf29668256ccb96598fc4423501ccab5312812653a | SHA-256 | Injected malicious validator.js file hash. |
| b270bdf8e2274ea1af0a6eed74d8f10e5fe61012d6cc226a43cc7cc7fd9f6292 | SHA-256 | Injected malicious ErrorHandling.js file hash. |
| 8351d251cf0b5a0bd82242deaa0a14e3e1394418d55c0f4259dac4303b79fc0c | SHA-256 | Injected index.js file hash for specs alpha and stable. |
| 6e78713b75bd34828d49896176627f7face7aa9036cd874f2e02d9f23a9a9c71 | SHA-256 | Injected src/utils.js file hash. |
| 24b9ee242f21a73b55f7bb3297eafb33c60840907386b542ed79fc6b72365168 | SHA-256 | sync.js wrapper hash for generator-family IPFS object. |
| 22bf76fe317ea6769bd38619bd440e42d119bd6b | SHA-1 | Malicious generator file (validator.js). |
| a7e18d96efd3cdb127ef4cdcad9e3ad26c482bf2 | SHA-1 | Malicious generator-helpers file (utils.js). |
| 9890950adcbc2478e7a080234f053214adbad44e | SHA-1 | Malicious generator-components file (ErrorHandling.js). |
| c70e105e212ff3c1daa04bb2a62507717f296b0b | SHA-1 | Malicious specs file (index.js). |
| c8cb3f6d5b90c46686d2bf531dc1a5786e27edc5 | SHA-1 | sync.js second-stage payload. |
| 0432fa4ba871877d94081fe83323fa24dfa1491e9de8725cbab7b734de9e9be3b233ef6742fd6264437c9532223d687b05fa540b70af6a516b8539af84d0eeb48e | secp256k1 public key | Attacker public key reported in config. |
Appendix B: MITRE ATT&CK mapping
| Tactic | Technique ID | Technique name | Observed behavior |
| Resource Development / Initial Access | T1195.001 | Compromise Software Dependencies and Development Tools | Malicious code was inserted into AsyncAPI npm packages that downstream users installed and imported. |
| Initial Access / Persistence / Privilege Escalation / Defense Evasion | T1078 | Valid Accounts | The attacker appears to have abused existing repository-side credentials or account context associated with asyncapi-bot or equivalent release permissions. |
| Execution | T1059 | Command and Scripting Interpreter | Injected JavaScript spawned hidden detached Node.js processes for staged execution. |
| Command and Control | T1105 | Ingress Tool Transfer | The runtime fetched sync.js from IPFS into the compromised environment. |
| Credential Access | T1552.001 | Credentials In Files | The runtime targeted .npmrc, cloud credential files, SSH keys, kubeconfig, and related artifacts. |
| Persistence | T1547.001 | Registry Run Keys / Startup Folder | Windows persistence used HKCU Run value miasma-monitor. |
| Persistence | T1543.002 | Systemd Service | Linux persistence wrote ~/.config/systemd/user/miasma-monitor.service. |
| Persistence | T1546.004 | Unix Shell Configuration Modification | macOS and Unix-like persistence appended commands to shell RC files such as .zshrc or .bashrc. |
Appendix C: Software supply chain security mapping (NIST Secure Software Development Framework)
| SSDF practice | How the incident relates | Defensive implication | Recommended improvement |
| PO.5 Implement and Maintain Secure Environments for Software Development | NIST specifically highlights PO.5 as a secure-environment practice. | The AsyncAPI incident demonstrates that source control and CI/CD are part of the development environment security boundary. | Periodically review branch protections, workflow permissions, secret storage, and organization-level token defaults. |
| PS.3 Protect All Forms of Code From Unauthorized Access and Tampering, including provenance for releases | NIST highlights added provenance-sharing tasks under SSDF 1.1. | Release provenance helped confirm where the malicious packages came from, but release protection failed upstream. | Preserve provenance, but add release approvals, artifact quarantine, and source-state verification around public publication. |
| PW. practices for secure build and review | SSDF expects secure toolchains and code review inside the SDLC. | Hidden single-line injected JavaScript should be a review signal, but automation-triggered publishing compressed the response window. | Add automation that flags whitespace-padded single-line changes, entrypoint modifications, and unexpected child-process code inside runtime modules. |
| RV.1 Identify and confirm vulnerabilities on an ongoing basis | NIST describes RV.1 as continuous vulnerability identification. | Rapid package takedown and consumer notification are part of the software response function after malicious publication. | Establish package-level incident playbooks covering depublication, advisory publication, cache purge, and consumer notification for registry compromises. |
Analyst assessment:
The operator showed a solid understanding of open-source release mechanics and CI/CD trust boundaries. The choices made in this campaign point to someone who knew where defenders commonly look in npm compromises. Execution was moved into import-time code paths, package publication went through legitimate release workflows, and the infrastructure mixed centralized C2 with decentralized delivery or fallback channels. Even with some payload modules disabled in the analyzed build, the tradecraft suggests more than basic package poisoning.
The objective was likely durable access and follow-on exploitation, not nuisance publication. Workflow abuse before publication, remote shell capability after execution, persistence logic, and broad credential targeting all point to interest in developer and build environments. There is not enough evidence to attribute the activity to a specific actor or group. Labels such as M-RED-TEAM v6.4 and Miasma-related identifiers should be treated as clustering signals, not attribution proof.
The main gaps are the exact credential theft sequence, the full relationship between the generator and spec-json-schemas compromises, and the downstream infection count. Stronger conclusions would require repository audit logs, retained workflow logs, registry publication metadata, and endpoint telemetry from affected consumers. As of July 22, 2026, those gaps remain unresolved.
Conclusion:
The July 2026 AsyncAPI incident shows how package compromise, CI/CD workflow abuse, and valid build provenance can all exist in the same attack. The attacker did not need to break every control. They only needed to compromise the trust boundary before publication, then use the official pipeline to distribute the malicious packages. After publication, import-time execution and IPFS-based second-stage delivery increased the chance of execution in developer and build environments where the affected packages were loaded as part of normal use.
Defensive priorities are straightforward: identify any use of the five malicious versions, inspect affected endpoints and runners for sync.js and persistence artifacts, rotate exposed credentials from clean systems, purge caches and internal mirrors, and harden GitHub Actions workflows that mix untrusted pull request content with privileged repository context. Provenance still has value, but it needs to be paired with stronger controls over source integrity, workflow authorization, and release approval.
References:
-
StepSecurity | Coordinated AsyncAPI Supply Chain Attack: Miasma RAT Delivered via Compromised CI/CD Pipelines in Two Repositories
-
Microsoft Security Research | Unpacking the AsyncAPI npm supply chain compromise and import-time payload delivery
-
Aikido | AsyncAPI npm packages backdoored via GitHub Actions
-
Wiz| M-Red-Team: AsyncAPI Supply Chain Compromise via GitHub Actions
-
Datadog Security Labs | Compromised AsyncAPI npm packages: inside a CI supply-chain attack
-
GitHub | [SECURITY] Malicious versions published via compromised next branch
-
GitHub | [SECURITY] Malicious versions published via compromised alpha branch
-
GitHub Docs | Securely using pull_request_target
-
GitHub Docs | Secure use reference
-
GitHub Docs | Workflow syntax for GitHub Actions
-
NIST CSRC | Secure Software Development Framework Project

