blob: 15fe34374b15ff9bbba2a10c8d5a9472d7c58424 [file] [view]
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# Apache Maven — Umbrella Threat Model (v0 DRAFT)
## §1 Header
- **Project family:** Apache Maven (build tool core, runtime, resolver, and the maintained plugin set). This is an **umbrella** threat model covering ~26 repositories across ~34 branch-targets — see §2. Individual repos/branches inherit this model except where a §2 row narrows it.
- **Modeled against:** the current `master`/maintenance branches of the in-scope repositories as of the date below. Because Maven is mid-transition from the 3.x to the 4.x runtime line, this model carries a **3.x-vs-4.x axis** as a first-class distinction rather than describing a single profile (see §2, §4, §5a, §6).
- **Date:** 2026-07-04
- **Author:** ASF Security team, drafted via the threat-model-producer (Scovetta) rubric at the Maven PMC's request (path 3 — Security team drafts, Maven PMC reviews).
- **Status:** **v0 DRAFT — for Maven PMC review.** Not yet ratified. Most claims are *(inferred)* and each carries a matching open question in §14.
- **Version binding:** the threat model is versioned alongside the projects. A report against a released Maven core / plugin version *N* is triaged against the model as it stood at *N*, not at `master` HEAD. Each branch-target (§2) binds to the releases cut from that branch. See §14 Q20.
- **Reporting cross-reference:** findings that fall under §8 (claimed properties) should be reported privately per the ASF process at <https://maven.apache.org/security.html> / <https://www.apache.org/security/>. Findings that fall under §3 (out of scope) or §9 (properties not provided) — including "a plugin executed code", "the published POM differs from the source POM", or "checksums do not authenticate the publisher" — will be closed citing this document.
- **Provenance legend:** every non-trivial claim carries exactly one tag:
- *(documented)* — stated in Maven's own docs/site; cited inline.
- *(maintainer)* — stated by a Maven PMC member in response to this process. (None yet — v0.)
- *(inferred)* — reasoned from Maven's architecture, domain knowledge, or the absence of a feature; **not yet confirmed.** Each *(inferred)* tag names the §14 question that must ratify it, e.g. *(inferred, Q5)*.
- **Draft confidence:** ~26 documented / 0 maintainer / ~59 inferred. This is a react-to-me draft, not a ratified model — the heavy *(inferred)* weighting is expected for a v0 the PMC has not yet reviewed.
**What Maven is.** Apache Maven is a build-automation and dependency-management tool for JVM projects. Given a project description in `pom.xml` (the Project Object Model), Maven resolves declared dependencies and build **plugins** from configured **repositories** into a local repository (`~/.m2/repository`), then executes a lifecycle of plugin goals — compiling, testing, packaging, signing, and deploying code. Plugins and build **extensions** are ordinary JVM artifacts that Maven downloads and executes **as arbitrary code in the build JVM**. Maven is invoked from the CLI (`mvn`, or the `mvnd` daemon, or a project-local `mvnw` wrapper) by a developer or a CI runner. Its security model is therefore fundamentally a **supply-chain and arbitrary-code-execution** model, and — by explicit design — Maven does not sandbox the code it is asked to build or the plugins it is asked to run.
---
## §2 Scope and intended use
**Primary intended use.** Building, testing, packaging, and publishing JVM software from a trusted `pom.xml` in a developer or CI environment, resolving dependencies and plugins from repositories the operator has chosen to configure and trust. *(documented — security.html: "the Maven security model assumes you trust the `pom.xml` and the code, dependencies and repositories that are used in your build".)*
**Caller roles.** Unlike a network service, Maven has no anonymous client. The roles are:
- **Build author / operator** — writes or vendors the `pom.xml`, `settings.xml`, and `.mvn/` config; chooses repositories; runs `mvn`. **Trusted** — this actor has already chosen what code to execute. *(inferred, Q10)*
- **Dependency / plugin / extension author** — a third party whose artifact is resolved into the build and executed. **Semi-trusted adversary in scope** for supply-chain threats (see §7). *(inferred, Q10)*
- **Repository / mirror operator** — serves artifacts and metadata. **Semi-trusted adversary in scope** (compromise, poisoning, MITM on plaintext transport). *(inferred, Q10)*
**The 3.x-vs-4.x axis (carried on this table).** Maven is mid-transition. Most plugin `master` branches still compile and run against the **Maven 3.9.x** API; seven "split" plugins have moved `master` to the **Maven 4** API and keep a `*-3.x` maintenance branch on the 3.9.x API. The runtime line changes the trust surface (consumer-POM transform, `mvnenc`, resolver 2.x, `mvnup` — all Maven-4-only; see §6/§9). Each branch-target is therefore tagged with the **Maven API line** it targets, and a finding is triaged against **that** line's surface.
| # | Repository / component | Branch-target(s) | Maven API line | Touches outside process | In model? |
| --- | --- | --- | --- | --- | --- |
| **Core & runtime** | | | | | |
| 1 | `maven` (core) | `master` (4.0.x); `maven-3.9.x`/3.10.x maint. | **both** | reads POM/settings/`~/.m2`, spawns plugin code, network resolve | **yes** |
| 2 | `maven-resolver` | `master` (2.x); `1.9.x` maint. | **both** | HTTP(S) transport, local-repo I/O | **yes** |
| 3 | `maven-mvnd` (daemon) | `master` | 4.x-oriented | long-lived JVM, sockets, filesystem | **yes** |
| 4 | `maven-wrapper` | `master` | line-agnostic | downloads + executes a Maven distribution | **yes** |
| 5 | `maven-build-cache-extension` | `master` | 4.x-oriented | reads/writes build-output cache | **yes** |
| **Single-line master plugins (Maven 3.9.x API)** | | | | | |
| 6 | `maven-surefire` (surefire + failsafe) | `master` | 3.x | forks test JVMs, runs test code | **yes** |
| 7 | `maven-javadoc-plugin` | `master` | 3.x | forks `javadoc`, unpacks archives | **yes** |
| 8 | `maven-dependency-plugin` | `master` | 3.x | resolves + unpacks artifacts | **yes** |
| 9 | `maven-checkstyle-plugin` | `master` | 3.x | reads source, resolves rulesets | **yes** |
| 10 | `maven-release-plugin` | `master` | 3.x | SCM writes, invokes nested Maven | **yes** |
| 11 | `maven-shade-plugin` | `master` | 3.x | rewrites/merges JAR bytecode | **yes** |
| 12 | `maven-assembly-plugin` | `master` | 3.x | reads/writes archives | **yes** |
| 13 | `maven-scm` | `master` | 3.x | invokes SCM clients (git/svn/…) | **yes** |
| 14 | `maven-site-plugin` | `master` | 3.x | renders site, resolves skins/reports | **yes** |
| 15 | `maven-enforcer` | `master` | 3.x | evaluates rules over the build | **yes** |
| 16 | `maven-archetype` | `master` | 3.x | scaffolds projects from templates | **yes** |
| 17 | `maven-resolver-ant-tasks` | `master` | 3.x | Ant-side resolution/transport | **yes** |
| 18 | `maven-indexer` | `master` | 3.x | parses repository index metadata | **yes** |
| 19 | `maven-war-plugin` | `master` | 3.x | assembles WAR archives (overlays, resource filtering) | **yes** |
| 20 | `maven-ear-plugin` | `master` | 3.x | assembles EAR archives from module artifacts | **yes** |
| **Split plugins — master on Maven 4 API + `*-3.x` maintenance** | | | | | |
| 21 | `maven-compiler-plugin` | `master` (M4); `*-3.x` | **both (two targets)** | forks/embeds compiler, reads source | **yes** |
| 22 | `maven-jar-plugin` | `master` (M4); `*-3.x` | **both** | writes JARs | **yes** |
| 23 | `maven-clean-plugin` | `master` (M4); `*-3.x` | **both** | deletes filesystem paths | **yes** |
| 24 | `maven-deploy-plugin` | `master` (M4); `*-3.x` | **both** | uploads artifacts to remote repo | **yes** |
| 25 | `maven-install-plugin` | `master` (M4); `*-3.x` | **both** | writes to local repo | **yes** |
| 26 | `maven-resources-plugin` | `master` (M4); `*-3.x` | **both** | copies/filters resource files | **yes** |
| 27 | `maven-source-plugin` | `master` (M4); `*-3.x` | **both** | packages source JARs | **yes** |
| **Shared libraries (build/release path; no standalone CLI)** | | | | | |
| 28 | `maven-filtering` | `master` | line-agnostic | interpolates `${...}` into resource files (used by resources/war plugins) | **yes** |
| 29 | `maven-shared-utils` | `master` | line-agnostic | shared IO / process-exec / CLI helpers used across plugins | **yes** |
| 30 | `maven-archiver` | `master` | line-agnostic | shared archive + JAR/WAR manifest assembly used by packaging plugins | **yes** |
| 31 | `maven-dependency-analyzer` | `master` | line-agnostic | bytecode analysis of used-vs-declared dependencies (engine behind `dependency:analyze`) | **yes** |
| 32 | `maven-shared-io` | `master` | line-agnostic | shared file/resource IO helpers; low activity, likely near-defunct but still resolved by some plugins | **yes** |
Counting the two targets each for rows 1, 2, and 21–27 yields ~41 branch-targets across ~32 repositories. `mvnup` and `mvnenc` ship inside the Maven 4 core distribution (row 1, `master`) and are in model. See §14 Q3.
---
## §3 Out of scope (explicit non-goals)
- **Parent POMs** (`maven-parent`, `apache` parent, plugin/plugins parents) — configuration aggregation, no runtime surface of their own. *(inferred, Q2)*
- **`maven-studies`** — experimental/incubating code, not a supported product. *(inferred, Q2)*
- **SVN mirrors** of Git repositories — read-only mirrors, no independent surface. *(inferred, Q2)*
- **CI / build infrastructure** (Jenkins jobs, GitHub Actions, ASF Infra) — SDLC/build-hygiene, out of layer per the rubric. *(inferred, Q2)*
- **Project website, skins, and documentation content** (`maven-site`, Fluido/skin projects) — presentation, not the tool. *(inferred, Q2)*
- **Building untrusted code without operator-provided isolation.** Maven does not aim to safely build code it has been told to build. *(documented — security.html: "If you want to use Maven to build untrusted code, it is up to you to provide the required isolation.")*
- **The security of the artifacts a build produces.** Whether *your* code is vulnerable is your project's concern, not Maven's. *(inferred, Q2)*
- **Build/release/SDLC hygiene** of the Maven repos themselves (action pinning, reproducible builds, 2FA) — out of scope per the threat-model rubric.
---
## §4 Trust boundaries and data flow
Maven is not a network service; it is a **local process that pulls remote inputs and executes them.** The meaningful trust transitions are:
1. **Local config → build JVM (trusted → trusted).** `pom.xml`, `settings.xml`, `.mvn/extensions.xml`, `.mvn/maven.config`, and the existing contents of `~/.m2/repository` are treated as **already-authorized by the operator**. Malicious content here is out of model — the operator supplying it has already won. *(inferred, Q5)* The exception the operator must understand: cloning and building an **untrusted** third-party project makes that project's `pom.xml`/`.mvn/` a trusted input to Maven even though the operator did not author it (see §11). *(inferred, Q10)*
2. **Remote repository → local repository (semi-trusted → trusted-on-arrival).** This is the **primary security boundary.** Artifacts and metadata arrive over resolver transport from repositories/mirrors. Maven verifies **transport integrity** (checksums) but does **not**, in core, verify **publisher authenticity** (PGP signatures) — see §8/§9. Once an artifact lands in `~/.m2/repository` it is thereafter treated as trusted local input (transition 1). A poisoned local repo is out of model; poisoning it *over the wire* is in model. *(inferred, Q4, Q5, Q6, Q11)*
3. **Resolved plugin/extension → arbitrary code execution (BY DESIGN).** A resolved plugin, build extension, or `${...}`-driven lifecycle binding runs as arbitrary JVM code in the build. There is **no trust boundary here** — crossing from "declared in POM" to "executing" is the intended behavior, not a violation. *(documented — security.html trust statement.)*
**Maven-4-only transitions (in model only for 4.x branch-targets):**
- **Build POM → consumer POM transform.** At `install`/`deploy`, Maven 4 rewrites the source `pom.xml` into a flattened **consumer POM** that is what downstream actually resolves. The **published POM deliberately differs from the source POM.** *(documented — whatsnewinmaven4: consumer POM is flattened, drops parent references, flattens BOM imports, keeps only compile/runtime transitive deps.)* See §9 / §11a / Q7.
- **`mvnup` → source `pom.xml` rewrite.** The Maven 4 upgrade tool **writes back into the user's `pom.xml`** in place. *(documented — whatsnewinmaven4.)* The write target (the source tree) is a trusted output location; the transform inputs are the trusted POM. *(inferred, Q8)* See Q8.
- **`mvnenc` → encrypted `settings-security`/vault.** Maven 4 reworks settings encryption from Maven 3's obfuscation to real encryption with optional external vault. *(documented — whatsnewinmaven4.)* Key/vault material is outside Maven's boundary (§10). *(inferred, Q9)*
**Reachability precondition (the triager's first test).** A finding is in-model only if it is reachable from a **remote-repository-supplied** artifact/metadata byte (transition 2), from **resolver transport**, or from the **Maven-4 transform surfaces** above — *without* first assuming the operator supplied malicious local config or a pre-poisoned `~/.m2`. A finding that requires attacker control of `pom.xml`/`settings.xml`/`~/.m2` on a build the operator authored is `OUT-OF-MODEL: trusted-input` (§6, §13).
---
## §5 Assumptions about the environment
- **Runtime.** A JVM. Maven 4 core requires **Java 17+**; the 3.x line runs on older JDKs. *(documented — whatsnewinmaven4.)*
- **Operator-controlled machine.** Maven assumes it runs on a host the operator controls; local filesystem, environment variables, and `~/.m2` are trusted. *(inferred, Q5)*
- **Network.** Maven reaches repositories over operator-configured URLs. Plaintext `http://` external repositories are treated as untrusted and are **blocked by default** in the shipped `conf/settings.xml` via the `external:http:*` mirror / `<blocked>` mechanism. *(documented — security.html, CVE-2021-26291 mitigation.)*
- **Clock / entropy.** Not security-load-bearing in core. *(inferred, Q2)*
- **What Maven does to its host (side-effect inventory).** Maven **does** write to `~/.m2/repository` and the project `target/`, open network connections to configured repositories, fork child JVMs/processes (plugins, test runners, compilers, SCM clients), and read environment variables and `settings.xml` (which may contain **plaintext or obfuscated credentials**). `mvnd` additionally runs a **long-lived daemon JVM** reused across builds. These are all intended. *(inferred, Q5, Q12)*
## §5a Build-time and configuration variants (the knobs that move the model)
| Knob | Default | Effect on model | Maintainer stance |
| --- | --- | --- | --- |
| Resolver **checksum policy** (`-C`/`--strict-checksums` vs `-c`/`--lax-checksums`; `checksumPolicy`) | `warn` (lax) | `warn` logs but does **not** fail on checksum mismatch → integrity not enforced by default | to confirm — Q4 |
| PGP **signature verification** | **off** in core (no signature check) | authenticity of publisher unverified unless an extension is added | to confirm — Q6 |
| `maven.consumer.pom.flatten` (M4) | on for install/deploy | published POM ≠ source POM | to confirm — Q7 |
| `settings.xml` credential encryption | Maven 3: obfuscation; Maven 4: `mvnenc` real encryption | master-password/vault protects at-rest creds | to confirm — Q9 |
| `external:http:*` repo blocking | **on** (shipped `settings.xml`) | plaintext external repos rejected | documented (security.html) |
| `mvnd` daemon reuse | on when invoked as `mvnd` | cross-build JVM state reuse | to confirm — Q12 |
**Insecure-default question (Q4, wave 1).** Resolver's default checksum policy is `warn`, not `fail`. Is `warn` the **supported production posture** (so "a corrupted artifact was accepted with only a warning" is `BY-DESIGN`/downstream-responsibility), or is `fail`/`--strict-checksums` the intended posture for anything trust-sensitive (making a silent-accept report `VALID-HARDENING`)? This reshapes §8, §9, §10, §11a, §13 at once.
---
## §6 Assumptions about inputs
Maven's inputs split cleanly into **operator-supplied (trusted)** and **repository-supplied (semi-trusted)**. Where the trust posture differs between the 3.x and 4.x lines, the table has a `Line` column.
| Input | Line | Origin | Attacker-controllable? | Caller/operator must enforce |
| --- | --- | --- | --- | --- |
| `pom.xml` (source/build POM) | both | operator's project | **no** — trusted local config | don't build untrusted projects unsandboxed (§10) *(inferred, Q5)* |
| `settings.xml` (+ `settings-security`) | both | operator `~/.m2` | **no** — trusted; holds credentials | protect the file / key material *(inferred, Q5, Q9)* |
| `.mvn/extensions.xml`, `.mvn/maven.config` | both | operator project | **no** — trusted; loads extensions as code | same as `pom.xml` *(inferred, Q5)* |
| Existing `~/.m2/repository` contents | both | prior builds | **no** — trusted-on-arrival | don't share a poisoned local repo *(inferred, Q11)* |
| **Resolved dependency/plugin/extension bytes** | both | remote repo/mirror | **yes** | checksum policy; add signature verification; pin versions *(inferred, Q4, Q6)* |
| **Repository metadata** (`maven-metadata.xml`, checksums, index) | both | remote repo/mirror | **yes** | resolver parses these before trust is established *(inferred, Q4, Q18)* |
| Resolver **transport** (HTTP responses, redirects, TLS) | both | network/mirror | **yes** | HTTPS; block plaintext external repos *(documented — security.html)* |
| Archive contents unpacked by plugins (zip/tar/jar) | both | remote artifact | **yes** | path-traversal (`zip-slip`) surface in unpack plugins *(documented — security.html Plexus Archiver)* |
| **Consumer-POM transform input** (`mvn deploy`) | **4.x only** | operator project → published | source **no**; the *published* result is a new surface for downstream | verify what you publish matches intent *(inferred, Q7)* |
| **`mvnup` rewrite input** | **4.x only** | operator `pom.xml` | **no** — trusted, but written back in place | review the diff `mvnup` produces *(inferred, Q8)* |
| **`mvnenc` secrets / vault** | **4.x only** | operator | **no** — trusted | manage the master key/vault (§10) *(inferred, Q9)* |
**Size/shape.** Maven imposes no general bound on POM size, dependency-graph depth, or artifact size; resolution of a hostile dependency graph (deep transitive fan-out, decompression of hostile archives) is a resource surface. *(inferred, Q17)*
---
## §7 Adversary model
**In scope:**
- **Malicious dependency, plugin, or extension author** whose artifact is resolved into a build. Capability: run arbitrary code in the build JVM (that part is by design — §9), *and* attempt to exploit resolver/plugin parsing (archive path traversal, metadata parsing) **before/around** that execution, or poison other builds via the shared local repo. *(inferred, Q10)*
- **Compromised or malicious repository / mirror operator.** Capability: serve tampered artifacts, tampered metadata/checksums, or malicious redirects. Bounded by checksum/signature posture (§5a) and HTTPS. *(inferred, Q10)*
- **Network attacker (MITM)** on any plaintext (`http://`) transport the operator has not blocked. *(documented — security.html, CVE-2021-26291/CVE-2013-0253/CVE-2012-6153.)*
**Explicitly out of scope:**
- **The operator running the build**, and anyone with write access to the operator's `pom.xml`, `settings.xml`, `.mvn/`, or `~/.m2`. They have already chosen the code that runs. *(documented — security.html trust statement.)*
- **A local co-tenant with filesystem access** to `~/.m2` or the source tree. *(inferred, Q5)*
- **The author of the project being built**, when the operator has chosen to build that project (see §11 for the untrusted-project misuse). *(inferred, Q10)*
---
## §8 Security properties the project provides
For each: property → violation symptom → severity → provenance.
1. **Transport integrity via checksums.** For each resolved artifact, resolver compares the downloaded bytes against the repository's published checksum. *Symptom of break:* a tampered artifact is accepted as valid. *Severity:* **high** (but note the default policy only **warns** — §5a/Q4). *(inferred, Q4)*
2. **Plaintext-external-repo rejection.** The shipped `conf/settings.xml` blocks `external:http:*` so a plaintext external mirror cannot silently substitute artifacts. *Symptom:* a MITM downgrades resolution to plaintext and substitutes bytes. *Severity:* **high.** *(documented — security.html.)*
3. **Credential-at-rest protection (`mvnenc`, Maven 4).** Server credentials in `settings.xml` can be encrypted (real encryption in 4.x; obfuscation only in 3.x). *Symptom:* plaintext credential recovery from `settings.xml`. *Severity:* **medium** (3.x is obfuscation, not a confidentiality guarantee — §9). *(documented — whatsnewinmaven4.)*
4. **Consumer-POM minimization (Maven 4).** The published consumer POM omits build-internal detail (internal plugin config, non-inherited structure), reducing accidental exposure of internal repository URLs/config to downstream. *Symptom:* internal config leaks into the published POM. *Severity:* **low–medium.** *(documented — whatsnewinmaven4.)*
5. **Resolver API encapsulation (Maven 4).** Resolver 2.x is hidden behind the new Maven API; plugins no longer call it directly, shrinking the plugin-facing transport attack surface. *Symptom:* a plugin drives transport in an unsafe way. *Severity:* **low.** *(documented — whatsnewinmaven4.)*
6. **JVM memory safety.** Maven is managed JVM code; classic memory-corruption (OOB, UAF) is not the threat class. *Symptom:* JVM crash/`OutOfMemoryError` — a robustness bug, not memory unsafety. *Severity:* **correctness-only** unless it enables the above. *(inferred, Q2)*
**No resource-exhaustion guarantee is made.** Maven does not bound POM size, transitive-graph size, archive decompression, or plugin CPU/memory. A hostile dependency graph or archive that exhausts memory/CPU is **not** a §8 violation. *(inferred, Q17)* See §9.
---
## §9 Security properties the project does *not* provide
This is the load-bearing section for triage. State each plainly.
- **No build/plugin sandbox — arbitrary code execution during a build is BY DESIGN.** Maven executes the `pom.xml` it is given, which "commonly includes compiling and running the associated code and using plugins and dependencies". A plugin, extension, test, or lifecycle binding running arbitrary code is **the intended behavior, not a vulnerability.** *(documented — security.html.)* **A scanner reporting "plugin/extension/test executes arbitrary code" is a `BY-DESIGN` non-finding, not a vulnerability.** See §11a.
- **No publisher-authenticity verification in core.** Checksums prove *integrity in transit*, not *who published the artifact*. Maven core/resolver does **not** verify PGP signatures of dependencies or plugins by default; a correctly-checksummed but attacker-published artifact is accepted. Signature verification requires an add-on extension the operator installs. *(inferred, Q6)* **False friend:** a checksum is not a signature and not a MAC — it authenticates nothing about origin.
- **No defense against a poisoned local repository.** Once bytes are in `~/.m2/repository`, they are trusted. Maven does not re-validate them against a remote source on each build. *(inferred, Q11)*
- **Maven 3 credential encryption is obfuscation, not encryption.** The 3.x master-password scheme does not provide confidentiality against an attacker who reads both `settings.xml` and `settings-security.xml`. Use `mvnenc` (4.x). *(documented — whatsnewinmaven4: Maven 3's scheme "more accurately called password obfuscation".)*
- **The published (consumer) POM is deliberately not byte-identical to the source POM (Maven 4).** The transform is a feature. **A finding of the form "the deployed POM does not match the repo `pom.xml`" is `BY-DESIGN`, not tampering.** *(documented — whatsnewinmaven4.)* See §11a.
- **No resource-exhaustion / DoS defense.** No bound on transitive-dependency fan-out, POM/XML entity expansion, or hostile-archive decompression ("zip/tar bombs"). *(inferred, Q17)* Well-known classes Maven leaves to the operator/ecosystem: **dependency-confusion** (name-squatting across repositories), **typosquatting**, **XML entity expansion in POM/metadata parsing**, and **archive path traversal on unpack** (the Plexus Archiver class — *documented*).
- **No guarantee that `mvnup`'s rewrite is safe to apply unreviewed** — it rewrites your `pom.xml`; the operator must review the diff. *(inferred, Q8)*
- **No cross-build isolation for `mvnd`.** The daemon reuses a JVM across builds; it is not an isolation boundary between projects built by the same daemon. *(inferred, Q12)*
- **No integrity guarantee for shared build caches.** `maven-build-cache-extension` keys outputs by hashes; a shared/remote cache is only as trustworthy as its writers. *(inferred, Q14)*
- **No trust anchor in `maven-wrapper`.** `mvnw` downloads and executes whatever Maven distribution `distributionUrl` names; checksum pinning (`distributionSha256Sum`) is optional and operator-supplied. *(inferred, Q13)*
---
## §10 Downstream (operator) responsibilities
For Maven, "downstream" is the **build author / operator / CI owner.**
- **Only build code and POMs you trust** — or provide OS/container isolation yourself. *(documented — security.html.)*
- **Curate your repositories and mirrors.** Prefer HTTPS; keep the shipped `external:http:*` block; use a controlled proxy/mirror rather than arbitrary third-party repos. *(documented — security.html.)*
- **Choose your checksum/signature posture deliberately.** Enable `--strict-checksums` and add signature-verification tooling if your threat model needs publisher authenticity. *(inferred, Q4, Q6)*
- **Pin versions** (avoid version ranges / `LATEST`/`RELEASE` for plugins and dependencies) to reduce dependency-confusion and surprise-upgrade risk. *(inferred, Q17)*
- **Protect `settings.xml` and key material.** Use `mvnenc` (4.x); manage the master password / external vault; never commit credentials. *(inferred, Q9)*
- **Review `mvnup` diffs** before committing the rewritten `pom.xml`. *(inferred, Q8)*
- **Pin the wrapper** (`distributionSha256Sum`, `wrapperSha256Sum`) if you commit `mvnw` into a repo others run. *(inferred, Q13)*
- **Treat shared build caches as a trust boundary** — sign/authenticate cache writers. *(inferred, Q14)*
---
## §11 Known misuse patterns
- **Building an untrusted third-party project unsandboxed.** Cloning a random repo and running `mvn ...` (or opening it in an IDE that auto-imports) executes that project's plugins/extensions as you. *What to do instead:* build unknown projects only inside a disposable container/VM. *(inferred, Q10)*
- **Adding an untrusted `http://` repository or disabling the `external:http:*` block.** Reopens the MITM/substitution vector CVE-2021-26291 closed. *(documented — security.html.)*
- **Relying on Maven 3 password "encryption" as confidentiality.** It is obfuscation; an attacker with both files recovers the secret. *(documented — whatsnewinmaven4.)*
- **Assuming checksums authenticate the publisher.** They only detect transit corruption. *(inferred, Q6)*
- **Running `mvnd` across mutually-distrusting projects and assuming isolation.** The daemon JVM is shared. *(inferred, Q12)*
- **Committing a `mvnw` wrapper with an unpinned `distributionUrl`.** Anyone who moves/poisons that URL controls the Maven that runs. *(inferred, Q13)*
## §11a Known non-findings (recurring false positives)
Feed this list to scanners as a suppression / negative prompt.
- **"A Maven plugin / build extension / test / lifecycle goal executes arbitrary code."** `BY-DESIGN` per §9 — this is Maven's core function, not a vulnerability. Applies to `exec`-style goals, code-generating plugins, `surefire`-forked test code, `shade` bytecode rewriting, etc. *(documented — security.html.)*
- **"The deployed/published POM differs from the source `pom.xml`."** `BY-DESIGN` per §9 (Maven 4 consumer-POM transform). Not tampering. *(documented — whatsnewinmaven4.)*
- **Dependabot / SCA alerts on `test`-scope, `provided`-scope, or integration-test-only dependencies of the Maven repos themselves.** These do not ship in released Maven artifacts and are not reachable in a consuming build → `OUT-OF-MODEL: trusted-input`/unsupported-surface for the Maven product. *(inferred, Q17)*
- **Alerts on transitive dependencies pulled only by the build/test harness** (not by the plugin/core runtime artifact). Same routing. *(inferred, Q17)*
- **"`settings.xml` contains a credential."** Expected — that is what `settings.xml` is for; confidentiality is `mvnenc`'s job at rest, not a finding against the file's existence. *(inferred, Q9)*
- **"Checksum policy defaults to warn, not fail."** Routed by the Q4 ruling — pending confirmation this is `BY-DESIGN`/downstream, not a bug. *(inferred, Q4)*
- **"Maven downloads and runs code from the internet."** `BY-DESIGN` — the resolve-and-execute model per §4/§9. *(documented — security.html.)*
---
## §12 Conditions that would change this model
- Core adopts **default publisher-authenticity verification** (signature checking on by default) — rewrites §8/§9.
- The resolver **default checksum policy** changes from `warn` to `fail` — flips Q4's disposition.
- A new **Maven-4 transform** or tool that reads remote/untrusted input into a rewrite (beyond `mvnup`/consumer-POM).
- The **3.x line reaches EOL** or a split plugin drops its `*-3.x` branch — removes a branch-target column.
- `mvnd` or `build-cache-extension` gains a **network-shared** default surface (remote daemon, remote cache) — adds a network adversary.
- **Any finding that cannot be routed to a §13 disposition** — that is a `MODEL-GAP` and the model must be revised (add the property to §8/§9), not decided ad hoc.
## §13 Triage dispositions
| Disposition | Meaning | Licensed by |
| --- | --- | --- |
| `VALID` | Violates a claimed property via a repo/mirror/transport-controlled input or a Maven-4 transform surface. | §8, §6, §7 |
| `VALID-HARDENING` | No §8 property broken, but a §11 misuse is easy enough that the project elects to harden (e.g. tighten a default). | §11, §5a |
| `OUT-OF-MODEL: trusted-input` | Requires attacker control of `pom.xml`/`settings.xml`/`.mvn/`/`~/.m2` on an operator-authored build. | §6 |
| `OUT-OF-MODEL: adversary-not-in-scope` | Requires the operator, a local co-tenant, or the built project's author (when the operator chose to build it). | §7 |
| `OUT-OF-MODEL: unsupported-component` | Lands in parent POMs, `maven-studies`, site/skins, SVN mirrors, or CI infra. | §3 |
| `OUT-OF-MODEL: non-default-build` | Only manifests when a §5a default is flipped to the less-safe value (e.g. `external:http:*` block removed). | §5a |
| `BY-DESIGN: property-disclaimed` | Plugin/extension/test code execution; published-POM ≠ source-POM; checksum ≠ signature; resolve-and-run. | §9 |
| `KNOWN-NON-FINDING` | Matches a §11a entry (test-scope SCA alerts, `settings.xml`-holds-a-credential, etc.). | §11a |
| `MODEL-GAP` | Cannot be routed above — triggers a model revision. | §12 |
---
## §14 Open questions for the maintainers
Each *(inferred)* tag in the body routes to one question below. Every question states a **proposed answer** to confirm/correct. Grouped in waves.
**Wave 1 — scope & the 3.x/4.x axis (shapes everything else):**
- **Q1.** We treat the **3.x-vs-4.x split** as the primary axis: a finding is triaged against the Maven API line of the specific branch-target, and Maven-4-only surfaces (consumer POM, `mvnenc`, resolver 2.x, `mvnup`) are out of model for 3.x targets. *Proposed: correct.* (→ §2, §4, §6)
- **Q2.** Out-of-scope set — parent POMs, `maven-studies`, SVN mirrors, CI/infra, site/skins, and "the security of the artifacts your build produces" — is right and complete. *Proposed: correct.* (→ §3)
- **Q3.** `mvnup` and `mvnenc` ship inside the Maven 4 core distribution (repo #1 `master`) and are in model as part of core, not as separate products. *Proposed: correct.* (→ §2)
- **Q4.** *(Insecure-default.)* Resolver's default checksum policy is `warn`, not `fail`. Is `warn` the supported posture (silent-accept-on-mismatch = `BY-DESIGN`/downstream), or is `--strict-checksums` intended for trust-sensitive use (silent-accept = `VALID-HARDENING`)? (→ §5a, §8, §9, §11a)
**Wave 2 — inputs, trust, and the Maven-4 surfaces:**
- **Q5.** `pom.xml`, `settings.xml`, `.mvn/` config, and existing `~/.m2` contents are **trusted local inputs**; findings requiring attacker control of them (on an operator-authored build) are out of model. *Proposed: correct.* (→ §4, §6)
- **Q6.** Maven core/resolver does **not** verify PGP publisher signatures by default; authenticity verification is an operator-installed extension. *Proposed: correct — this is a disclaimed property (§9), and "no signature check" is not a core finding.* (→ §8, §9)
- **Q7.** The Maven-4 **consumer/build-POM transform** is by design; "published POM ≠ source POM" is a `BY-DESIGN` non-finding. Are there transform behaviors (e.g. what the consumer POM *retains*) that you *would* consider security-relevant? *Proposed: transform itself is by design; retention of internal repo URLs would be the concern.* (→ §9, §11a)
- **Q8.** `mvnup` rewrites the operator's `pom.xml` in place from trusted inputs; the operator is expected to review the diff. Does `mvnup` ever read **untrusted/remote** input into that rewrite (which would make it an in-model input surface)? *Proposed: inputs are trusted; operator reviews the diff.* (→ §4, §6, §9, §10)
- **Q9.** `mvnenc` provides at-rest credential encryption; master-key/vault management is a downstream responsibility, and "`settings.xml` contains a credential" is not a finding. *Proposed: correct.* (→ §5a, §9, §10, §11a)
**Wave 3 — adversary, resolver, runtime components:**
- **Q10.** Adversary model — in scope: malicious dependency/plugin/extension author, compromised repo/mirror, plaintext-transport MITM; out of scope: operator, local co-tenant, and the built project's author when the operator chose to build it. *Proposed: correct.* (→ §2, §7, §11)
- **Q11.** A **pre-poisoned local repository** (`~/.m2/repository`) is out of model (trusted-on-arrival); poisoning it *over the wire* during resolution is in model. *Proposed: correct.* (→ §4, §9)
- **Q12.** `mvnd` is **not** an isolation boundary between projects built by the same daemon; cross-build state isolation is not a claimed property. *Proposed: correct.* (→ §5, §9, §11)
- **Q13.** `maven-wrapper` executes whatever `distributionUrl` names; checksum pinning is optional/operator-supplied and there is no built-in trust anchor. *Proposed: correct — disclaimed.* (→ §9, §10, §11)
- **Q14.** `maven-build-cache-extension` makes **no integrity guarantee for shared/remote caches**; authenticating cache writers is a downstream responsibility. *Proposed: correct.* (→ §9, §10)
**Wave 4 — plugin branch-targets & non-findings:**
- **Q15.** For the seven **split plugins**, the `*-3.x` branch targets the Maven 3.9 API and shares most code with `master`; a finding present in shared code applies to **both** branch-targets, while a finding in Maven-4-only master code applies only to the 4.x target. *Proposed: correct.* (→ §2)
- **Q16.** The 13 **single-line `master` plugins** require Maven **3.9.x** at runtime despite living on `master` (no `*-3.x` branch needed). *Proposed: correct.* (→ §2)
- **Q17.** Known non-findings for **SCA/Dependabot noise** — `test`/`provided`/IT-only and build-harness-only transitive deps of the Maven repos are not reachable in the shipped product → out of model; and no resource-exhaustion (dependency-graph/XML-entity/archive-bomb) guarantee is made. *Proposed: correct and representative.* (→ §6, §8, §9, §11a)
- **Q18.** `maven-resolver-ant-tasks` and `maven-indexer` inherit the same repo-supplied-metadata trust posture as core resolver (index/metadata parsing is an in-model input). *Proposed: correct.* (→ §2, §6)
**Wave 5 — meta / process:**
- **Q19.** **There is no `SECURITY.md` in `apache/maven` `master`** (verified 404); the documented model lives on `maven.apache.org/security.html`. For the scan to mechanically discover this model, where should the canonical `THREAT_MODEL.md` live (per-repo vs one umbrella), and can an `AGENTS.md → SECURITY.md → THREAT_MODEL.md` discoverability chain be added? *Proposed: one umbrella model, linked from each repo's `SECURITY.md`/`AGENTS.md`.* (→ §1)
- **Q20.** **Version binding** — each branch-target's model binds to the releases cut from that branch; a report against Maven core 3.9.x is triaged against the 3.x profile, not the 4.x `master` surface. *Proposed: correct.* (→ §1)
---
## §15 Machine-readable companion
Deferred for v0. Once the PMC ratifies, emit a `threat-model.yaml` sidecar indexing: branch-target → Maven API line (§2); entry point → parameter trust (§6); §5a knobs + defaults; §8 properties (symptom + severity); §9 disclaimed properties + false friends; §11a non-findings; §13 dispositions. The prose remains canonical.