Remediate SCA vulnerabilities in transitive dependencies
Last updated: Mar 2, 2026
A transitive dependency (also called an indirect dependency) is a package that your project does not depend on directly, but that is required by one of your direct or intermediate dependencies.
It is common to receive software composition analysis (SCA) reports flagging vulnerabilities in transitive dependencies. Addressing them is not always straightforward, because you do not control the version of the transitive dependency directly — a direct or intermediate dependency does. The following strategies are listed in escalation order: try each one in sequence and move to the next only if the previous one does not resolve the issue.
Strategy 1: Update the transitive dependency
The first and simplest approach is to update the vulnerable dependency directly.
For npm:
npm update <package>For Python (uv):
uv lock -P <package>For Kotlin (Gradle):
./gradlew --refresh-dependenciesThis works when the semver ranges specified by the direct or intermediate dependencies allow for the installation of a version that includes the security patch. However, if a direct or intermediate dependency pins the transitive dependencies to a specific vulnerable version or a range that excludes the patched release, this command will not update it.
Strategy 2: Update the direct or intermediate dependency
If the transitive dependency was not updated in the previous step, the direct or intermediate dependency is likely constraining its version. Update that dependency instead:
For npm:
npm update <direct-or-intermediate-package>For Python (uv):
uv lock -P <direct-or-intermediate-package>For Kotlin (Gradle),
update the version of the direct dependency in your build.gradle.kts:
implementation("group:direct-or-intermediate-artifact:new-version")This may pull in a newer version of the transitive dependency that includes the security fix. Note that the direct dependency may already be at its latest version and still reference the vulnerable transitive package, in which case this strategy will also be insufficient.
Strategy 3: Use dependency overrides
When the semver ranges in the dependency tree prevent the installation of a patched version, you can force a specific version using overrides.
For npm,
add the following to your package.json:
"overrides": {
"<package>": "<safe-version>"
}For Python (uv),
add the following to your pyproject.toml:
[tool.uv]
override-dependencies = ["<package>>=<safe-version>"]For Kotlin (Gradle),
add a resolution strategy to your build.gradle.kts:
configurations.all {
resolutionStrategy {
force("group:<package>:<safe-version>")
}
}Overrides bypass the version resolution logic of the package manager and may cause incompatibilities. Before merging, run all tests and linters locally and verify that the CI pipeline passes. If something breaks, move to strategy 4.
Strategy 4: Wait for maintainers
If the override introduces breaking changes, the safest path is to wait for the upstream maintainers to resolve the vulnerability. This involves two separate releases:
- The maintainers of the vulnerable package publish a patched version.
- The maintainers of the direct or intermediate dependency release a new version that adopts the patched transitive dependency.
This process can take days or longer. In the meantime, you may accept the risk through your vulnerability management workflow and track the upstream activity to act as soon as a safe version is available.
Additional strategy: Fork and remove shrinkwrap
In extreme cases,
a library may be shipped with an
npm-shrinkwrap.json
file that locks its dependency tree,
preventing overrides from taking effect.
In this scenario,
you can fork the library,
remove the shrinkwrap file,
and reference your fork until the upstream issue is resolved.
This approach requires the most effort and maintenance overhead,
so it should only be used when no other strategy is viable.