skip to content

A vulnerable transitive dependency keeps slipping into your build via several libraries. How would you use eachDependency to pin it to a safe version across the whole graph?

level: juniorimportance: should knowfreq 40%

answer

  1. fires for transitives too
  2. match group/name then useVersion
  3. configurations.all for all classpaths
  4. because() records the CVE
  5. verify with dependencyInsight

basics

~10 s

Add configurations.all { resolutionStrategy.eachDependency { if it's the vulnerable module, useVersion("safe") } }. The rule fires for every transitive, so every path to that module resolves to the safe version.

solid answer

~40 s

Because `eachDependency` fires for **every** dependency in the graph — direct and transitive — it's a clean way to pin a transitive that arrives via many parents. Match the module by `requested.group`/`requested.name`, call `useVersion("<safe>")`, and add `because("CVE fix")`. Wrap it in `configurations.all` so it covers compile, runtime, and test classpaths. Every resolution path to that module now lands on the safe version regardless of which library pulled it in. Verify with `./gradlew dependencyInsight --dependency <name>`, which shows the rule's reason. Note this is one option among several — a `dependencies { constraints { } }` block or a platform/BOM is often more declarative — but `eachDependency` is the imperative tool that wins when you need conditional or computed logic.

code

kotlin · 8 lines
kotlin
configurations.all {
    resolutionStrategy.eachDependency {
        if (requested.name == "jackson-databind") {
            useVersion("2.17.1")
            because("pin patched jackson-databind")
        }
    }
}

go deeper

for a junior

Show the basic configurations.all + eachDependency + useVersion pattern and that it covers transitives.

for a middle

Add because() and dependencyInsight verification; note it covers all classpaths.

for a senior

Compare against constraints/platform and explain when each is preferable for security pinning.

for a principal

Discuss org-wide policy: convention plugins applying such pins consistently and lifecycle for removing them.

## The problem A transitive dependency (one you never declared directly) carries a vulnerability. It enters your graph through several libraries, possibly at different versions. You want one safe version everywhere. ## Why eachDependency fits The `eachDependency` rule is invoked **once per module per resolution**, covering transitives. So a single rule catches every path to the offending module without you editing each parent declaration. ```kotlin configurations.all { resolutionStrategy.eachDependency { if (requested.group == "com.fasterxml.jackson.core" && requested.name == "jackson-databind") { useVersion("2.17.1") because("CVE-XXXX: pin jackson-databind to a patched release") } } } ``` ## Cover all configurations `configurations.all { ... }` applies the rule to every configuration — `compileClasspath`, `runtimeClasspath`, `testRuntimeClasspath`, and any plugin-added ones — so the pin is comprehensive and future-proof against new configurations. ## Verify it took effect ```bash ./gradlew dependencyInsight \ --configuration runtimeClasspath \ --dependency jackson-databind ``` The report shows the resolved version and the `because` reason, confirming the rewrite and helping the next engineer understand it. ## Trade-offs vs alternatives - **`eachDependency` + useVersion**: imperative, supports arbitrary conditions/computed versions; runs per-module so keep it cheap. - **`constraints { }`**: declarative, expresses a preferred/required version and participates more cleanly in conflict resolution and reporting. - **Platform/BOM**: best for aligning a whole family of modules to one version. For a single conditional security pin, `eachDependency` is direct and effective. For broad, long-lived version policy, prefer constraints or a platform. Whichever you choose, document the CVE in `because`/comments so the pin can be removed once upstream libraries catch up.

  • How do you confirm the pin actually applied and see why?
    Run ./gradlew dependencyInsight --configuration runtimeClasspath --dependency <name>; it shows the resolved version and the because() reason.
  • When would a constraints block be a better choice than this eachDependency rule?
    When you want a declarative, conflict-resolution-friendly preferred/required version with cleaner reporting and no imperative per-module callback.

saying these in an interview costs you the question

  • Claiming you must edit every library that pulls the transitive — the rule covers all paths.
  • Thinking the rule misses transitives — it fires for them too.

context