How do I add a constraint that will only ever caus...
# community-support
l
How do I add a constraint that will only ever cause a transient dependency to move forward? I frequently want to do this when: • a dependency A depends on something B • B has a bug (often a CVE) that's fixed in a later version than A depends on • I don't (directly) depend on B, so I don't want to add a dependency on it
I thought this could be done by adding a dependency constraint like this:
Copy code
dependencies {
    constraints {
        api("com.graphql-java:graphql-java") {
            version {
                strictly("[20.4,)")
                prefer("20.4")
            }
            because("CVE-2023-2976")
        }
    }
However, just now I noticed that the above constraint was somehow causing graphql-java to get downgraded to 20.4. From the output of
./gradlew dependencies
, this was in runtimeClasspath with this constraint:
Copy code
|    +--- com.expediagroup:graphql-kotlin-schema-generator:7.1.0
|    |    +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 1.9.23 (*)
|    |    +--- org.jetbrains.kotlin:kotlin-reflect:1.8.22 -> 1.9.23 (*)
|    |    +--- org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.7.3 -> 1.8.0 (*)
|    |    +--- io.github.classgraph:classgraph:4.8.170
|    |    +--- org.slf4j:slf4j-api:2.0.12 -> 2.0.13
|    |    +--- com.graphql-java:graphql-java:21.5 -> 20.4
|    |    |    +--- com.graphql-java:java-dataloader:3.2.0 -> 3.2.2
I though this constraint would only ever push the dependency forward, not back, but the line
com.graphql-java:graphql-java:21.5 -> 20.4
suggests otherwise, and indeed when I removed the constraint it caused me to get a newer version of graphql-java.
So how do I correctly make a constraint that will only ever cause a transient dependency to move forward?
c
Copy code
version {
  require("20.4")
}
require
Implies that the selected version cannot be lower than what
require
accepts but could be higher through conflict resolution, even if higher has an exclusive higher bound. This is what a direct dependency translates to. This term supports dynamic versions.
When defined, this overrides any previous
strictly
declaration and clears previous
reject
.
Isn’t that what you want?
l
Hmmm... that does seem to work. Why does the other version have this weird behavior, though? And why does it seem to be recommended so often. I'd originally been told to use the strictly+prefer form on this Slack, and I've seen it in many other places too including this post on the Gradle blog about dealing with log4J:
```dependencies {
constraints {
implementation("org.apache.logging.log4j:log4j-core") {
version {
strictly("[2.17, 3[")
prefer("2.17.0")
}
```
Hmmm... looking at that more closely now, I think I see the problem. 🤦
I guess Gradle does not consider "21.5" to comply with "[20.4" because of a different major version. So perhaps I should have had:
Copy code
version {
    strictly("[20.4, 21[")
    prefer("20.4")
}
c
I think the strictly form is probably preferred to avoid floating through semantic versioning ‘barriers’? I don’t see why yours is breaking though
your existing version declaration should have no upper bound
t
Fwiw, Gradle apparently uses
require 2.17.1; reject [2.0, 2.17.1)
for log4j.
c
what does
dependencyInsight
have to say?
l
Looks like that modified "strictly" doesn't work. It still downgrades to 20.4.
Fwiw, Gradle apparently uses
require 2.17.1; reject [2.0, 2.17.1)
for log4j
Oof. Perhaps someone should update https://blog.gradle.org/log4j-vulnerability?
I ran
dependencyInsight
on
--configuration runtimeClasspath --dependency com.graphql-java:graphql-java
and it says... a bunch of stuff. I've never heard of dependencyInsight before, so I'm not familiar with how to interpret its output. 😆 What should I be looking for?
FWIW, this also seems to work:
Copy code
api("com.graphql-java:graphql-java") {
            version {
                require("20.4")
                reject("[20.0, 20.4)")
            }
            because("CVE-2023-2976")
        }
I'm curious to know if the added
reject
clause here provides any safety over having only the
require
clause.
c
sorry - stepped out for the evening… I don’t think the reject does anything extra, but I’m not an expert so take that with a pinch of salt. Regarding the dependencyInsight output, share and I might be able to see something in it. It’s either that, or share a reproducible test case if IP allows.
l
Thanks, I'll see if I can create a self-contained test case.