Keep bad packages out
Dependency confusion protection
Dependency confusion is simple and nasty. Somebody publishes a package on public npm, PyPI, NuGet or Docker Hub with the name of one of your internal packages, at a higher version, and your build picks theirs. ForgeRepo™ closes that door in two places.
Reserved names
Reserved names are the names that belong to you, for the four types you can publish to: npm scopes like @acme/*, PyPI projects or prefixes like acme-*, image namespaces like acme/*, and NuGet ids or prefixes like Acme.*. An exact name works too. PyPI names fold -, _, . and case the way PyPI does, and NuGet ids ignore case. A reserved name is never fetched from any upstream registry, not even a copy cached before it was reserved, and npm search leaves public packages under those names out. Until you publish something under a reserved name, asking for it answers 404 and says why.
Patterns, not a fallback chain
The obvious way to use more than one registry is to ask each in turn until one answers. That is exactly how dependency confusion works. ForgeRepo™ routes by pattern instead, for every package type: @acme/* can only ever come from the registry written against it, and so can com.acme:* on Maven or acme/* on Composer. RPM and APT mirrors are one address each, so there is nothing to confuse. A near miss goes to your supplier, who says 404, and the install stops. The match is case insensitive, so @ACME/thing cannot escape to the public registry either.
There is a fall back switch per registry for anybody who really wants the default asked on a 404. It is off unless you turn it on, and the docs tell you to leave it off for internal scopes.
Doing the routing here, rather than with a per scope line in every developer's .npmrc, also keeps the rules, the cache, the audit trail and the kill switch in the path for your internal packages.
In short
- Reserve exact names, scopes and prefixes for npm, PyPI, images and NuGet
- Reserved names are never fetched from outside, not even from an old cached copy
- One upstream per pattern for every type, first match wins, no silent fallback
- Case insensitive routing, so a change of case cannot escape
- Every change to the reserved list is in the audit trail
In the documentation
- Reserved names and publishing Administrator Guide
- Upstream registries and package types Administrator Guide
Goes well with
- Private npm registry npm, pnpm, Yarn and Bun. Publish your own packages under reserved names, mirror the rest.
- Typosquat detection lodahs, reqeusts, l0dash, python-numpy. Lookalike names are caught and warned about, or refused.
- Allow lists and block lists Whitelist or blacklist by name, scope, wildcard or version range, for every type. A blocked version is not even listed to the client.
One container, about two minutes
A Linux box with Docker, or one without it, and a reverse proxy for TLS. The installer does the rest and it is safe to run twice. Free, MIT licensed, nothing to sign up for.