Output, transport and consumer POM robustness fixes - #12946
Conversation
5279b64 to
c43dbd8
Compare
gnodet
left a comment
There was a problem hiding this comment.
Well-structured multi-topic robustness PR with genuine security and correctness improvements. All changes are well-tested with both unit and integration tests. A few minor observations:
Minor observations:
-
Style (
MavenITgh10210SettingsXmlDecryptTest.java:77):assertTrue(!log.contains(...))→assertFalse(log.contains(...))for readability and better failure messages. -
Logging level (
DefaultConsumerPomBuilder.java:602):LOGGER.warn()for every retained repository will fire in every build that declares non-central repositories, even when the user is doing everything correctly. Consider usingINFOorDEBUGfor retained repositories and reservingWARNfor dropped repositories. -
Duplication (
DefaultMirrorSelector.java/MavenRepositorySystem.java): The regex patternURL_PROTOCOL_AND_HOSTand theisExternalRepo/isExternalHttpRepo/isLocalmethods are duplicated verbatim between the compat and core modules. A shared utility could reduce maintenance burden, though the duplication is understandable given the compat module's deprecated status. -
Duplication (
AbstractMavenTransferListener): Thesanitize()method is duplicated between the impl and compat versions of this class. Consider extracting to a shared utility.
Technical notes (for other reviewers):
- The fail-closed change (
return trueon URL parse failure) inisExternalRepo/isExternalHttpRepois the right security posture — unparseable URLs will now be subject toexternal:*andexternal:http:*mirror rules (including the default HTTP blocker). - The transport URI slash normalization fixes a subtle bug where
URI.resolve("foo")againsthttp://example.com/reporesolves tohttp://example.com/fooinstead ofhttp://example.com/repo/foo, and prevents prefix-match false positives (e.g.repo-othermatchingrepo). - The
DecryptingSettingsTransformerrefactoring withwithReferenceproperly tracks context through nested calls and restores state infinallyblocks. The reference equality checknewVal != entry.getValue()is intentional and correct. - The consumer POM repository sanitization correctly collects declared repository IDs from both model-level and profile-level, with a proper opt-out via
maven.consumer.pom.sanitizeRepositories=false. - The mvnup remote-resolution gate correctly identifies offline mode, redirecting mirrors, and active proxies as scenarios where standalone resolution cannot honor operator settings.
- The possessive quantifier
{2,}+in the URL regex prevents ReDoS.
🤖 This review was generated by ForgeBot.
37f8cb4 to
9038987
Compare
Repository-supplied strings (checksum mismatch messages, resource names, repository ids/urls) were written straight to the console or logger without any filtering of control characters. Add a small sanitize() helper to AbstractMavenTransferListener (impl/maven-cli and the deprecated compat/maven-embedder copy) that replaces C0 controls other than tab/newline, DEL, and C1 controls with visible \uXXXX escapes, and apply it everywhere these listeners print remote-derived text, so terminal output always reflects what was actually printed.
Normalize the repository base URI to end with a slash before resolving a relative get/put location, and centralize the containment check in a shared helper. This keeps URI.resolve() on a whole path segment boundary and makes get/put behave consistently regardless of whether the configured repository URL carries a trailing slash.
isExternalRepo/isExternalHttpRepo parsed the repository URL with java.net.URL, which has no stream handler for the dav/dav:http/dav+http schemes wagon-webdav speaks and throws for any other unparseable URL; both methods caught the exception and returned false, so such a repository was classified as neither external nor external-http and silently skipped the external:*/external:http:* mirror rules, including the shipped external:http:* blocker. Parse the scheme and host textually instead, the way the resolver's own mirror matcher does, so these URLs are classified and matched like any other.
The consumer POM's <repositories> section previously published every effective-model repository except 'central', including repositories that only reached the effective model through parent-POM inheritance or an active settings.xml profile. Restrict publication to repository ids declared in the project's own raw POM (model or profiles); everything else is dropped, with a WARN naming each dropped and each retained repository so operators can see what a deploy will publish. The new user property maven.consumer.pom.sanitizeRepositories (default true) restores the previous behavior when set to false.
The existing regression tests for consumer-POM repository scoping only checked the intermediate Model object returned by transformNonPom(). Add a test that builds a bom-packaged project through the real public PomBuilder entry point and serializes the result with the same MavenStaxWriter the production ConsumerPomArtifactTransformer uses, so the assertion is against the actual generated POM XML.
mvnup's compatibility and plugin-upgrade strategies built effective models with a standalone session that always remapped user.home to a test directory and hardcoded Maven Central plus an Apache snapshots repository, ignoring the operator's real settings.xml (mirrors, proxies, offline mode, blocked repositories) entirely. ApiRunner.createSession() gains an isolateUserHome flag (existing callers keep isolation; production callers can opt out). mvnup now builds its session against the operator's real settings and local repository when settings were loaded, drops the hardcoded snapshots repository, and skips remote-model-dependent analysis with a warning when the effective settings declare a mirror, proxy or offline mode the standalone resolver cannot honor, instead of resolving around it.
isExternalRepo/isExternalHttpRepo in the legacy DefaultMirrorSelector parsed the repository URL with java.net.URL, which has no stream handler for the dav/dav:http/dav+http schemes wagon-webdav speaks and throws for any other unparseable URL, silently skipping the external:*/external:http:* mirror rules for such repositories. Parse the scheme and host textually instead, matching the fix already applied to MavenRepositorySystem.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aredRepositoryIds After rebase onto maven-4.0.x, transformBom() needed its visibility restored to package-private (matching transformNonPom) and the existing test updated to pass the now-required declaredRepositoryIds parameter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9038987 to
02295cf
Compare
Robustness fixes across the CLI, transport and consumer POM.
isExternalRepo/isExternalHttpRepoparsed withjava.net.URL, which throws ondav:,dav:http:anddav+http:— leaving those branches unreachable — and returnedfalseon any parse failure, so an unparseable URL matched noexternal:*mirror. Parsing is now textual and an unparseable URL is treated as external.settings.xmlprofile are dropped, with a warning naming each.maven.consumer.pom.sanitizeRepositories=falserestores the previous behaviour. This changes a published artifact format — see the note below.mvnupreused hardcoded repositories and forceduser.home, so a realsettings.xmlwas never read. It now uses the settings the CLI already loaded and the configured local repository. Where the standalone resolver cannot honour the configuration — offline mode, a redirecting mirror, an active proxy — it is given no remote repositories and the affected strategies skip their remote work with a warning.Release note for the consumer POM change: a downstream consumer relying on inheriting a repository declaration that existed only in a parent POM or the publisher's settings will need to declare that repository itself.
Each change is a separate commit.
Draft while related code paths are reviewed — the same guard may be needed in sibling implementations of these interfaces, and I would rather establish that before asking for review time.