A dependency confusion tabletop starts when someone finds your private package name on a public registry. This 45 minute script tests whether you can tell which builds resolved the public copy, what it could have run, and which registry settings would have prevented it.
The mechanism in plain words
Many teams publish internal packages to a private registry under a short name. If a build tool is allowed to look at both a private and a public source, and the public one has the same name with a higher version, the tool may prefer the public copy. An attacker who learns an internal name can register it publicly. SLSA lists using a compromised dependency as a build and runtime threat (D), and the registry controls you set decide how exposed you are.
Use a fictional package, acme-billing-core, with the premise that someone published a public version 99.0.0 yesterday.
Setup
- Facilitator, scribe, a platform engineer who owns the package registry, two application developers, a security lead.
- No handouts. See what the team can find.
Injects
Inject 1, minute 5. A developer sees acme-billing-core on a public registry, published a day ago by an unknown account, with a postinstall script.
Ask:
- Who confirms this is not ours?
- Which projects list this name? How do you search lockfiles and manifests?
- Which registry does each build use, and in what order?
Inject 2, minute 18. CI logs show three builds last night fetched version 99.0.0 from the public registry.
Ask:
- What did the install script have access to on those runners?
- Were any artifacts from those builds deployed?
- Which credentials do you rotate, and which artifacts do you rebuild?
Inject 3, minute 32. Leadership asks how you will make sure it cannot happen again.
Ask:
- Can the build restrict internal names to the private source only?
- Do you reserve your internal names on the public registry or use a scoped namespace?
- Is there an alert when a new public package matches an internal name?
Prevention options to compare
| Approach | Strength | Cost |
|---|---|---|
| Scoped or namespaced internal names | Clear ownership | Rename effort |
| Registry proxy that routes internal names only to private | Strong, central | Setup and upkeep |
| Lockfiles with integrity hashes, installed from lockfile in CI | Stops new resolution | Needs discipline |
| Reserving names on public registries | Cheap defence | Easy to miss one |
Package managers differ in how they pick between sources, so confirm behaviour for each ecosystem you use in its own documentation. Our guides on Maven repository order and Composer canonical repositories show two examples.
Debrief
Write down the three most valuable fixes and put them into an after-action report. A quick win is a scan of every manifest to list internal names, which doubles as your reservation list.
Safeguard provides SBOM and dependency scanning that lists what your projects resolve, and policies and gates for new dependencies. Try a free scan.
Try it yourself
Click through it at your own pace, or open it full screen. Find where a package comes from in the inventory and ask whether yours would show the source registry.
Quick check
Is renaming internal packages worth it?
Scoped names are one of the cleaner fixes because ownership is built in, but a proxy rule can reach a similar result with less code churn.
Does a lockfile fully protect us?
It protects builds that install from it with integrity checks. Anything that resolves fresh, such as a new project or an update, still needs registry rules.
Sources
Last reviewed: 19 October 2026.