skip to content

How do you prevent a Maven build (and its repository manager) from silently falling back to untrusted public repositories for internal coordinates?

level: principalimportance: should knowfreq 25%

answer

  1. miss must NOT hit upstream proxy
  2. routing/exclusion rules for com.acme.**
  3. hosted-before-proxy ordering
  4. owned reverse-DNS groupId, reserve namespace
  5. managed global settings on all CI agents

basics

~20 s

Route all resolution through one trusted virtual repo (mirrorOf *), don't declare public repos in POMs, and configure the repo manager so internal groupId prefixes are never proxied from upstream. Use owned groupId namespaces so collisions can't exist publicly.

solid answer

~40 s

Defense is layered. In Maven config: use <mirrorOf>*</mirrorOf> so the build only ever contacts your virtual repo, and remove any ad-hoc <repositories> pointing at public mirrors (JitPack, random CDNs) from POMs. On the repository manager (Nexus/Artifactory): build the virtual/group repo so that internal hosted repos are ordered ahead of any proxy, and add routing/exclusion rules (e.g. content/routing rules, 'foreground'/include-exclude patterns) so internal groupId prefixes like com.acme.* are NEVER resolved from upstream proxies. Disable or tightly scope the public proxy, and consider blocking unknown coordinates entirely for internal-namespace patterns. Finally, use a groupId you actually own (reverse-DNS of a domain you control) so an attacker cannot register the same coordinate on Central at all. Combine with -C checksums and signature verification so even an accepted artifact is integrity/authenticity checked.

go deeper

for a junior

Knows builds shouldn't reach random public repos and a company repo should be the single source.

for a middle

Configures mirrorOf * and removes ad-hoc <repositories> from POMs.

for a senior

Adds repo-manager routing so internal prefixes aren't proxied and reasons about ordering vs misses.

for a principal

Designs the full policy: owned namespaces, routing/exclusion rules, managed global settings, plus checksum/signature enforcement org-wide.

## The failure mode to eliminate "Silent fallback" is when a resolution **miss** in your internal repo causes the system to ask an **upstream public repo**, which may serve an attacker's substituted artifact for an internal coordinate. The goal is to ensure internal coordinates can ONLY ever be served by trusted internal sources. ## Layer 1 — Maven client config - **Single entry point:** `<mirrorOf>*</mirrorOf>` (or `external:*`) so the build never contacts Central/JitPack/etc. directly; everything goes to your virtual repo. - **Purge rogue repositories:** audit POMs and profiles for `<repositories>`/`<pluginRepositories>` pointing at arbitrary public URLs and remove them. Mirrors override these, but removing them avoids confusion and accidental scopes. - **Lock down `settings.xml`** at the global/managed level on every CI agent so developers can't reintroduce untrusted sources. ```xml <mirror> <id>acme-virtual</id> <url>https://nexus.acme.internal/repository/maven-virtual/</url> <mirrorOf>*</mirrorOf> </mirror> ``` ## Layer 2 — Repository manager policy (the real fix) The virtual/group repository aggregates members in order; what it proxies is the decisive control: - **Ordering:** put internal **hosted** repos ahead of any **proxy** in the group so a present internal artifact always wins. - **Routing/exclusion rules:** configure rules so internal groupId prefixes (e.g. `com.acme.**`) are **never** routed to upstream proxies. In Nexus this is content/routing rules; in Artifactory it's include/exclude patterns on the remote/virtual repo. This is what actually blocks fallback — ordering alone doesn't help when the artifact is genuinely absent internally (a miss still hits the proxy). - **Scope the public proxy:** restrict the upstream proxy to non-internal namespaces, or block unknown coordinates under internal prefixes outright. ## Layer 3 — Namespace ownership The most robust prevention: use a **groupId you own**, i.e. reverse-DNS of a domain your org controls (`com.acme.*` where `acme.com` is yours and you publish under it / reserve it on Central). If the coordinate cannot be registered publicly by anyone but you, the collision the attack depends on simply cannot occur. ## Layer 4 — Verify even what you accept Even with the above, enforce **`-C` (strict checksums)** and **PGP signature verification** for critical dependencies, so a wrongly-served or tampered artifact is still rejected on integrity/authenticity grounds. ## Putting it together (policy) 1. One virtual repo via `mirrorOf *`; managed global settings on all agents. 2. Repo-manager routing so `com.acme.**` is internal-only, never proxied. 3. Owned reverse-DNS groupId; reserve the namespace publicly. 4. `-C` + signature verification in CI. This removes both the *routing* path (fallback) and the *collision* precondition (public namespace) of dependency confusion.

  • Why isn't ordering internal repos before the proxy enough on its own?
    Ordering only decides the winner when the artifact exists in multiple members. If an internal coordinate is genuinely absent internally, the miss still falls through to the proxy. You need explicit routing/exclusion rules to block upstream resolution of internal prefixes.
  • How does owning the groupId namespace eliminate the attack rather than just mitigate it?
    Dependency confusion needs a public artifact with the same coordinate. If the groupId is reverse-DNS of a domain only you can publish under (and you reserve it), no attacker can register the colliding coordinate publicly, so there's nothing to fall back to.

saying these in an interview costs you the question

  • Believing repo ordering alone prevents fallback (a true miss still proxies upstream).
  • Using a generic/unowned groupId for internal artifacts.
  • Leaving CI agents on per-user settings.xml that developers can override.

context