Skip to content

Validate metadata inputs across legacy and new API paths, clone proxies before decryption - #12950

Merged
gnodet merged 7 commits into
apache:masterfrom
slachiewicz:pr/legacy-compat-master
Sep 1, 2026
Merged

Validate metadata inputs across legacy and new API paths, clone proxies before decryption#12950
gnodet merged 7 commits into
apache:masterfrom
slachiewicz:pr/legacy-compat-master

Conversation

@slachiewicz

@slachiewicz slachiewicz commented Aug 30, 2026

Copy link
Copy Markdown
Member

Fixes on both the legacy compatibility paths and the new API resolver layer.

Legacy compat layer (compat/)

  • Checksum policy. Three legacy metadata paths ignored the configured checksum policy and defaulted to a warning regardless of a fail setting; the configured policy is now threaded through and honoured.
  • Repository key and metadata tokens. The repository key is validated before it becomes part of a local file name, and version tokens from downloaded metadata are validated at the single point all metadata passes through, which also covers the latest/release/snapshot transformations.
  • Proxy decryption. DefaultSettingsDecrypter now clones each proxy before setting its decrypted password, mirroring the existing behaviour for servers, so decryption no longer mutates the caller's settings objects. Maven 3 already clones here.

New API resolver layer (impl/maven-impl)

  • Shared validation utility. MetadataInputValidator consolidates coordinate-component and version-token validation: rejects .., /, \, :, and ISO control characters.
  • Version resolution. DefaultVersionResolver and DefaultVersionRangeResolver validate parsed Versioning elements from maven-metadata.xml before the values are used to compose filesystem paths or artifact coordinates.
  • Relocation validation. DistributionManagementArtifactRelocationSource validates relocation groupId, artifactId, and version before constructing a RelocatedArtifact.

Each change is a separate commit.

AbstractRepositoryMetadata.getLocalFilename rejects a repository key that is
the ".." token, contains '/', '\', ':', or an ISO control character, before
using it in a local file name. DefaultRepositoryMetadataManager.readMetadata
applies the same check to every version token carried by parsed repository
metadata (latest, release, versions, snapshot versions, snapshot timestamp)
before the metadata is merged and used to resolve a version.

Matches the check already used for relocation and version-range coordinates
elsewhere in the resolver.
The proxy loop in DefaultSettingsDecrypter.decrypt mutated the
caller's live Proxy objects directly, unlike the server loop just
above it which already clones before mutating. Since compat model
objects propagate setter changes up into their parent Settings
delegate, this meant a decrypted proxy password could end up written
back into the session-wide Settings object. Clone the proxy first,
mirroring the server handling, so decryption only ever touches the
copies returned in the result.
@slachiewicz slachiewicz added the bug Something isn't working label Aug 30, 2026
@slachiewicz
slachiewicz marked this pull request as draft August 30, 2026 19:05
@slachiewicz
slachiewicz marked this pull request as ready for review August 30, 2026 22:00

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repository key validation and proxy clone-before-decrypt changes are correct, well-tested, and consistent with the approved 4.0.x (#12945) and 3.10.x (#12954) variants.

However, this PR appears to be missing two checksum-policy commits that are present in the 4.0.x variant (#12945):

1. Description/content mismatch (medium)

The PR description states "Checksum policy. Three legacy metadata paths ignored the configured checksum policy and defaulted to a warning regardless of a fail setting; the configured policy is now threaded through and honoured." — but the diff does not include the commits that implement this:

  • DefaultRepositoryMetadataManager.getArtifactMetadataFromDeploymentRepository() (line 358) still hardcodes CHECKSUM_POLICY_WARN
  • DefaultRepositoryMetadataManager.resolve() lacks a ChecksumFailedException catch clause
  • LegacyRepositorySystem.retrieve() (line 663) still hardcodes CHECKSUM_POLICY_WARN

The approved 4.0.x PR (#12945) has 5 commits; this master PR has only 3 — the two missing are "Fail closed on metadata checksum mismatches under the fail policy" and "Thread the configured checksum policy through two legacy metadata fetches".

Suggestion: Either cherry-pick the two missing checksum commits into this PR, or update the PR description to remove the checksum bullet.

2. Minor: code duplication (low)

isInvalidPathToken() / validateRepositoryKey() are duplicated in LegacyLocalRepositoryManager and AbstractRepositoryMetadata — acceptable for deprecated compat code.

📋 PR Metadata

Aspect Current Suggested
Milestone (none) 4.1.0

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Guillaume Nodet

The legacy metadata manager and repository system hardcoded
CHECKSUM_POLICY_WARN, silently ignoring the operator's
--strict-checksums / -C flag and per-repository checksumPolicy
settings. This brings the master branch in line with the
maven-4.0.x fix (PR apache#12945):

- Catch ChecksumFailedException in resolve() and propagate it as a
  RepositoryMetadataResolutionException under CHECKSUM_POLICY_FAIL
- Move updateCheckManager.touch() out of the finally block so that
  failed transfers do not suppress retries for a full update interval
- Use the repository's configured checksum policy for deployment
  metadata retrieval instead of hardcoded WARN
- Pick the stricter of release/snapshot policies in
  LegacyRepositorySystem.retrieve() instead of hardcoded WARN

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta re-review — Previous finding resolved. The new commit 6ff4b8a cleanly addresses the missing checksum-policy issue.

Previous finding resolved:

Checksum policy now honoured on all three legacy paths:

  • DefaultRepositoryMetadataManager.getArtifactMetadataFromDeploymentRepository() — no longer hardcodes CHECKSUM_POLICY_WARN
  • DefaultRepositoryMetadataManager.resolve()ChecksumFailedException catch clause correctly placed before the TransferFailedException catch (important since ChecksumFailedException extends TransferFailedException)
  • LegacyRepositorySystem.retrieve() — uses getChecksumPolicy() which returns the stricter of releases/snapshots policies via a rank helper

Design observations (non-blocking):

  • updateCheckManager.touch() wisely restructured: moved from finally to explicit success/error paths, deliberately omitting it from ChecksumFailedException so the next build retries immediately instead of trusting stale metadata
  • The "stricter of releases/snapshots" approach in LegacyRepositorySystem.getChecksumPolicy() is the right choice for a generic path that can't be classified
  • Two focused tests verify the strict-fail behaviour

📋 PR Metadata

Aspect Current Suggested
Milestone (none) 4.1.0

This review was generated by an AI agent (Claude Code) and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Guillaume Nodet

@gnodet

gnodet commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Update on cross-branch coverage:

The original review on this PR incorrectly referenced #12954 as a related variant. PR #12954 is actually a different set of fixes (credential scoping, relocation validation, exact server ID matching) — not a port of this PR.

The correct cross-branch map for the fixes in this PR (path traversal validation + checksum policy + proxy clone) is:

Branch PR
maven-4.0.x #12945
master #12950 (this PR)
maven-3.10.x #12978

Separately, the fixes from #12954 (credential scoping + relocation validation + server ID matching) have been forward-ported:

Branch PR
maven-3.10.x #12954
maven-4.0.x #12977
master #12976

…solvers

Add MetadataInputValidator, a shared utility in impl/maven-impl that
rejects version tokens, snapshot timestamps, and relocation coordinates
containing path-traversal sequences (..), separators (/, \, :), or
ISO control characters.

Wire the validator into the new-API implementations:
- DefaultVersionResolver.readVersions() validates parsed Versioning
- DefaultVersionRangeResolver.readVersions() validates parsed Versioning
- DistributionManagementArtifactRelocationSource validates relocation
  groupId, artifactId, and version before applying

This mirrors the compat-layer guards already present in this PR and
eliminates the gap where maven-metadata.xml content bypasses the
model validator entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta re-review — New commit 5e1e17ee extends the validation to the new API resolvers, addressing the gap we flagged in sibling #12976. The MetadataInputValidator utility and its wiring into both version resolvers and the relocation source are sound.

Two observations:

1. [medium] Test file invisible in diffs due to literal NUL bytes

MetadataInputValidatorTest.java contains 2 literal NUL bytes (as control-character test data), causing git to classify it as binary (Binary files /dev/null and b/... differ). The entire 140+ lines of test coverage are invisible in the PR diff. Replacing the raw NUL bytes with Java \0 escape sequences (e.g., "abc\0def" instead of a literal NUL) would preserve test semantics while making the file diffable.

2. [low] validateVersionToken duplicates isInvalidCoordinateComponent logic

validateVersionToken (line ~90) reimplements the exact same null/empty, .., /, \, :, and ISO control char checks that isInvalidCoordinateComponent (lines 47-59) already performs. Could simplify to:

public static void validateVersionToken(String value, String description) throws IOException {
    if (isInvalidCoordinateComponent(value)) {
        throw new IOException("Rejecting metadata with invalid " + description + " '" + value + "'");
    }
}

What looks good:

  • Extracting a shared MetadataInputValidator utility is cleaner than the compat-layer's inlined validation
  • Fail-fast pattern in both version resolvers (validate before assigning) is correct
  • DistributionManagementArtifactRelocationSourceTest provides good coverage of relocation validation
  • Correctly not validating SnapshotVersion.extension/classifier (used only for lookup-key matching, not filesystem paths)

📋 PR Metadata

Aspect Current Suggested
Milestone (none) 4.1.0

This review was generated by an AI agent (Claude Code) and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Guillaume Nodet

@gnodet gnodet changed the title Validate legacy metadata inputs and clone proxies before decryption Validate metadata inputs across legacy and new API paths, clone proxies before decryption Sep 1, 2026
- validateVersionToken now delegates to isInvalidCoordinateComponent
  instead of reimplementing the same logic
- Replace raw NUL bytes in MetadataInputValidatorTest with \0 escape
  sequences so git treats the file as text and diffs are visible

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta re-review — Previous findings addressed.

NUL bytes in test file — Literal NUL bytes replaced with Java \0 escape sequences. File now renders as text in future diffs.

validateVersionToken duplication — Method body simplified to delegate to isInvalidCoordinateComponent, eliminating the duplicated null/empty + path-traversal + control-char logic. Runtime behavior preserved.

Clean follow-up commit with no new issues introduced.

📋 PR Metadata

Aspect Current Suggested
Milestone (none) 4.1.0

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Guillaume Nodet

gnodet added a commit that referenced this pull request Sep 1, 2026
…policy (#13002)

Backport of the path traversal validation and checksum policy fixes from
#12950 (master) / #12945 (maven-4.0.x) / #12978 (maven-3.10.x) to the
maven-3.9.x branch.

- Path traversal: AbstractRepositoryMetadata.getLocalFilename() and
  LegacyLocalRepositoryManager.ArtifactMetadataAdapter.getLocalFilename()
  now reject repository keys containing '..', '/', '\', ':', or ISO
  control characters.

- Metadata token validation: DefaultRepositoryMetadataManager.readMetadata()
  validates every version token (latest, release, versions, snapshot
  versions, snapshot timestamp) for the same characters after parsing.

- Checksum policy: resolve() now catches ChecksumFailedException
  separately and fails metadata resolution under checksumPolicy=fail.
  getArtifactMetadataFromDeploymentRepository() and
  LegacyRepositorySystem.retrieve() resolve the effective policy from
  the repository configuration instead of hardcoding 'warn'.

The proxy clone fix (DefaultSettingsDecrypter) is not included because
3.9.x already clones each proxy before decryption.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…utValidator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet
gnodet merged commit a65083f into apache:master Sep 1, 2026
3 of 4 checks passed
@github-actions github-actions Bot added this to the 4.1.0 milestone Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants