npmjavascriptsecuritynews

npm Security 2025: Why Provenance and Sigstore Change Everything

From the 'Shai-Hulud' worm to typosquatting, 2025 has been brutal for npm. Discover how to fortify your supply chain with provenance, Sigstore, and Trusted Publishing.

DataFormatHub Team
December 22, 202512 min read
Share:
npm Security 2025: Why Provenance and Sigstore Change Everything

The JavaScript package ecosystem, particularly npm, has always been a vibrant, if occasionally chaotic, frontier. But as we close out 2025, the air is thick with a different kind of energy: a palpable, urgent push towards a more secure software supply chain. Recent, high-profile attacks have shifted the conversation from "if" to "how" we collectively fortify our dependencies. I've been deep in the trenches, testing the latest tooling and watching the landscape evolve, and honestly, some of the advancements are genuinely impressive, even if the path to full adoption is still fraught with a few gnarly thorns.

This isn't just about patching; it's a fundamental re-evaluation of trust, provenance, and the very mechanics of how we consume and publish code. The good news? We're seeing real, tangible efforts from the core npm team, GitHub, and the broader community to address these systemic vulnerabilities.

The Rise of npm provenance and Sigstore: Trust at the Source

This is genuinely impressive because it tackles one of the most insidious threats: a compromised build or publishing environment. For years, we've relied on package.json's integrity field for checksums, but that only verifies the downloaded package against what the registry thinks it should be. It doesn't tell us if the package was built and published by the legitimate maintainer in a trustworthy environment. Enter npm provenance, now generally available since October 2023, with significant momentum through 2024 and 2025.

npm provenance is npm's implementation of the Supply-chain Levels for Software Artifacts (SLSA) framework, leveraging the power of Sigstore. The core idea is to create verifiable attestations about how and where a package was built and published. When you publish a package with --provenance, the npm CLI works with your CI/CD provider (currently GitHub Actions and GitLab CI/CD are supported) to generate a provenance attestation. This attestation includes details like the source repository URI, commit hash, and build instructions.

Here's the architectural elegance: Instead of managing long-lived signing keys, Sigstore uses short-lived, ephemeral certificates issued by a Certificate Authority (Fulcio) that federates with your OIDC provider. This certificate is then used to sign the provenance statement, and both the certificate and the signature are recorded in a public, tamper-evident transparency log (Rekor). This means you sign the build, throw away the private key, and the entire process is publicly auditable.

To publish with provenance, it's as simple as adding a flag:

npm publish --provenance --access public

And for verification? The npm audit signatures command, introduced in July 2022, is now our best friend for this. It verifies ECDSA signatures and will error if packages have missing or invalid signatures, indicating potential tampering.

npm audit signatures

This is a monumental step forward. While it doesn't guarantee the absence of malicious code, it provides a cryptographic link back to the source and build environment, empowering developers to make informed trust decisions. The limitation, for now, is its reliance on cloud-hosted runners and specific CI/CD providers. For those of us with self-hosted runners or alternative CI systems, adoption is still a bit clunky, requiring manual workarounds or waiting for broader support. But the direction is clear, and it's robust.

Hardening the Publisher: 2FA, Granular Tokens, and Trusted Publishing

The spate of supply chain attacks in 2025, including the "Shai-Hulud" worm in September and subsequent typosquatting campaigns in October, served as a brutal wake-up call. Attackers compromised maintainer accounts through sophisticated phishing, leading to malicious package injections. GitHub, which owns npm, has responded with aggressive, necessary changes to authentication and publishing, largely rolled out between October and mid-November 2025.

The key shifts are:

  1. Mandatory 2FA for Local Publishing: This is a huge win. No more publishing from a local machine with just a password. If you're publishing from your dev environment, 2FA will be required. This directly addresses the phishing vectors that plagued maintainers.
  2. Deprecation of Legacy Classic Tokens: Classic npm tokens, often long-lived and broadly scoped, are being phased out. This is critical because compromised long-lived tokens were a primary attack vector.
  3. Limited Lifetime for Granular Tokens: New granular tokens, which allow for more fine-grained permissions, will have a maximum lifetime of seven days (with a 90-day maximum for some cases). This dramatically reduces the window of opportunity for attackers if a token is stolen.
  4. Trusted Publishing Expansion: npm's support for Trusted Publishing, introduced in July 2025, removes the need to manage API tokens in CI/CD systems entirely. Instead, CI/CD providers (like GitHub Actions) can directly attest to the identity of the publisher, and npm verifies this attestation. This is the gold standard, as it eliminates the token as a secret in your build pipeline.

The transition, while necessary, has caused some workflow disruption for developers accustomed to older practices. Deprecating TOTP 2FA in favor of FIDO-based 2FA (like WebAuthn/passkeys) is also a significant move, as phishing-resistant authentication is demonstrably superior to TOTP against sophisticated attacks. While some developers might find the shift to passkeys challenging due to device compatibility, the security benefits are undeniable.

For those publishing from CI, the recommendation is to use npm publish --provenance with Trusted Publishers configuration, completely bypassing the need for explicit tokens in your CI environment. For local publishing, ensure your 2FA is up-to-date and phishing-resistant.

Beyond npm audit: The Evolving Landscape of Dependency Scanning

npm audit has been a staple since 2018, doing a decent job of flagging known vulnerabilities in your dependency tree by checking against the npm Public Advisories Database. And while it's a quick, built-in way to identify issues, recent events have highlighted its limitations. An npm audit only works against known vulnerabilities. It doesn't detect novel malware, subtle supply chain attacks that modify legitimate packages, or vulnerabilities in your own code.

The "Shai-Hulud" worm, for instance, compromised packages and republished them with malware. While npm audit might eventually catch the new malicious versions if advisories are published, the initial spread can be rapid. This underscores the need for a multi-layered approach.

This is where specialized tools truly shine. Solutions like Snyk and SonarQube go much deeper. Snyk, for example, offers real-time vulnerability detection in dependencies, continuous monitoring, and even automated pull requests to apply fixes. SonarQube, while broader in scope for code quality, also provides comprehensive security analysis for JavaScript and Node.js, capable of breaking builds if security thresholds aren't met.

Integrating these tools into CI/CD pipelines is no longer a "nice-to-have" but a "must-have." A typical setup might involve:

  1. Pre-commit hooks: Running basic linters and static analysis to catch obvious issues early.
  2. CI build step: Running npm audit for a quick check, but more importantly, integrating a robust SCA (Software Composition Analysis) tool like Snyk or a SAST (Static Application Security Testing) tool like SonarQube.
  3. Post-deployment monitoring: Continuously scanning deployed applications and their dependencies for newly discovered vulnerabilities.

The reality check here is that while npm audit is accessible, it's a baseline. Relying solely on it in 2025 is like bringing a butter knife to a gunfight. The commercial and advanced open-source tools, while requiring more setup and potentially cost, offer the depth needed to truly secure modern applications.

Runtime Security Reimagined: Deno's Permissions and JSR's Vision

While npm is the dominant package manager for Node.js, the broader JavaScript ecosystem is seeing a fascinating evolution in runtimes, with Deno and Bun challenging Node.js's hegemony. What’s exciting is how their philosophies are influencing security. Understanding the differences between Node.js, Deno, Bun in 2025: Choosing Your JavaScript Runtime is now a prerequisite for secure architecture.

Deno: Security-First by Default

Deno, created by Node.js founder Ryan Dahl, has security baked into its core. Unlike Node.js, which grants scripts unrestricted access to the system by default, Deno operates with a sandboxed permissions model. This means a Deno program cannot access the file system, network, or environment variables without explicit permission.

For example, to allow network access and read from the file system, you'd run your Deno script with flags:

deno run --allow-net --allow-read app.ts

This explicit permission model is a game-changer for preventing supply-chain attacks, where malicious packages often try to exfiltrate data or compromise the host system. If a compromised dependency tries to fetch data from an unauthorized domain or read sensitive files, Deno will simply deny it, unless you've explicitly granted that permission.

JSR: A New Registry for Modern JavaScript

Introduced in public beta in March 2024, JSR (JavaScript Registry) is Deno's answer to a modern package registry. JSR isn't meant to fork npm but to build on its success, embracing ESM, native TypeScript support, and a focus on security and developer experience.

One of JSR's standout security features is "secure, token-less publishing". Similar to npm's Trusted Publishing, JSR aims to protect against supply chain attacks by removing the need for long-lived tokens during the publishing process. JSR also focuses on full provenance for published packages, ensuring verifiable build information.

Bun: Speed with Evolving Security

Bun, the new kid on the block built in Zig, prioritizes raw speed in every aspect: runtime execution, package installation, bundling, and testing. While Bun is incredibly fast and offers npm compatibility, its security model is currently more akin to Node.js, with unrestricted access by default.

However, Bun's rapid development cycle suggests that more explicit security features might be on the roadmap. For now, if you're leveraging Bun's incredible performance, it's crucial to layer your security with robust dependency scanning and CI/CD safeguards, as its native sandboxing is not as mature as Deno's.

The Crucial Role of package-lock.json and SRI: Integrity You Can't Ignore

The package-lock.json file is far more than just a list of pinned versions; it's a cryptographic manifest of your dependency tree. Its integrity field, specifically, holds a Subresource Integrity (SRI) hash, typically SHA512, for each package. This hash is a unique fingerprint of the package's contents as it was originally published to the registry.

When you run npm install (or npm ci in CI/CD, which is highly recommended for reproducibility), npm downloads the package, recalculates its hash, and compares it against the integrity value in your package-lock.json. If they don't match, the installation fails, signaling potential tampering. This is our first, and often most critical, line of defense against malicious actors modifying packages in the registry or during transit.

However, this mechanism isn't foolproof against all supply chain attacks. The "lockfile poisoning" attack vector demonstrates this: if an attacker gains control of a maintainer's account, they can publish a malicious version of a package and then update the package-lock.json in the legitimate repository with the new, malicious integrity hash. If this compromised package-lock.json is committed and then npm install or npm ci is run, the malicious package will be installed because the integrity hash matches the attacker's version.

This is why npm provenance is so crucial as an additional layer of defense, proving who published the package and how it was built. It adds a "source of truth" beyond just the hash.

Developers should always:

  • Commit package-lock.json: This ensures deterministic installs and provides the integrity hashes for verification.
  • Review package-lock.json diffs in PRs: Look for unexpected changes, especially to package versions or integrity hashes, which could indicate a malicious actor attempting to inject a compromised dependency.
  • Ensure SHA-512 is used: Older package-lock.json files might use SHA-1, which is cryptographically weak and vulnerable to collision attacks. Modern npm versions default to SHA-512, but if you're on an older project, a fresh npm install after deleting node_modules and package-lock.json (and then committing the new one) can upgrade these hashes.
# To force update to SHA512 (use with caution, commit changes after!)
rm -rf node_modules package-lock.json
npm install

Defending Against Lifecycle Script Attacks: The Silent Threat

One of the most powerful—and dangerous—features of npm packages is lifecycle scripts. These are arbitrary shell commands defined in package.json (e.g., preinstall, install, postinstall, prepack, prepare) that execute at various stages of the package installation or publishing process. While incredibly useful for compilation, setup, or native module building, they are also a prime vector for supply chain attacks.

A malicious postinstall script, for example, can execute arbitrary code on a user's machine immediately after a package is installed. This could range from stealing environment variables and credentials to installing additional malware. The October 2025 typosquatting attacks, which used multi-stage infostealers and launched hidden terminals to extract system passwords and browser cookies, heavily leveraged postinstall scripts.

Defending against this requires a multi-pronged approach:

  1. --ignore-scripts flag: For production deployments or when auditing new dependencies, running npm install --ignore-scripts can prevent these scripts from executing. This is a crucial safeguard, especially in CI/CD environments where you want to minimize execution surface.
    npm install --ignore-scripts
    # Or via environment variable for global effect
    NPM_CONFIG_IGNORE_SCRIPTS=true npm install
    
  2. Careful Review of package.json: When adding new dependencies, always inspect their package.json for suspicious lifecycle scripts. If a simple utility package has a complex postinstall script, it's a red flag.
  3. Sandboxed Environments: Running npm install within sandboxed environments or containers can limit the blast radius of a malicious script.
  4. Static Analysis: Advanced security tools can often detect suspicious patterns in lifecycle scripts.

The Road Ahead: A More Resilient Ecosystem

Looking back at 2024 and 2025, it’s clear the JavaScript package ecosystem has been through the wringer, but it’s emerging stronger. The recent attacks, while painful, have accelerated the adoption of critical security features. We're moving towards:

  • Stronger Identity & Provenance: npm provenance and Sigstore are game-changers, offering verifiable links from published package back to source and build. This shifts trust from "I hope this is good" to "I can verify this was built as expected."
  • Hardened Publishing Workflows: Mandatory 2FA, short-lived tokens, and Trusted Publishing are making it significantly harder for attackers to compromise maintainer accounts and inject malicious code. This is a practical, sturdy defense against phishing.
  • Sophisticated Scanning: While npm audit remains useful, the increasing reliance on advanced SCA and SAST tools, integrated deeply into the SDLC, is crucial for detecting both known and novel threats.
  • Runtime-Level Security: Deno's explicit permissions model and JSR's security-first registry are pushing the boundaries of what's possible in a secure JavaScript environment. These are not just "alternatives" but "innovations" that will influence the entire ecosystem.

However, we're not out of the woods. The sheer scale of the npm ecosystem (4.5 trillion requests in 2024 alone) means it remains a prime target. The adoption of these new security features is not universal, and legacy projects will continue to pose challenges. The developer experience can still be clunky when integrating new security measures, and the documentation for some experimental features (like Node.js's experimental Permissions Model, which is still evolving) can be sparse.

My take? Embrace these changes with enthusiasm. Integrate npm publish --provenance into your CI/CD. Mandate phishing-resistant 2FA for your team. Don't just run npm audit; invest in deeper scanning. Scrutinize package-lock.json and think twice before enabling lifecycle scripts from unknown sources. The JavaScript ecosystem is more resilient than ever, but its strength ultimately depends on our collective vigilance and willingness to adopt these practical, efficient security measures. We're building the future, and making it secure is our shared responsibility.


Sources


🛠️ Related Tools

Explore these DataFormatHub tools related to this topic:


📚 You Might Also Like