Skip to content

chore: harden installAtEnd integrity contracts - #445

Open
gnodet wants to merge 1 commit into
masterfrom
quick-fix/harden-install-at-end
Open

chore: harden installAtEnd integrity contracts#445
gnodet wants to merge 1 commit into
masterfrom
quick-fix/harden-install-at-end

Conversation

@gnodet

@gnodet gnodet commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens the installAtEnd integrity contracts in InstallMojo, addressing two findings from a static security audit. No adversary steers these — they are reported because the plugin's own javadoc promises an all-or-nothing property the code does not enforce, and the deviation lands in the trusted-on-arrival shared local repository.

Findings addressed

ID Severity Title
f005 LOW installAtEnd all-or-nothing contract broken by mid-loop failure
f006 LOW installAtEnd trigger is unsynchronized check-then-act under parallel builds

Changes

  • Plugin matching (f005): usingPlugin() now groups reactor projects by plugin presence (any execution not bound to phase none) instead of exact execution-id equality. A module binding the goal under a custom id can no longer observe a singleton project set, trivially satisfy allProjectsMarked, and install mid-build.
  • Partial-install inventory (f005): On a mid-loop deferred-install failure, an explicit ERROR inventory of installed / failed / not-installed projects is logged before rethrowing.
  • Javadoc correction (f005): The installAtEnd javadoc is updated to state the actual guarantee.
  • Exactly-once trigger (f006): The mark-check-fire sequence is wrapped in a synchronized block (static lock shared across reactor threads). Each project transitions TO_BE_INSTALLED → INSTALLED inside the loop, so a concurrent or repeated trigger skips completed work.

Category

Integrity hardening — no adversary, no live exploit. The documented all-or-nothing contract is now enforced.

Test plan

  • usingPluginMatchesAnyExecutionId — custom execution id is recognized
  • usingPluginIgnoresExecutionsBoundToNone — phase none is excluded
  • midLoopFailureLogsPartialInstallInventory — explicit inventory logged on failure
  • deferredInstallRunsExactlyOncePerProject — second execute() call does not re-install
  • Existing tests pass unchanged
  • mvn clean install -B succeeds

🤖 Generated with Claude Code

Security audit findings (integrity hardening, no adversary):

- f005 (LOW): Group reactor projects by plugin presence instead of
  execution-id equality; log explicit partial-install inventory on
  mid-loop deferred-install failure
- f006 (LOW): Synchronize the deferred-install mark-check-fire sequence;
  transition project state to INSTALLED for exactly-once semantics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ArtifactInstallerRequest request =
(ArtifactInstallerRequest) pluginContext.get(ArtifactInstallerRequest.class.getName());
try {
installProject(request);

@slawekjaranowski slawekjaranowski Aug 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In 3.x branch we build request wich contains all artifacts from all projects, and finally repositorySystem.install is called only once, so we resolver take care about install all artifacts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct — 3.x collects all artifacts into a single InstallRequest and calls repositorySystem.install() only once, so:

  • f005 (usingPlugin): 3.x already matches by plugin presence (hasExecution checks goals + phase ≠ none), not by execution-id — no fix needed
  • f005 (partial-install inventory): the single atomic install means there is no per-project loop that could fail mid-way — no partial state to log
  • f006 (DEFERRED_INSTALL_LOCK): even if two threads race into the deferred block, the second call just re-installs the same request — idempotent, no risk of mixed state

No backport to 3.x planned for this PR.

@gnodet
gnodet marked this pull request as ready for review September 1, 2026 10:57
@gnodet

gnodet commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

No backport to maven-install-plugin-3.x is needed for this PR. The 3.x branch uses a fundamentally different deferred-install architecture: all artifacts are collected into a single InstallRequest and repositorySystem.install() is called exactly once, so:

  • usingPlugin matching: 3.x already matches by plugin presence (hasExecution checks goals + phase ≠ none), not by execution-id
  • Partial-install inventory: no per-project install loop means no mid-loop failure and no partial state to report
  • DEFERRED_INSTALL_LOCK: a duplicate call just re-installs the same atomic request — idempotent, no risk of mixed state

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants