chore/plugin-wide-review: apply plugin-wide code-review findings (T1+T2+T3)#30
Merged
vedanthvdev merged 1 commit intomasterfrom Apr 22, 2026
Merged
chore/plugin-wide-review: apply plugin-wide code-review findings (T1+T2+T3)#30vedanthvdev merged 1 commit intomasterfrom
vedanthvdev merged 1 commit intomasterfrom
Conversation
…T2+T3)
A four-reviewer sweep (correctness, security, maintainability, simplicity)
against the plugin source tree as it landed in v1.9.18 surfaced ten
actionable findings. This PR lands all of them in a single change so
v1.9.19 carries the security-critical, correctness, and polish fixes
together.
Tier 1 — safety-critical:
* TransitiveStrategy no longer drops consumer tests when a production
class is deleted by the MR (`git rm FooService.java` MRs were
silently losing coverage on every consumer).
* SourceFileScanner now rejects symlinks whose real path escapes the
project root, closing both a DoS (walking `/`) and an information-
leak surface (`.java` filenames flowing into --explain / WARN).
* New LogSanitizer escapes C0 + DEL + C1 control characters; every
filename-derived log site (four total: --explain sample, engine
unmapped-file WARN, AffectedTestTask malformed-FQN WARN, engine
per-test INFO) now routes through it so a hostile MR cannot forge
log lines or hide real warnings with ANSI escapes.
* AffectedTestsConfig#effectiveMode() now always returns a concrete
Mode (falls back to Builder.detectMode()), matching the Javadoc
contract that downstream switches depend on.
* OutOfScopeMatchers literal branch now matches path.equals(bare) and
path.endsWith("/" + bare) — ProjectIndex relativises paths without
a trailing slash, which was exactly the shape the matcher was
missing.
Tier 2 — correctness gaps that silently dropped tests:
* UsageStrategy now selects tests that reference the changed class
via inner-class imports (import x.Outer.Inner) or static imports
(import static x.Constants.MAX), including the wildcard form.
* ImplementationStrategy now performs a fixpoint subtype-closure
walk bounded by transitiveDepth, so grandchildren of a changed
interface (A <- B <- C) are discovered and C's tests run when A
changes.
Tier 3 — polish in adjacent code:
* PathToClassMapper rejects path-traversal segments (`..`) in diff
input (segment-aware, not substring) and routes them to the
unmapped bucket at debug level.
* AffectedTestsConfig baseRef validation now delegates to JGit's
Repository.isValidRefName plus a SHA/rev-expression whitelist,
replacing the brittle `contains("..")` rule.
* Deleted dead PathToClassMapper#extractModule + its 3 unit tests;
the method had no in-tree caller.
* LogSanitizer gained null-tolerance on joinSanitized and
Locale.ROOT on its hex format; ImplementationStrategy deep-copies
its simple-name-to-FQN map so fixpoint mutations can't leak.
CHANGELOG entry describes every listed fix and the behaviour change for
operators. ./gradlew check passes (unit + functional).
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.
Why this PR exists
A four-reviewer sweep (correctness, security, maintainability, simplicity) ran against the plugin source tree as it landed in v1.9.18. Ten findings were triaged into three tiers; all ten ship here so v1.9.19 carries the security-critical, correctness, and polish fixes together instead of dribbling them out over three releases.
A re-review by the
code-reviewersubagent ran against the staged diff before push; its two tier-2 findings (two missed log-sanitisation sites, C1 control range not covered) are applied in the same commit. Tier-3 polish suggestions (deep-copy of the subtype-closure map,joinSanitizednull-tolerance,Locale.ROOTon hex formatting, CHANGELOG rewording) are folded in too.Tier 1 — safety-critical
git rm FooService.javaMRs were silently losing coverage on every consumer ofFooServicebecause the reverse-dependency map was built from on-disk files only. Fix unionschangedProductionClassesintoallKnownFqns./) and an information-leak surface (.javafilenames flowing into--explain/ WARN).LogSanitizerescapes C0 + DEL + C1 control characters. Every filename-derived log site — four total:--explainsample, engine unmapped-file WARN,AffectedTestTaskmalformed-FQN WARN, engine per-test INFO — now routes through it. A hostile MR can no longer forge plugin log lines or hide real warnings behind ANSI escapes.AffectedTestsConfig#effectiveMode()always returns a concrete Mode. Previously returnednullfor zero-config callers despite a Javadoc contract promisingLOCAL/CI/STRICT; downstream switches that followed the docs NPE'd.OutOfScopeMatchersliteral branch now matches exact directory paths.ProjectIndexrelativises without a trailing slash; that was the exact shape the matcher was missing, so literal entries silently leaked tests into discovery.Tier 2 — correctness gaps that silently dropped tests
import x.Outer.Inner) and static imports (import static x.Constants.MAX, including the wildcard form).transitiveDepthas a sanity cap. GivenA ← B ← C, the single-pass walk stopped at B and silently dropped CTest; the grandchild is now discovered.Tier 3 — polish in adjacent code
PathToClassMapperrejects path-traversal segments (..) in diff input (segment-aware, not substring) and routes them to the unmapped bucket atdebuglevel so a hostile diff cannot flood the build log.AffectedTestsConfigbaseRef validation is now JGit-native — delegates toRepository.isValidRefNameplus a SHA/rev-expression whitelist, dropping the brittlecontains(\"..\")rule.PathToClassMapper#extractModuleand its three unit tests. No in-tree caller.LogSanitizergained null-tolerance onjoinSanitizedandLocale.ROOTon its hex format.ImplementationStrategydeep-copies its simple-name-to-FQN map so fixpoint mutations can't leak.Test coverage added
Verification
Behaviour for users
No API or default-value changes. All fixes either close a latent bug or tighten a boundary that was documented-but-not-enforced. The only operator-visible difference is log output: filenames with control characters now show as
\\n/\\x1Betc. instead of corrupting the log line.