Security audit: LOW robustness fixes — skip, fallback, policy, redaction, containment, parse - #699
Merged
Merged
Conversation
…eCredentialBinding guard Security audit finding f001 (MEDIUM). Keeps v1's structure: AbstractDeployMojo.validateCredentialBinding guard, known-URL collection (settings mirrors + profile repositories/pluginRepositories, plus distributionManagement via DeployMojo's getKnownRepositoryUrls override), trailing-slash URL normalization, and the user-property-only -Dmaven.deploy.allowCredentialReuse=true knob (session.getUserProperties() only, not POM-settable). v2 adds provenance semantics (fleet precedent: checkstyle bug_04 / clean bug_08): validateCredentialBinding(id, url, fromUserProperty). Mismatch against non-empty known URLs is refused regardless of provenance (unchanged from v1). Empty-record case no longer passes silently: POM-sourced (DeployMojo computes isFromUserProperty by requiring the -D user property to be present AND equal to the value in use, since explicit POM <configuration> beats -D in Maven precedence; the alt selection chain records provenance per parameter name) -> REFUSE naming id and URL, knob-overridable; CLI-sourced -> WARN (id has stored credentials, no known URL for it, credentials will be sent to <url>). DeployFileMojo keeps the 2-arg call, which delegates with fromUserProperty=true (deploy-file is CLI-driven by nature). Javadoc on getKnownRepositoryUrls documents dm-sourced known URLs as advisory in the malicious-POM model; parameter javadoc for altDeploymentRepository and repositoryId documents the provenance-dependent behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ransportSecurity(id,url) Security audit finding f002 (MEDIUM). Single choke point: AbstractDeployMojo.validateTransportSecurity(id,url) refuses http:// and ftp:// deployment URLs by default, called from createDeploymentArtifactRepository (covers the alt-repo route in DeployMojo and the deploy-file url) and explicitly on both distributionManagement branches (which bypass that factory via session.createRemoteRepository(Repository)). Loopback hosts (localhost, 127.0.0.0/8, ::1) are exempt so local mock/IT repositories keep working; opt-out is -Dmaven.deploy.allowInsecureUrl=true (user property, non-pom-bindable), which downgrades the refusal to a WARN. Unknown/other schemes (https, file, scp, sftp, scm:, dav:) are untouched. Docs leg: deploy-http.md example switched to https with an explicit prefer-HTTPS + refusal note; deploying-with-classifiers.md.vm http:// examples switched to https. (Wagon-era deploy-ftp.md/deploy-ssh-external.md rewrites are in bug_13.) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…alidators hoisted to Abs Security audit finding f003 (MEDIUM). Both legs of the root cause closed with shared validators hoisted to AbstractDeployMojo: isValidId now rejects any empty dot-separated segment (kills '.', '..', '.a', 'a.', 'a..b' while keeping the [a-zA-Z0-9._-] allowlist); isValidVersion keeps the separator blocklist and additionally rejects empty, whitespace/ISO-control characters, and all-dots values; new isValidClassifier (same allowlist, not all-dots, absent/empty allowed) and isValidTypeOrExtension (non-empty variant) cover the components the old gate never saw. DeployFileMojo now validates packaging and classifier at the :272 gate (after processModel, so jar-embedded-POM-supplied packaging is covered too) and every classifiers/types list entry inside the side-artifact loop (empty entries rejected). DeployMojo (second mojo per the finding brief) validates project g/a/v and every deployable's classifier in createDeployerRequest before building the request. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…on: (1) deployAllAtOnce Security audit finding f004 (MEDIUM). Two-sided state fix, per the triage recommendation: (1) deployAllAtOnce collects the batched projects while flattening and, after all requests deploy successfully, marks each one State.DEPLOYED (mark-after-batch-deploy per the design guidance - a failed batch stays TO_BE_DEPLOYED so a genuine retry of a failed build is not suppressed); (2) execute() treats DEPLOYED as terminal: re-entering the goal for an already-deployed project logs and returns, closing both triggers (double-bound executions and the direct deploy:deploy O(N^2) walk). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, a constant lock object Security audit finding f005 (MEDIUM). A single class-level monitor (DEPLOY_AT_END_LOCK, a constant lock object - not mutable static state; batch state stays in per-project session plugin contexts) now serializes every state read/write and the batch trigger: the terminal-state re-entry check, the SKIPPED/DEPLOYED/TO_BE_DEPLOYED marks, and the allProjectsMarked()+deployAllAtOnce() check-then-act. The lock is deliberately held across deployAllAtOnce(): the losing thread waits, then observes the DEPLOYED states written by the winner (bug_04) and no-ops. Request construction (createDeployerRequest) stays outside the lock to keep contention minimal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, per the finding brief.
Security audit finding f006 (MEDIUM).
Verified-true doc fix plus visibility escalation, per the finding brief. The deployAtEnd javadoc now states the real semantics: batch fires when the last deploy-bound project reaches its deploy phase (trailing execution-less modules can still fail afterward), multi-repo/mixed-retry requests deploy sequentially with no rollback, and deployAtEnd=false modules cannot be recalled; the false 'none of the reactor projects is deployed' sentence and the incoherent '(experimental)' marker (on a default-on publish path) are removed. Partial-failure visibility: deployAllAtOnce now deploys the grouped requests in an explicit loop and, on failure after at least one group succeeded, logs an ERROR naming how many groups and which repository ids were already published ('remain published: there is no rollback') before rethrowing, so the failure is no longer attributed only to the wrong module.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ility The deployAtEndBatchIsNotRedeployedOnReentry test needs a properly mocked MojoExecution with plugin model, because the upstream cache optimization (PROJECTS_WITH_DEPLOY_KEY) calls mojoExecution.getPlugin().getModel().getKey() in hasDeployExecution(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merged
3 tasks
The classifier validator from f003 wrongly rejected empty classifier
entries in the classifiers CSV (e.g. "classifiers=,src,"). Empty means
"no classifier" and is valid Maven convention used by the
3rd-party-{jar,pom}-with-extras ITs. isValidClassifier("") already
returns true; the redundant isEmpty() pre-check was the bug.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…kipMode(value, parame Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sitory declared' (doc Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ic limit the audit re Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…heme://userinfo@ as s Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ion (pipeline variabl Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… swap: reordering the Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gnodet
force-pushed
the
security/audit-low-robustness
branch
from
August 31, 2026 13:45
01b6a86 to
b8e8bcb
Compare
The f007 patch intentionally moved the releases/snapshots skip check after version resolution so that a version supplied via the jar's embedded POM is classified correctly. This means the jar IS inspected before the skip decision, which is expected. Update the IT to assert what matters (the deployment was skipped) instead of an implementation detail (whether the jar was opened). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same fix as deployfile-release-skip: the jar is inspected to resolve the version before the snapshots skip check, which is the intended behavior of the version-aware skip reordering (f007). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4 tasks
On macOS, /var is a symlink to /private/var. When a non-existent path (e.g. an artifact that hasn't been created yet) is checked against an existing containment root, toRealPath() fails for the child but succeeds for the root — giving /var/... vs /private/var/..., which breaks the startsWith check. Fix realOrNormalized() to walk up to the closest existing ancestor, resolve its real path, then re-append the non-existent suffix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gnodet
marked this pull request as ready for review
September 1, 2026 21:16
slawekjaranowski
approved these changes
Sep 5, 2026
…ustness, guard, log (#700) * fix: f013 (LOW) — Verified-true doc fixes (each claim checked against src/main before writ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: f014 (LOW) — All four fixed in readingPomFromJarFile/initProperties/finally: (1) the Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: f015 (LOW) — The guard now compares locations: isSameLocation() resolves both sides t Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: f016 (INFO) — The immediate branch now logs 'Deploying <gav>' with a comment explainin Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add per-project partial-deploy inventory to deployAllAtOnce Align deploy-at-end failure reporting with install plugin PR #445: when the batch deploy fails mid-loop, log an explicit per-project inventory showing which projects were already published and which were not, instead of only reporting at the request level. Also moves the DEPLOYED state marking into the deploy loop so each project transitions TO_BE_DEPLOYED → DEPLOYED as soon as its contributing request completes, making the exactly-once guard and the partial-deploy inventory consistent. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Member
|
@gnodet please check conflicts |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Applies 6 LOW-severity robustness findings from the Glasswing static security audit (scan-maven-deploy-plugin-20260811). All confirmed TP with 5/5 adversarial-panel unanimity. Stacks on #698 (MEDIUM fixes).
Findings addressed
skipis a fail-open tri-state — unrecognized values silently publishChanges
parseSkipModein both mojos —true/false/releases/snapshotsmatched case-insensitively; unrecognized values (typos liketure) now fail the build instead of silently deploying. Deploy-filereleases/snapshotsclassification reordered after coordinate resolutionsnapshotRepositoryroutes a snapshot to the release repositorywarnIfPolicyMismatchWARNs pre-upload when the selected repository's explicit releases/snapshots policy rejects the artifact kind; enforcement stays server-sideredactUrlUserInfomasksscheme://userinfo@asscheme://***@at all log sites; deploy lines logid (redacted-url)maven.deploy.file.containedInparameter: restricts all deploy-file path inputs to a containment directory (off by default)id::layout::urlwhose URL part contains::refusedBreaking changes
skipvocabulary is now fail-closed: unrecognized values fail the buildid::layout::urlalt-repo values now refused (was silently misdeploying)Test plan
Audit source:
scan-maven-deploy-plugin-20260811.zip🤖 Generated with Claude Code