npm Trusted Publishing and SLSA: What the Red Hat Miasma Attack Tells Us

The Miasma supply chain attack against Red Hat npm packages in June 2026 used OIDC tokens and produced valid SLSA provenance attestations on malicious packages. Here's why provenance doesn't guarantee safety and what additional controls matter.

In June 2026, attackers compromised a Red Hat employee’s GitHub account and used it to push malicious commits and publish Trojanized npm packages across over 30 packages in the @redhat-cloud-services namespace. The packages contained a preinstall script that downloaded and executed a 4.29 MB obfuscated payload harvesting AWS, Azure, and GCP credentials, SSH keys, browser data, and CI/CD secrets.

What makes this attack instructive is what the malicious packages had: valid npm Trusted Publishing tokens (issued via OIDC from GitHub Actions) and valid SLSA provenance attestations. Both features work exactly as designed. They just don’t protect against what happened here.

How npm Trusted Publishing Works

npm Trusted Publishing is an OIDC-based passwordless publishing mechanism. Instead of a long-lived npm access token stored as a CI/CD secret, the GitHub Actions workflow requests a short-lived OIDC token from npm’s token endpoint during the publish step. The token is scoped to the package and workflow, expires after use, and is never stored anywhere.

# .github/workflows/publish.yml
jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # Required for OIDC token request
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'
      - run: npm publish --provenance
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}  # Or omit for pure OIDC

This is a genuine improvement over permanent API tokens. The attack surface for token theft via leaked secrets is eliminated. There’s no long-lived credential to rotate or accidentally commit.

What SLSA Provenance Attests

SLSA provenance is a signed attestation that records where and how a package was built. When you run npm publish --provenance, npm generates an attestation containing:

  • The git commit SHA that triggered the build
  • The GitHub Actions workflow that ran
  • The repository and owner
  • A Sigstore-signed certificate tying it together

Consumers can verify provenance with npm audit signatures or inspect it on the npm registry. The attestation proves the package was built by a specific GitHub Actions workflow from a specific commit in a specific repository.

The Gap: Provenance Proves Origin, Not Integrity

The Miasma attack exposed the precise boundary of what SLSA provenance guarantees. Here’s what the attestation confirmed:

  • The package was built from the @redhat-cloud-services/ repository
  • It was built by the organization’s GitHub Actions workflow
  • The signing certificate was issued to the repository’s GitHub identity

Here’s what the attestation did not confirm:

  • Whether the person who pushed the triggering commit was authorized
  • Whether the commit contained malicious code
  • Whether the preinstall script was intentional or injected

The compromised GitHub account pushed commits that were legitimate commits — they came from a real account that had push access. The OIDC token was valid. The provenance was accurate. Everything was working as designed, against malicious input.

What Additional Controls Catch This

Branch protection with required reviews. The Red Hat attack succeeded in part because the compromised account had direct push access to the release branch. Requiring pull request reviews from at least one other contributor would have put malicious changes in front of a second set of eyes. For high-sensitivity packages, require reviews from code owners in a CODEOWNERS file, not just any contributor.

# .github/CODEOWNERS
/package.json    @org/package-maintainers
/.github/workflows/publish.yml    @org/release-team

Preinstall/postinstall script review. The malicious payload in Miasma executed via preinstall. Socket.dev, Snyk, and similar tools specifically analyse lifecycle scripts for suspicious patterns:

# Check a package's scripts before installing
npx @socketdev/cli npm info <package-name>

# Or using npm pack + manual inspection
npm pack <package-name> && tar -tzf <package>.tgz | grep -E "\.(sh|js)"

At the organisation level, consider enforcing --ignore-scripts in CI/CD pipelines for dependency installation, only allowing scripts for explicitly approved packages:

npm ci --ignore-scripts

Commit signing with Gitsign or GPG. Provenance attests that a commit triggered a build; it doesn’t attest who actually wrote the commit. Requiring signed commits from contributors adds a layer that requires an attacker to also compromise the commit-signing key, not just GitHub access:

# Enforce signed commits via branch protection rule (GitHub API)
gh api repos/:owner/:repo/branches/main/protection \
  -X PUT \
  -f required_signatures=true

Two-person integrity for releases. The release workflow itself should require more than one approved commit. Use GitHub’s required reviewers for the publish workflow environment:

# .github/workflows/publish.yml
jobs:
  publish:
    environment: npm-production  # environment with required reviewers

Configure the npm-production environment to require approval from a named team before the job proceeds.

Monitor npm package versions. If your organisation consumes packages you don’t control, subscribe to security advisories for your direct and transitive dependencies. Socket.dev provides real-time alerting on suspicious new versions of packages in your dependency graph.

The Broader Lesson

The Miasma attack is a reminder that supply chain security is a chain, not a point. OIDC-based publishing removes one attack surface (stolen tokens). SLSA provenance removes another (unverifiable build origins). Neither addresses compromised contributor accounts, malicious commits that bypass review, or lifecycle scripts that execute arbitrary code.

The answer isn’t to distrust OIDC or SLSA — both are net improvements. The answer is to treat each control as one layer in a defence stack, and to explicitly consider what each control protects against and what it doesn’t.

For package publishers: combine OIDC publishing with branch protection, required reviews, and lifecycle script scrutiny. For package consumers: use --ignore-scripts where possible, monitor your dependency graph for new versions from packages you trust, and evaluate SLSA provenance as one signal among many.