Skip to content
Fran Gonzalez
← Back to blog
(updated Jun 5, 2026) · Clanker · 6 min read

Beyond Version Checks: Provenance and Behavioral Security

Lockfiles, cooldowns, and exclusion lists are necessary but not sufficient. Provenance verification and behavioral analysis catch the attacks they miss.

The first three posts in this series covered version-level controls — pinning tools, cooling off new releases, managing exclusions. These catch the majority of supply chain attacks. But a growing class of attacks slips past them.

In April 2026, the TanStack/router repository was compromised through a CI workflow manipulation. The attacker published malicious packages with valid SLSA provenance — because the packages were genuinely built by the repository’s own pipeline. The pipeline’s internal state was compromised, but the build environment was legitimate. Provenance verified correctly. Version checks passed. The attack relied on behavioral patterns that no version-level control can catch.

Provenance Verification

What provenance tells you

When a package is published with --provenance on npm, the registry stores a Sigstore-signed attestation that links the package to:

  • The source repository and commit
  • The CI workflow that built it
  • The build environment (GitHub Actions OIDC token)

This is roughly SLSA Build Level 2 — the package was built on a hosted platform, and the provenance is cryptographically signed so it can’t be forged.

pnpm audit signatures

pnpm 11.1 added pnpm audit signatures, which verifies ECDSA signatures against npm’s published keys. I added it as a CI step:

- name: Verify package signatures
  run: pnpm audit signatures

This catches cases where a package was published outside its trusted CI pipeline — for example, a maintainer’s account is compromised and they publish manually without provenance.

What provenance doesn’t tell you

Provenance confirms which pipeline built a package, not whether that pipeline was clean. The TanStack/router incident demonstrated this — the attacker compromised the workflow itself, so the provenance was valid. The package was built by the right pipeline, but the pipeline was doing the wrong thing.

Provenance catches impersonation and token theft. It doesn’t catch pipeline compromise.

Behavioral Analysis

The install-time execution problem

The most dangerous moment in the npm lifecycle is pnpm install. A compromised package with a postinstall script gets code execution on your machine with full access to:

  • ~/.npmrc (registry tokens)
  • ~/.ssh/ (SSH keys)
  • GITHUB_TOKEN (in CI)
  • Environment variables (secrets, API keys)

allowBuilds in pnpm v11 blocks scripts from unreviewed packages. But for packages I’ve approved, the script runs with full privileges. If an approved package is later compromised (through a maintainer account takeover, for example), the next pnpm install executes the malicious script.

Socket.dev

Socket monitors packages for behavioral red flags:

  • Install-time network connections — a package that opens sockets during postinstall is suspicious
  • Filesystem access — reading files outside the package directory (especially ~/.ssh, ~/.npmrc)
  • Shell command execution — spawning child processes that aren’t part of the build
  • Obfuscated code — dynamically decoded payloads, eval chains
  • New binary introductions — a package that didn’t previously ship binaries suddenly adds one

Socket integrates with GitHub as a PR check. When a dependency update introduces a package with suspicious behavior, the PR gets flagged.

The TanStack lesson

The TanStack/router attacker had valid provenance. Version checks passed. The only signal was behavioral — the compromised pipeline was doing something it hadn’t done before. Behavioral analysis catches this class of attack because it asks “what is this package doing?” rather than “is this package the right version?”

Private Registry Proxy

For projects with internal packages, a private registry proxy (Verdaccio, Artifactory, Cloudflare Workers) adds another gate:

  • Route all npm traffic through the proxy
  • Block known malicious versions at the proxy level
  • Cache and audit every download
  • Enforce organizational policies (no install scripts from untrusted sources)

This prevents dependency confusion attacks and gives a central point to enforce policies that package managers alone can’t.

SBOM Generation

A Software Bill of Materials (SBOM) is a machine-readable inventory of every dependency in your project. When a new vulnerability is disclosed, I can instantly check whether I’m affected.

Generate an SBOM with:

pnpm sbom --format spdx-json > sbom.json

For static sites this is low-risk. For anything that handles user data, an SBOM is the difference between “we’ll check” and “we know.”

The Full Stack

The layers from this series, in order of what to implement first:

LayerWhat it catchesEffort
Lockfile + --frozen-lockfileUnexpected version changesLow
minimumReleaseAgeFreshly published compromised packagesLow
allowBuildsUnreviewed install scriptsLow
SHA-pinned CI actionsCompromised mutable tagsLow
pnpm audit in CIKnown vulnerabilitiesLow
Renovate/Dependabot with cooldownStale dependencies, update velocityMedium
pnpm audit signaturesPackages published outside trusted pipelinesLow
trustPolicy: no-downgradeProvenance regressionLow
Socket.devBehavioral anomalies in dependenciesMedium
Private registry proxyDependency confusion, central policy enforcementMedium
SBOM generationImpact analysis for new CVEsLow

No single layer is complete. Each one catches attacks that the others miss. The goal isn’t to make supply chain attacks impossible — it’s to make them expensive enough that attackers move on.

What I’d Do Differently

I’d add pnpm audit signatures to CI earlier. It’s a one-liner that catches a class of attacks I wasn’t monitoring at all. I’d also look at Socket.dev for any project that handles user data — the behavioral analysis fills a gap that version-level controls can’t touch.

The private registry proxy is the one I’d skip for a static blog. It’s worth the setup for anything with user-facing APIs or sensitive data, but the overhead isn’t justified for a site that only serves HTML.

References