$npx -y skills add Prohao42/aimy-skill --skill dependency-confusion--- name: dependency-confusion description: >- Supply-chain testing via package-manager dependency confusion: when internal package names resolve to attacker-controlled public registries, leading to malicious install and script execution. Use for npm/pip/gem/Maven/Composer/D
| 1 | # SKILL: Dependency Confusion — Supply Chain Attack Playbook |
| 2 | |
| 3 | > **AI LOAD INSTRUCTION**: Expert dependency-confusion methodology. Covers how private package names leak, how public registries can win version resolution, ecosystem-specific pitfalls (npm scopes, pip extra indexes, Maven repo order), recon commands, non-destructive PoC patterns (callbacks, not data exfil), and defensive controls. Pair with supply-chain recon workflows when manifests or CI caches are in scope. **Only use on systems and programs you are authorized to test.** |
| 4 | |
| 5 | ## 0. QUICK START |
| 6 | |
| 7 | **What to look for first** |
| 8 | |
| 9 | - **Manifests** listing package names that look **internal** (short unscoped names, org-specific tokens, product codenames) without a **hard-private registry lock**. |
| 10 | - Evidence the **same name** might exist—or be **squattable**—on a **public** registry with a **higher semver** than the private feed publishes. |
| 11 | - **Lockfiles** missing, stale, or not enforced in CI so `install`/`build` can drift toward public metadata. |
| 12 | |
| 13 | **Fast mental model**: *If the resolver can see both private and public indexes, and version ranges allow it, the “newest” matching version may be the attacker’s.* |
| 14 | |
| 15 | Routing note: if the task comes from supply-chain, repository exposure, or CI-build recon, first use `recon-for-sec` to list internal package names and possible public-registry collisions. |
| 16 | |
| 17 | --- |
| 18 | |
| 19 | ## 1. CORE CONCEPT |
| 20 | |
| 21 | 1. **Private packages**: An organization ships libraries only on an internal registry (or under conventions that imply “ours”), e.g. a scoped name like `@org-scope/internal-utils` or an **unscoped** name such as `acme-billing-sdk`. |
| 22 | 2. **Attacker squats the name**: The same package name is published on a **public** registry (npmjs, PyPI, RubyGems, etc.). |
| 23 | 3. **Resolver preference**: Many setups resolve **highest matching version** across **all configured indexes** (or merge metadata), so a public `9.9.9` can beat a private `1.2.3` if ranges allow. |
| 24 | 4. **Execution**: Package managers run **lifecycle scripts** (npm `preinstall`/`postinstall`, setuptools entry points, etc.) → **attacker code runs** on developer laptops, CI, or production image builds. |
| 25 | |
| 26 | This is a **supply-chain** class issue: impact is often **broad** (many consumers) and **silent** until build or runtime hooks fire. |
| 27 | |
| 28 | --- |
| 29 | |
| 30 | ## 2. AFFECTED ECOSYSTEMS |
| 31 | |
| 32 | | Ecosystem | Typical manifest | Confusion angle | |
| 33 | |-----------|------------------|-----------------| |
| 34 | | **npm** | `package.json` | **Scoped** packages (`@scope/pkg`) are **safer** when the scope is **owned** on the registry; **unscoped** private-style names are **high risk**. Multiple registries / `.npmrc` `registry` vs per-scope `@scope:registry=` misconfiguration increases risk. | |
| 35 | | **pip** | `requirements.txt`, `pyproject.toml`, `setup.py` | `pip install -i` / **`--extra-index-url`** merges indexes; a public index can serve a **higher version** for the same distribution name. | |
| 36 | | **RubyGems** | `Gemfile` | **`source`** order and additional sources; ambiguous gem names reachable from rubygems.org. | |
| 37 | | **Maven** | `pom.xml` | **Repository** declaration **order** and **mirror** settings; a public repo publishing the same `groupId:artifactId` under a higher version can win if policy allows. | |
| 38 | | **Composer** | `composer.json` | **Packagist** is default; private packages without **`repositories`**/`canonical` discipline may collide with public names. | |
| 39 | | **Docker** | `FROM`, image tags | **Typosquatting** on container registries (e.g. public hub) for images with names similar to internal base images. | |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## 3. RECONNAISSANCE |
| 44 | |
| 45 | **Where internal names leak** |
| 46 | |
| 47 | - Committed **`package.json`**, **`requirements.txt`**, **`Gemfile`**, **`pom.xml`**, **`composer.json`** in repos or forks. |
| 48 | - **JavaScript source maps**, bundled assets, or **error stack traces** referencing package paths. |
| 49 | - **`.npmrc`**, **`.pypirc`**, **CI logs** showing install URLs or mirror endpoints. |
| 50 | - **Issue trackers**, **gist snippets**, and **dependency graphs** from SBOM exports. |
| 51 | |
| 52 | **Check public squatting / claimability (read-only)** |
| 53 | |
| 54 | ```bash |
| 55 | # npm — metadata for a name (unscoped) |
| 56 | npm view some-internal-package-name version |
| 57 | |
| 58 | # npm — scoped (requires scope to exist / be readable) |
| 59 | npm view @some-scope/internal-lib versions --json |
| 60 | |
| 61 | # PyPI — dry-run style version probe (adjust name; fails if not found) |
| 62 | python3 -m pip install --dry-run 'some-internal-package-name==99.99.99' |
| 63 | |
| 64 | # RubyGems — query remote |
| 65 | gem search '^some-internal-package-name$' --remote |
| 66 | |
| 67 | # Maven Central — search coordinates (example pattern) |
| 68 | # curl "https://search.maven.org/solrsearch/select?q=g:co |