Skip to main content
npm Package Attack Harvested GitHub TokensIncident
4 min readFor Security Engineers

npm Package Attack Harvested GitHub Tokens

A supply chain attack targeting npm packages and GitHub Actions workflows revealed how attackers can now move across package ecosystems to harvest developer credentials at scale. The Miasma malware campaign compromised multiple packages in the npm and Go ecosystems, with the initial breach vector being a force-pushed commit to a widely-used GitHub Action.

What Happened

On June 24, 2026, attackers force-pushed a malicious commit to codfish/semantic-release-action, a GitHub Action used in CI/CD pipelines. This compromised action became the entry point for credential harvesting across projects using it in their workflows.

The attack expanded to compromise npm packages associated with LeoPlatform and RStreams, both cloud-native serverless frameworks. The malware, designated Miasma, was designed to exfiltrate developer credentials and tokens from build environments. The campaign then jumped ecosystems entirely, affecting Go packages in the Verana Blockchain project.

The cross-ecosystem nature marks a tactical evolution. Instead of saturating a single package manager with malicious releases, attackers used initial access in one ecosystem (npm) to pivot into another (Go), multiplying their credential harvest surface.

Timeline

  • June 24, 2026: Malicious commit force-pushed to codfish/semantic-release-action repository.
  • June 24-25, 2026: Compromised action executes in CI/CD pipelines of downstream projects, harvesting GitHub tokens and npm credentials.
  • Late June 2026: Attackers publish malicious npm releases affecting LeoPlatform and RStreams packages using harvested credentials.
  • Early July 2026: Attack expands to Go ecosystem, compromising Verana Blockchain packages.
  • July 2026: Security researchers identify the Miasma malware family and begin tracking the campaign.

Which Controls Failed or Were Missing

Dependency pinning in workflows: Teams referenced the GitHub Action by branch name rather than commit SHA. When the attacker force-pushed to the branch, all workflows automatically pulled the malicious code. No human review occurred.

GitHub token scope limits: The GITHUB_TOKEN available in Actions had write permissions to repositories and packages. The workflows needed only read access for their semantic versioning tasks. Excessive permissions gave attackers the keys to publish malicious packages.

Package registry authentication audit: Multiple developers had npm publish tokens stored as repository secrets. No system tracked which tokens existed, when they were last rotated, or whether they were still necessary. When the Action harvested these secrets, no alerts fired.

Cross-ecosystem monitoring: Security teams monitored npm package integrity but had no visibility into Go module dependencies. When the attack jumped ecosystems, existing detection systems went blind.

Commit signature verification: The malicious commit was not signed. The repository had no branch protection requiring signed commits, so the force-push succeeded without cryptographic proof of author identity.

What the Standards Require

PCI DSS v4.0.1 Requirement 6.3.2 mandates that bespoke and custom software be developed securely, including "security of coding practices." Allowing unsigned commits in a repository that feeds production deployments violates this control.

NIST 800-53 Rev 5 SA-10 (Developer Configuration Management) requires organizations to "employ configuration management using [Assignment: organization-defined configuration management roles and responsibilities] throughout the system development life cycle." Referencing dependencies by mutable branch names instead of immutable commit SHAs fails this control.

ISO/IEC 27001 Annex A.8.30 (Outsourced development) addresses the security of development activities outsourced to third parties. Running third-party Actions without pinning commits or limiting token scope fails this requirement.

SOC 2 Type II CC6.1 (Logical and Physical Access Controls) requires that "the entity implements logical access security software, infrastructure, and architectures over protected information assets to protect them from security events." Storing publish tokens without rotation schedules, scope documentation, or access audits violates this control.

Lessons and Action Items for Your Team

Pin all GitHub Actions to commit SHAs, not tags or branches. In your workflow YAML, replace uses: codfish/semantic-release-action@v1 with uses: codfish/semantic-release-action@a1b2c3d4... where the SHA is the specific commit you've reviewed. This breaks automatic updates, which should be deliberate, not automatic.

Set GITHUB_TOKEN to minimum required permissions. Add this to every workflow file:

permissions:
  contents: read
  packages: none

Only escalate specific permissions when required for a job. If a workflow needs to publish a package, grant packages: write to that single job, not the entire workflow.

Rotate and document all package registry tokens quarterly. Create a spreadsheet: token name, scope, last rotation date, owner, purpose. If you can't explain why a token exists, revoke it. Use scoped tokens (npm granular access tokens, not legacy tokens) so compromise of one doesn't grant full account access.

Require signed commits on protected branches. Enable "Require signed commits" in branch protection rules for main/master and release branches. Distribute GPG or SSH signing keys to your team. Unsigned force-pushes will fail.

Monitor package publications in all ecosystems your team uses. If you consume npm packages, Go modules, and Python wheels, you need visibility into unexpected publications in all three. Set up alerts for new package versions published outside your normal release windows. Tools like Socket.dev or Phylum can monitor multiple ecosystems from a single dashboard.

Audit third-party Actions before adding them to workflows. Review the source code of any Action you're considering. Check the repository's commit history for force-pushes (red flag). Verify the maintainer's identity. If the Action is critical and maintained by a single developer, consider forking it to your organization and maintaining your own reviewed copy.

Implement network egress filtering in CI/CD runners. Self-hosted runners should not have unrestricted internet access. Allowlist the specific domains needed (github.com, registry.npmjs.org, etc.) and block everything else. This won't stop all exfiltration, but it forces attackers to use approved channels where you have better logging.

The Miasma campaign succeeded because it exploited the trust model of modern development workflows. You trust that GitHub Actions do what their README claims. You trust that force-pushes come from legitimate maintainers. You trust that your secrets stay secret. Each assumption was wrong. Build your controls around verifying trust, not assuming it.

Topics:Incident

You Might Also Like