Skip to content
Get Started. Free Consult
Blog/Security/20 May 2026

npm Supply Chain Attacks: The Hidden Risk in Every Vibe-Coded App

AI coding tools install whatever npm packages they think solve the problem. Most of the time that works. Sometimes it installs malware that has been sitting in the registry for weeks.

JO
Josh·Founder·7 min read·20 May 2026
npm Supply Chain Attacks: The Hidden Risk in Every Vibe-Coded App

Malicious packages identified across open source registries roughly doubled year on year through 2024, with npm bearing most of the load (Sonatype State of the Software Supply Chain Report). For Australian businesses shipping vibe-coded apps, every npm install your AI agent runs draws from the same poisoned well as a hand-written project. The difference is that the AI agent does not stop to ask whether the package it just pulled in is the real one.

The risk is not theoretical. The polyfill.io supply chain attack of February 2024 reached more than 100,000 websites before researchers caught it. The Lottie Player npm compromise of October 2024 inserted credential-stealing code into a package downloaded millions of times per week. The @ctrl/tinycolor worm of September 2025, which researchers nicknamed "Shai-Hulud", spread automatically through compromised maintainer accounts and pulled npm tokens, GitHub tokens and cloud credentials from anyone who installed an affected dependency. Each of these started life as legitimate code that someone trusted.

For vibe-coded apps the exposure compounds. AI coding tools install dependencies based on what the model "remembers" being a good fit, not on a real audit of the package's history. The Australian Cyber Security Centre's Essential Eight treats patching and application control as foundational mitigations. An unaudited npm dependency tree is the opposite of both. Worse, a successful supply chain attack on your dependencies usually means an obligation to notify the OAIC under the Notifiable Data Breaches scheme, whether or not you wrote any of the affected code yourself.

Anatomy of a modern npm supply chain attack

There are five common attack shapes and your AI coding agent will happily fall into any of them.

Typosquatting. The attacker publishes a package with a name a few characters off a popular one, like lodahs instead of lodash, or expreess instead of express. The AI suggests installing it. Phylum reported over 245,000 new malicious npm packages in 2024 alone, many of which were typosquats.

Dependency confusion. Your team uses a private internal package called @vibezero/util. An attacker publishes a public package of the same name with a higher version number. The npm client, configured naively, prefers the higher version. Your build pulls the attacker's code into production.

Maintainer account compromise. The legitimate maintainer of a popular package has their email, npm token or GitHub credential stolen. The attacker publishes a malicious version to the existing package name. Everyone who has the package in their dependencies pulls it on the next npm install. The ua-parser-js incident of 2021 and the event-stream incident of 2018 are the canonical examples.

Malicious update to a legitimate package. The maintainer themselves goes rogue, gets paid, or transfers the package to someone else who does. The recent @ctrl/tinycolor Shai-Hulud worm worked partly this way: each compromised package then attempted to publish itself to other packages the affected maintainer owned, spreading laterally.

Post-install scripts. npm allows packages to run arbitrary scripts on install. Even a "look at the code" review is not enough if you skip the install scripts. Malicious payloads frequently sit in the postinstall hook, fetching the real attack code from a remote server at install time so it does not appear in the published tarball.

What attackers actually do once they are in your build

The payloads have become depressingly predictable. Once a malicious package is installed in your project or your CI environment, the most common behaviours we see in incident reports are:

  • Read environment variables and exfiltrate OPENAI_API_KEY, ANTHROPIC_API_KEY, STRIPE_SECRET_KEY, AWS_*, SUPABASE_SERVICE_ROLE_KEY, and anything else the process can see.
  • Read ~/.npmrc, ~/.ssh/id_rsa, ~/.aws/credentials, ~/.config/gh/hosts.yml from the developer machine or build runner.
  • Steal browser session cookies (when the attack runs on a dev machine).
  • Plant a backdoor for later access (cryptominer, reverse shell, persistence script in ~/.bashrc).
  • Spread laterally: scan for other npm packages the same maintainer owns and try to publish themselves there. This is what made Shai-Hulud a worm rather than a one-shot.

For a vibe-coded Australian SMB application the worst case is usually credential theft. An exfiltrated Stripe key means refund fraud. An exfiltrated Supabase service role key means full read and write of every customer record in your database. An exfiltrated AWS key means cryptominers running in your account until you notice the bill.

Why vibe-coded apps are hit harder

The honest truth: most vibe-coded projects ship with a riskier dependency posture than a hand-written project of the same age and shape. There are five reasons.

One. AI coding agents install dependencies based on training-data familiarity. They tend to pick popular packages, which is mostly good. But they also pick old packages, abandoned packages and packages the maintainer transferred years ago, because those were popular at training time. Many of those now carry known vulnerabilities or have new owners with different intentions.

Two. The agent rarely checks publication date, weekly downloads or maintainer history before installing. We have audited Lovable and Bolt projects where the AI installed a package that was published two weeks earlier by a brand-new account, simply because the name was a plausible match.

Three. Lockfiles are usually committed but rarely reviewed. A package-lock.json diff with 800 new lines is treated as "AI did its thing" rather than as a code review artefact. Attackers know this. Compromised packages frequently slip through PR review because human reviewers visually filter out lockfile changes.

Four. The AI installs into your real workstation and your real CI environment. Unless you sandbox it (see our agentic coding security post), the agent is running npm install with the same permissions as you. Any post-install script has read access to your .env, your SSH keys and any cloud credential file in your home directory.

Five. Production runtime environments for vibe-coded apps are often permissive. Vercel functions, Supabase Edge Functions and similar serverless runtimes have outbound network access by default. A compromised package that wants to exfiltrate your environment variables to https://attacker.example.com/c2 just makes the HTTPS request. Nothing in the default deployment stops it.

How to defend without killing velocity

The point of vibe coding is shipping fast. The defences below add minutes to a workflow, not days.

Pin and audit lockfiles. Always commit package-lock.json (or yarn.lock / pnpm-lock.yaml). Use npm ci in CI rather than npm install so the lockfile is enforced exactly. Run npm audit on every CI build and fail the build on high or critical advisories.

Use Dependabot or Renovate. Both are free for public repos and cheap for private. They produce a PR per dependency upgrade with the changelog and any security advisories. A real human reviews the PR rather than letting an AI swallow the change.

Sandbox the AI when it runs npm install. Run agentic coding sessions in a Docker container with no host network access and no host filesystem mounts other than the project directory. The agent can install whatever it wants and the blast radius stays inside the container. We covered this pattern in our agentic coding security guide.

Restrict outbound network in production. Vercel allows IP-based firewall rules on Pro and Enterprise. AWS Lambda supports VPC-only outbound. Supabase Edge Functions can be configured similarly. If a package needs to call out to a service you have not approved, you find out at deploy time, not at incident time.

Disable install scripts where possible. npm install --ignore-scripts blocks post-install execution. Some packages legitimately need install scripts (native modules in particular), so this is not a universal switch. But on CI runners that do not need to compile anything native, --ignore-scripts removes the most common attack vector.

Run a Software Composition Analysis tool. Snyk, Socket.dev and GitHub's native dependency review all flag suspicious package behaviour (new typosquats, install-script changes, maintainer ownership transfers). Snyk has a free tier that covers most Australian SMB use cases.

Threat-model what your packages can reach. Every package in your dependency tree has access to your environment variables and your file system unless something stops it. Audit which packages run code at install time (postinstall, preinstall, prepare) and treat those as the high-risk surface.

What we do

We audit vibe-coded apps for exactly this class of issue. Our vibe code audit includes a full dependency review, a check for known-bad packages in your tree, a scan of post-install hooks, and a remediation plan that brings the project back to OWASP Top 10 alignment without rewriting everything.

For ongoing protection, our managed AI app security service runs quarterly penetration tests, monitors your dependency tree for new advisories, and gives you an Essential Eight maturity uplift across the year.

If you are not sure whether your vibe-coded app is exposed, the free vibe-code scanner catches the most common surface-level issues in about fifteen seconds. For a deeper look, our free human-reviewed security check puts a real engineer on your app and returns a plain-English report within five business days.

The npm supply chain is not going to get safer on its own. Malicious package counts are climbing. The defences are mature and they are mostly free. The only thing missing on most vibe-coded Australian SMB apps is the half-day of work to turn them on.