Dependency Safety for Vibe-Coded Apps
AI agents casually npm install whatever makes the error go away — typosquats, dead packages, CVEs, bundle bloat. How to vet dependencies before you accept them.
The short answer: The fastest way a vibe-coded app gets compromised isn't your code — it's a dependency the agent casually npm installed to make an error go away. Agents pull packages with lookalike typosquat names, abandoned libraries, known CVEs, and a 2MB dependency to do a one-line job. The fix is boring and total: vet every package before you accept it, pin exact versions, commit the lockfile, and let a scanner fail the build. Treat every dependency as code you are personally shipping, because you are.
Your own code gets reviewed. The 40 transitive packages an agent dragged in to satisfy one import do not — and that's where the risk lives. A dependency runs with the same privileges as the rest of your app: it can read your env vars, make network calls, and touch the filesystem. "The agent added it and the build passed" is not a security review. Here's the one you actually need.
1. Vet the package before you accept it
When an agent proposes a dependency, it's optimizing for "makes the code work," not "is this thing trustworthy." Three checks, every time, before the install lands:
- Is it real? Typosquatting is the oldest supply-chain trick — a malicious package named one character off a popular one (
lodahsforlodash,crossenvforcross-env,discordjsfordiscord.js). An agent that half-remembers a name will install the wrong one without blinking. Confirm the exact name against the official docs or the real repo, not the agent's memory. - Is it maintained? Recent releases, issues that get answered, a commit history that didn't stop two years ago. An abandoned package is an unpatched CVE waiting to happen and a dependency you'll be forced to rip out later.
- Is it popular? Download counts aren't proof of safety, but a package with millions of weekly downloads has thousands of eyes on it. A package with 40 downloads and no stars that the agent reached for is worth a hard look.
2. Pin exact versions — never open ranges
This is the single highest-leverage habit. Agents (and npm install defaults) write caret ranges like ^1.2.0, which means "any 1.x newer than this." That's a standing invitation for a future install to silently pull a version you never reviewed — including the one an attacker published after compromising a maintainer's account.
Exact versions make your builds reproducible and your supply chain auditable: an upgrade becomes a deliberate, reviewable change in a diff, not something that happens behind your back at 2am on a CI runner.
3. Commit the lockfile, and watch for the typosquat
The lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock) records the exact resolved version and an integrity hash for every package and every transitive dependency. Commit it. Then npm ci on CI installs that exact tree and fails if anything doesn't match the hash — which is your tripwire against a tampered package.
And when you read a lockfile or a proposed install, read the name like an attacker wrote it:
4. Scan in CI, prefer the platform, and watch the weight
Three more habits that close the loop:
- Run
npm audit(or Snyk / Dependabot /osv-scanner) in CI, and make a high-severity finding fail the build. A vulnerability flagged in a pull request is a fix; the same one discovered in production is an incident. - Prefer the platform over a dependency. Modern JS has
fetch,structuredClone,crypto.randomUUID(),Intl,Array.prototype.flat. Agents reach for a package out of habit —node-fetch, auuidlib, a left-pad — when the runtime already ships the capability. Every dependency you don't add is a supply-chain risk you never took. - Audit the bundle cost. Pulling a 300KB date library to format one timestamp, or all of
lodashfor a singleget, is bloat the agent won't think about. Check the weight (bundlephobia,source-map-explorer) and reach for a lighter alternative or a few lines of your own.
The one-pass checklist
Before you accept a dependency a vibe-coding agent added, run this:
- [ ] Package name verified against the official source — not a typosquat
- [ ] Actively maintained (recent releases, answered issues)
- [ ] Popular enough to have real scrutiny (download counts)
- [ ] Pinned to an exact version, no
^or~ - [ ] Lockfile committed; CI uses
npm ci(notnpm install) - [ ]
npm audit/ a scanner runs in CI and fails on high-severity CVEs - [ ] Could the platform or stdlib do this instead?
- [ ] Bundle cost checked — not a heavy lib for a trivial job
Use agents and skills deliberately
Dependency safety is a place where pointing a specialist agent at the generalist's output pays off directly. The agent that wrote the feature was optimizing for "works"; a reviewer with a narrower brief catches the supply-chain mistakes it never considered.
- A security-and-hardening skill to audit new and existing dependencies for known CVEs, unmaintained packages, and suspicious names before you accept them.
- A performance-optimization skill to flag bundle bloat — a heavy library pulled in for a trivial task, or a dependency that duplicates something the platform already provides.
- A dependency/vulnerability scan wired into CI so a flagged package fails the build instead of reaching production.
Dependencies are the part of a vibe-coded app you didn't write but are fully responsible for. The agent treats npm install as free; you can't. Vet the package, pin the version, commit the lockfile, scan in CI — and the casual install stops being the weakest link in your supply chain.
This is one of the vibe-coding deep-dives — start from the vibe-coding precautions overview. It pairs closely with security and networking precautions, where the same scanner-in-CI habit lives, and the frontend precautions, where bundle weight hits hardest.
The vibe-coding series
Things to keep in mind while building with AI agents — this post is one part of the full checklist.
- Start here: the precautions overview
- Architecture for your user count
- Choosing a backend
- Security & networking
- Frontend precautions
- Spec & prompt discipline
- Reviewing AI-generated code
- Testing & verification
- Data & database precautions
- Error handling & observability
- Cost control
- Keeping it maintainable
- Deployment & platform choice
Frequently asked questions
How do I check if an npm package is safe to install?
Before accepting it, confirm the package is real and not a typosquat of a popular name, check it's actively maintained (recent releases, open issues getting answered), look at weekly download counts for popularity, and run npm audit against it. A package with no recent commits, few downloads, or a name one character off a famous library is a red flag.
Should I pin exact dependency versions or use ranges like ^1.2.0?
Pin exact versions for anything you care about reproducing or securing. A caret or tilde range lets a future install silently pull a newer release — which is exactly how a compromised version enters your build. Exact versions plus a committed lockfile mean every install, everywhere, resolves to the same bytes you reviewed.
Why does a lockfile matter for AI-generated projects?
The lockfile records the exact resolved version and integrity hash of every dependency and transitive dependency. Commit it, and your CI, your teammates, and your production build all install the identical tree you tested. Without it, 'it worked on my machine' becomes a supply-chain gamble every time someone runs install.