MergeGuide
How-To

How to Defend an AI-Assisted Workflow Against npm Supply Chain Attacks

·5 min read
Share

On May 28, a single npm maintainer alias published 14 credential-stealing packages in a four-hour window. Every one of them executed the moment someone ran npm install. No import required, no function call. A preinstall hook fired during installation and dropped a payload that hunted AWS credentials, HashiCorp Vault tokens, GitHub Actions secrets, and npm publish tokens, per Microsoft's analysis.

It wasn't an isolated event. The next day, Microsoft documented a separate dependency-confusion campaign: 33 packages registered under lookalike corporate scopes, profiling developer and build environments. Two campaigns in two days, one registry.

If a teammate suggested adding opensearch-setup-tool to your project, you might ask where it came from. When an AI assistant suggests it, with a plausible name, a spoofed repository link pointing at the real OpenSearch project, and a mature-looking version number like 1.0.9108, it gets installed. This post is the practical defense: four controls you can deploy this week.

Why AI assistance widens the blast radius

AI coding assistants recommend dependencies constantly, and a measurable fraction of those recommendations don't exist. The USENIX Security 2025 study by Spracklen et al. (We Have a Package for You!) tested 576,000 generated code samples across 16 models and found 19.7% of suggested packages were hallucinated: roughly 5.2% for commercial models, 21.7% for open-source ones.

The dangerous part isn't the error rate; it's the predictability. When researchers re-ran identical prompts ten times, 43% of hallucinated names reappeared on every single run. A name your assistant invents today, it will invent again tomorrow, for your competitor's team too. Attackers register those predictable names and wait. The industry calls this slopsquatting, and the Cloud Security Alliance flagged it as an active supply-chain vector this April.

Combine that with agentic workflows where the assistant proposes a package and runs the install in the same loop, and the window for human judgment (the moment someone might have asked "wait, is elastic-opensearch-helper a real library?") closes entirely. The May 28 campaign needed exactly one unreviewed install to take CI/CD credentials.

Step 1: Turn off install-time code execution

The May campaign's entire execution model was npm lifecycle hooks. Remove that and the payload never runs:

npm config set ignore-scripts true

In CI, make it explicit per-invocation: npm ci --ignore-scripts. This is also Microsoft's own first-line mitigation guidance for the campaign.

The caveat: some legitimate packages (native modules, esbuild, sharp) need build scripts. pnpm solved this properly. Since v10 it refuses to run dependency lifecycle scripts by default and gives you an explicit allowlist (onlyBuiltDependencies) for the handful that genuinely need them. Allowlist-by-exception is the right shape: the default is no execution, and every exception is a deliberate, reviewable decision.

Step 2: Put a cooldown on new versions

Malicious package versions are usually detected and pulled within hours to days. The May 28 packages were taken down after Microsoft's report to the npm team. A cooldown means your builds simply never see them.

pnpm 11 now enforces this by default: minimumReleaseAge is set to 1440 minutes, so versions younger than 24 hours don't resolve. You can be stricter, with an escape hatch for packages you trust to hotfix:

# pnpm-workspace.yaml
minimumReleaseAge: 10080   # one week
minimumReleaseAgeExclude:
  - "@yourorg/*"

If you're on npm or Yarn, the same principle applies even without a native setting: pin versions, and don't let automated dependency updates merge on day zero.

Step 3: Treat the lockfile as a security boundary

A committed lockfile is only a control if nothing regenerates it silently. Three rules:

  1. CI installs with npm ci (or pnpm install --frozen-lockfile), which fails on any lockfile drift instead of resolving it.
  2. No agent or developer regenerates a lockfile as a side effect. A lockfile diff is a reviewable event, not noise.
  3. Add package.json and lockfiles to CODEOWNERS so every dependency change routes to a human who owns that decision.

That last one matters most in AI-assisted teams. The assistant can propose the change; a person approves the trust decision.

Step 4: Gate dependency changes at the PR

Pre-commit controls can be bypassed; the PR gate is where policy becomes enforceable for everyone, agents included. GitHub's dependency-review-action fails any PR that introduces packages with known vulnerabilities and supports deny-lists. Pair it with two policies:

  • Any net-new dependency requires explicit human approval: not a rubber stamp on a 400-file PR, but a named reviewer for the dependency change itself.
  • Verify registry signatures and provenance attestations in CI (npm audit signatures), so a package claiming to be built from a given repo actually was.

The real problem is enforcement timing

Each of these controls works. The failure mode is coverage: the one repo out of fifty where ignore-scripts was never set, the agent that ran an install outside the configured workspace, the cooldown that got removed during an incident and never restored. Settings drift. Policy written in a wiki is a suggestion.

The durable version of this is policy enforced at the moment code is created: when the assistant proposes the import, not after the credential harvester has already run in CI. That's the design principle behind MergeGuide's graduated enforcement layers (MCP, IDE, pre-commit hooks, PR gate): the dependency policy your security team wrote is verified in the code itself, at generation time, and a hallucinated or hours-old package never reaches npm install.

Concrete next step: run npm config get ignore-scripts on your CI image today. If it returns false, you know what your afternoon looks like.

CM

Chuck McWhirter

Founder & CEO, MergeGuide

Cybersecurity veteran with nearly three decades of experience spanning malware analysis, application security, and security operations. U.S. Air Force veteran (Air Force CERT), CISSP since 2000. Previously led solutions architecture teams at ReversingLabs, McAfee, and ArcSight. Founded MergeGuide to solve the governance gap created by AI-assisted development.