Vibe-Code Dependency Confusion: Stop Supply Chain Attacks
OverMCP Team
Quick answer
Dependency confusion attacks exploit package managers (npm, PyPI, etc.) by tricking your app into installing a malicious package from a public registry instead of your private one. Vibe-coded apps are especially vulnerable because AI tools often generate package.json files with typos or missing scope markers, making it easy for attackers to publish similarly named packages. To prevent this, always scope your private packages, pin versions, and scan your dependencies with a tool like OverMCP's free AI app security scanner.
What is dependency confusion and why vibe-coded apps are at risk
Dependency confusion (also called a supply chain substitution attack) happens when a package manager resolves a dependency name to a public registry instead of your private one. For example, if your package.json has "my-internal-lib": "^1.0.0" but you forgot to scope it (e.g., @company/my-internal-lib), npm will look for my-internal-lib on the public npm registry. An attacker can publish a malicious package with that exact name, and your next npm install will pull it in.
Vibe-coded apps—built quickly with tools like Cursor, Bolt.new, v0, Lovable, and Replit Agent—are particularly susceptible. AI coding assistants often generate dependency lists from vague prompts, leading to:
@your-org/ before a private package name.^ or * instead of exact versions, allowing the attacker's version to be installed.What to check first
Before you deploy or even run npm install on a vibe-coded app, run through this checklist:
package.json, requirements.txt, Gemfile, etc. Look for any package name that could be confused with a public one.@myorg/package-name).^1.0.0 with 1.0.0 (no caret or tilde) for all private dependencies.package-lock.json or yarn.lock is committed and reviewed.Step-by-step fix
Here's how to secure your vibe-coded app against dependency confusion.
1. Scope all private packages
Change this:
"dependencies": {
"internal-auth": "^1.0.0"
}To this:
"dependencies": {
"@mycompany/internal-auth": "1.0.0"
}2. Configure your registry in .npmrc
Create or update .npmrc in your project root:
registry=https://registry.npmjs.org/
@mycompany:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}This tells npm to use the public registry for all packages except those scoped under @mycompany, which should be fetched from GitHub Packages.
3. Pin exact versions
Remove version ranges for private packages. Use "1.0.0" not "^1.0.0".
4. Use overrides to block public lookups
In package.json, add an overrides section to force resolution of specific packages:
"overrides": {
"internal-auth": "npm:@mycompany/internal-auth@1.0.0"
}This only works if you have control over all package names.
5. Run a pre-install script
Add a script to package.json that checks for un-scoped packages:
"scripts": {
"preinstall": "node -e \"const pkg=require('./package.json'); Object.keys(pkg.dependencies||{}).forEach(d=>{if(!d.startsWith('@')){console.error('Unscoped dependency: '+d); process.exit(1)}})\""
}6. Scan regularly with OverMCP
After fixing, set up continuous security monitoring to catch new dependency confusion risks as your app evolves.
Common mistakes
Mistake 1: Trusting AI-generated package names blindly
AI tools often invent package names or misspell them. For example, Cursor might suggest "auth-helper" when the real internal package is "@acme/auth-helper". An attacker can publish auth-helper on npm, and your next deploy pulls it in.
Fix: Always verify every dependency name against your internal registry and public registries.
Mistake 2: Using `npm install <package>` without the scope
When adding a private package manually, it's easy to forget the @scope/ prefix. Run npm install @mycompany/package-name instead.
Mistake 3: Not using a lockfile
Lockfiles ensure the exact same versions are installed every time. If you have a lockfile but didn't pin versions, an attacker could publish a newer version that gets resolved first.
Fix: Always commit package-lock.json or yarn.lock and review changes carefully.
Mistake 4: Assuming private packages are safe because they're not public
If your private package is hosted on a private registry (like npm Pro or GitHub Packages), but an attacker publishes a public package with the same name, the package manager may choose the public one if the registry configuration is wrong.
Fix: Use scopes and explicit registry configuration in .npmrc.
Mistake 5: Ignoring similar package names on public registries
Attackers often use typosquatting: my-interna-lib vs my-internal-lib. AI-generated code can introduce such typos.
Fix: Run a tool like OverMCP's security headers checker to scan your app, but also manually search for similar package names.
How OverMCP helps
OverMCP is built for indie makers who ship fast with AI. Our free AI app security scanner checks your dependencies for known vulnerabilities and dependency confusion risks. You can also connect your Vercel project to get continuous monitoring every time you deploy. No complex setup—just scan and fix.
FAQ
What is dependency confusion in simple terms?
Dependency confusion is when your app accidentally installs a malicious public package because the package manager can't tell the difference between a private package and a public one with the same name. Attackers exploit this by publishing packages with names they know you use internally.
How do I know if my vibe-coded app has a dependency confusion vulnerability?
Check your package.json for any dependency that doesn't start with a scope (like @myorg/) and that also exists on the public npm registry. If you find one, you are vulnerable. Also run a lockfile diff—if a package name changes, investigate.
Can OverMCP detect dependency confusion?
Yes. OverMCP's secret leak scanner and continuous security monitoring can flag dependencies that match public packages. We're adding dedicated dependency confusion detection soon. In the meantime, run the checklist above.