# Dependency Confusion Audit

> Audit and remediate dependency confusion vulnerabilities in package management configurations. Use this skill whenever the user mentions package managers (npm, pip, Maven, Gradle, NuGet, Go modules, Cargo, Bundler), CI/CD security, supply chain attacks, internal registries, or wants to secure their build dependencies. Also use when reviewing .npmrc, pip.conf, nuget.config, settings.xml, Gemfile, or any package configuration files. Make sure to use this skill when the user asks about securing dependencies, preventing supply chain attacks, or configuring private registries.

- Skill: `abelrguezr/dependency-confusion-audit` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/dependency-confusion-audit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/dependency-confusion-audit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/dependency-confusion-audit

---


# Dependency Confusion Audit & Remediation

This skill helps you identify and fix dependency confusion vulnerabilities in your project's package management setup.

## What is Dependency Confusion?

Dependency confusion occurs when a package manager resolves a dependency from an unintended, less-trusted registry (usually public) instead of the intended private/internal one. Attackers exploit this by publishing malicious packages with the same names as your internal dependencies to public registries.

**Common root causes:**
- **Typosquatting**: Importing `reqests` instead of `requests` (resolves from public registry)
- **Missing internal packages**: Importing `company-logging` that doesn't exist internally, so the resolver falls back to public registries
- **Version preference**: Internal `company-requests@1.0.1` vs public `company-requests@1.0.2` — resolver picks the newer public version

## Quick Audit Checklist

Run through these checks to identify vulnerabilities:

1. **Registry Configuration**: Are you mixing internal and public registries in the same resolution?
2. **Internal Namespaces**: Do you have organization-specific package names (e.g., `@company/*`, `company-*`, `Company.*`)?
3. **Fallback Behavior**: Does your tooling fall back to public registries when internal packages aren't found?
4. **Version Pinning**: Are versions pinned or locked, or do you allow wide ranges?
5. **Lockfile Integrity**: Are lockfiles committed and enforced in CI?

## Ecosystem-Specific Fixes

### JavaScript/TypeScript (npm, Yarn, pnpm)

**Problem**: npm/yarn can resolve from multiple registries and pick the "best" version.

**Fix**: Bind internal scopes to private registry only.

`.npmrc` (project-level):
```
# Bind internal scope to private registry; do not allow public fallback for @company/*
@company:registry=https://registry.corp.example/npm/
# Always authenticate to the private registry
//registry.corp.example/npm/:_authToken=${NPM_TOKEN}
strict-ssl=true
```

`package.json` (for internal package):
```json
{
  "name": "@company/api-client",
  "version": "1.2.3",
  "private": false,
  "publishConfig": {
    "registry": "https://registry.corp.example/npm/",
    "access": "restricted"
  }
}
```

Yarn Berry (`.yarnrc.yml`):
```yaml
npmScopes:
  company:
    npmRegistryServer: "https://registry.corp.example/npm/"
    npmAlwaysAuth: true
# CI should fail if lockfile would change
enableImmutableInstalls: true
```

**Operational tips:**
- Only publish internal packages within the `@company` scope
- For third-party packages, allow public registry via your private proxy/mirror, not directly from clients
- Use `npm ci` or `yarn install --immutable` in CI

### Python (pip / Poetry)

**Core rule**: Don't use `--extra-index-url` to mix trust levels. Either expose a single internal index that proxies and caches approved PyPI packages, or use explicit index selection and hash pinning.

`pip.conf`:
```
[global]
index-url = https://pypi.corp.example/simple
# Disallow source distributions when possible
only-binary = :all:
# Lock with hashes generated via pip-tools
require-hashes = true
```

Generate hashed requirements with pip-tools:
```bash
# From pyproject.toml or requirements.in
pip-compile --generate-hashes -o requirements.txt
pip install --require-hashes -r requirements.txt
```

If you must reach public PyPI, do it via your internal proxy and maintain an explicit allowlist there. Avoid `--extra-index-url` in CI.

### .NET (NuGet)

**Problem**: Multiple package sources can be queried simultaneously.

**Fix**: Use Package Source Mapping to bind patterns to specific sources.

`nuget.config`:
```xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="corp" value="https://nuget.corp.example/v3/index.json" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
    <packageSource key="corp">
      <package pattern="Company.*" />
      <package pattern="Internal.Utilities" />
    </packageSource>
  </packageSourceMapping>
</configuration>
```

### Java (Maven/Gradle)

**Problem**: POMs can declare arbitrary repositories.

**Fix**: Mirror all to internal, ban ad-hoc repos.

Maven `settings.xml`:
```xml
<settings>
  <mirrors>
    <mirror>
      <id>internal-mirror</id>
      <mirrorOf>*</mirrorOf>
      <url>https://maven.corp.example/repository/group</url>
    </mirror>
  </mirrors>
</settings>
```

Add Enforcer plugin to ban repositories in POMs:
```xml
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <version>3.6.1</version>
  <executions>
    <execution>
      <id>enforce-no-repositories</id>
      <goals><goal>enforce</goal></goals>
      <configuration>
        <rules>
          <requireNoRepositories />
        </rules>
      </configuration>
    </execution>
  </executions>
</plugin>
```

Gradle: Centralize and lock dependencies.

`settings.gradle.kts`:
```kotlin
dependencyResolutionManagement {
  repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
  repositories {
    maven { url = uri("https://maven.corp.example/repository/group") }
  }
}
```

Enable dependency verification and commit `gradle/verification-metadata.xml`.

### Go Modules

**Problem**: Public proxy can resolve private module names.

**Fix**: Mark private paths to skip proxy.

```bash
# Use corporate proxy first, then public proxy as fallback
export GOPROXY=https://goproxy.corp.example,https://proxy.golang.org
# Mark private paths to skip proxy and checksum db
export GOPRIVATE=*.corp.example.com,github.com/your-org/*
export GONOSUMDB=*.corp.example.com,github.com/your-org/*
```

### Rust (Cargo)

**Problem**: crates.io fallback for internal packages.

**Fix**: Replace crates-io with internal mirror.

`.cargo/config.toml`:
```toml
[source.crates-io]
replace-with = "corp-mirror"

[source.corp-mirror]
registry = "https://crates-mirror.corp.example/index"
```

For publishing, be explicit with `--registry` and keep credentials scoped to the target registry.

### Ruby (Bundler)

**Problem**: Multi-source Gemfiles can mix registries.

**Fix**: Use source blocks and disable multisource.

`Gemfile`:
```ruby
source "https://gems.corp.example"

source "https://rubygems.org" do
  gem "rails"
  gem "pg"
end

source "https://gems.corp.example" do
  gem "company-logging"
end
```

Enforce at config level:
```bash
bundle config set disable_multisource true
```

## CI/CD Hardening

- **Single ingress**: Use Artifactory/Nexus/CodeArtifact/GitHub Packages/Azure Artifacts as the only endpoint developers/CI can reach
- **Immutable lockfiles**: Fail CI if lockfile would change
  - npm: commit `package-lock.json`, use `npm ci`
  - Yarn: commit `yarn.lock`, use `yarn install --immutable`
  - Python: commit hashed `requirements.txt`, enforce `--require-hashes`
  - Gradle: commit `verification-metadata.xml` and fail on unknown artifacts
- **Egress control**: Block direct access from CI to public registries except via the approved proxy
- **Name reservation**: Pre-register your internal names/namespaces in public registries where supported
- **Package provenance**: When publishing public packages, enable provenance/attestations to make tampering more detectable

## When to Use This Skill

Use this skill when:
- Reviewing package configuration files (`.npmrc`, `pip.conf`, `nuget.config`, `settings.xml`, `Gemfile`, etc.)
- Setting up a new project's dependency management
- Auditing CI/CD pipelines for supply chain security
- Investigating a potential dependency confusion incident
- Wanting to harden your build environment against supply chain attacks
- Configuring private registries or internal package management

## References

- [Original Dependency Confusion Post](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610)
- [AWS CodeArtifact Vulnerability](https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d)
- [NuGet Package Source Mapping](https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping)
- [Yarn Configuration](https://yarnpkg.com/configuration/yarnrc/)

