If you ship Android apps or Kotlin backend services, your build.gradle.kts file is really a manifest of third-party code you didn't write and don't fully control. Every AndroidX library, every Ktor client, every transitive dependency pulled in by Retrofit or Room is a potential entry point for a known CVE. Running OWASP Dependency-Check Kotlin builds through is one of the fastest ways to close that visibility gap without adopting a new platform or rewriting your build.
This guide walks through setting up OWASP Dependency-Check as a Gradle security plugin in a Kotlin or Android project, running your first scan, reading the report, and wiring it into CI so vulnerable dependencies fail the build before they reach production. By the end, you'll have a repeatable kotlin vulnerability audit process that runs on every pull request, plus a suppression workflow so false positives don't create alert fatigue.
1. Add the Dependency-Check Gradle Plugin to Your Project
OWASP Dependency-Check ships an official Gradle plugin, which is the cleanest way to integrate it into Kotlin and Android projects since it hooks directly into your existing dependency graph — no separate SBOM export step required.
In a Kotlin DSL project (build.gradle.kts), add the plugin to your root build file:
plugins {
id("org.owasp.dependencycheck") version "9.2.0"
}
For a Groovy-based build.gradle, the equivalent is:
plugins {
id "org.owasp.dependencycheck" version "9.2.0"
}
If you're on a multi-module Android project, apply the plugin at the root level so it can crawl every module's resolved configurations in one pass, rather than applying it per-module and getting fragmented reports.
2. Configure the Plugin for Kotlin and Android Builds
Out of the box, the plugin scans compile and runtime classpaths, but Android projects have quirks — build variants, debugImplementation, androidTestImplementation, and AAR dependencies that don't resolve the same way plain JARs do. Add a dependencyCheck block to tune what gets analyzed:
dependencyCheck {
formats = listOf("HTML", "JSON")
failBuildOnCVSS = 7.0f
scanConfigurations = listOf("releaseRuntimeClasspath", "debugRuntimeClasspath")
suppressionFile = "config/dependency-check-suppressions.xml"
analyzers.assemblyEnabled = false
}
A few notes specific to Android and Kotlin:
scanConfigurationsshould point at your resolved runtime classpaths (e.g.,releaseRuntimeClasspath), not the rawimplementationconfiguration, since that's what actually reflects what ships in the APK/AAB.- Kotlin stdlib and coroutine artifacts are published to Maven Central with proper Package URLs, so Dependency-Check's Central Analyzer will match them against CVEs correctly out of the box — no extra config needed there.
- Disabling analyzers you don't need (like
assemblyEnabledfor .NET) trims scan time on large Android dependency shows a modest reduction in noise and false-positive surface.
3. Get an NVD API Key
As of recent Dependency-Check releases, scanning without an NVD API key is painfully slow and frequently throttled, because the tool pulls the National Vulnerability Database feed on first run and keeps it updated on subsequent runs. Register for a free key at the NVD API key request page, then pass it via a Gradle property or environment variable rather than committing it to source control:
dependencyCheck {
nvd {
apiKey = System.getenv("NVD_API_KEY")
}
}
Set NVD_API_KEY as a secret in your CI system and locally in your shell profile. Without it, expect the first database sync to take 20-30+ minutes; with it, it typically drops to a few minutes.
4. Run Your First OWASP Dependency-Check Kotlin Scan
With the plugin configured, kick off a scan from the command line:
./gradlew dependencyCheckAnalyze
On the first run, Dependency-Check downloads and caches the NVD CVE data locally (typically in ~/.gradle/dependency-check-data), which is why that initial run is the slowest. Subsequent runs only pull incremental updates.
For a single-module or specific-module scan in a multi-module Android project:
./gradlew :app:dependencyCheckAnalyze
Once complete, you'll find reports under build/reports/dependency-check-report.html (and .json if you enabled that format). This is the artifact you'll want to publish from CI for humans and machines to consume respectively.
5. Read and Triage the Report
If you're wondering how to check a scanning report without getting lost in noise, start here. Open dependency-check-report.html. Each flagged dependency lists:
- The artifact and version (e.g.,
okhttp-3.12.0.jar) - Matched CVE identifiers with CVSS scores
- A confidence indicator for the CPE (Common Platform Enumeration) match
- Links to the NVD entry for further reading
Not every match is real. Dependency-Check matches on package naming heuristics, and Android/Kotlin artifacts with generic names (utility libraries, forked packages) occasionally produce false positives where the CPE doesn't actually correspond to your dependency. Treat "confidence: low" matches with more skepticism than "confidence: high" ones, and cross-check anything that will fail your build before suppressing it.
6. Suppress False Positives and Set a Fail Threshold
Once you've confirmed a match is a false positive (or an accepted, documented risk), add it to a suppression file rather than ignoring the whole report:
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<suppress>
<notes>False positive: CPE match on unrelated "commons" package, verified 2026-07-06</notes>
<packageUrl regex="true">^pkg:maven/org\.example/commons-utils@.*$</packageUrl>
<cve>CVE-2023-99999</cve>
</suppress>
</suppressions>
Reference this file from the suppressionFile property shown in step 2. Pair suppressions with failBuildOnCVSS, which controls the severity threshold at which a scan actively breaks the build rather than just reporting. Most teams start permissive (9.0, critical-only) and tighten it toward 7.0 once the initial backlog of findings is triaged.
7. Automate the Scan in CI/CD
A local scan is a kotlin vulnerability audit habit; a CI-enforced scan is a policy. Add a dedicated stage to your pipeline so every merge request gets checked automatically. A GitHub Actions example:
- name: OWASP Dependency-Check
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
run: ./gradlew dependencyCheckAnalyze
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: dependency-check-report
path: build/reports/dependency-check-report.html
Cache the dependency-check-data directory between CI runs (via actions/cache or your CI's equivalent) so you're not re-downloading the full NVD dataset on every job — this is the single biggest lever for keeping scan times reasonable in a pipeline that runs on every commit.
Troubleshooting and Verification
Scan takes forever or times out in CI. This is almost always a missing or rate-limited NVD API key, or a cold cache. Confirm NVD_API_KEY is actually set (log its presence, not its value) and that your CI cache step is restoring the data directory before the analyze task runs.
Build fails with "NVD Data is stale." Dependency-Check refuses to trust CVE data older than a configured threshold. Run ./gradlew dependencyCheckUpdate manually to force a refresh, or check that your scheduled cache refresh job hasn't broken.
Android module reports dependencies that aren't actually shipped. Double-check scanConfigurations — if you're scanning implementation instead of a resolved runtime classpath like releaseRuntimeClasspath, you'll pick up testImplementation and debugImplementation artifacts that never reach production, inflating your findings count.
Verify your setup end-to-end by intentionally pinning a dependency with a known CVE (e.g., an old Jackson or OkHttp version) in a throwaway branch, running ./gradlew dependencyCheckAnalyze, and confirming it appears in the report with the expected CVE ID. Remove the pin afterward. This is the fastest way to confirm your gradle security plugin configuration, suppression file, and CI wiring all actually work together rather than silently passing.
Confirm android dependency scanning coverage by comparing the artifact count in the Dependency-Check report against ./gradlew :app:dependencies --configuration releaseRuntimeClasspath — if the numbers diverge significantly, your scanConfigurations list is probably missing a module or variant.
How Safeguard Helps
OWASP Dependency-Check is a solid foundation, but it's a point-in-time scanner: it tells you what's vulnerable right now, in the branch you happened to run it against, based on an NVD dataset that updates on its own schedule. Safeguard builds on that foundation for teams shipping Kotlin and Android software at scale.
Instead of relying on a single Gradle task run manually or bolted onto one CI job, Safeguard continuously monitors your dependency graph across every repository and module, correlates findings against multiple vulnerability sources beyond the NVD feed (closing gaps during NVD backlog periods, which have been a recurring reliability issue), and de-duplicates the kind of low-confidence false positives that make teams tune out Dependency-Check alerts entirely. Findings get prioritized by actual reachability and exploitability rather than raw CVSS score, so your team spends triage time on the handful of dependencies that matter instead of wading through a 40-page HTML report after every release.
Safeguard also generates and tracks SBOMs automatically as part of your build and release process, giving you the audit trail that a suppression XML file and a build artifact alone don't provide — which matters the moment a customer security questionnaire or SOC 2 audit asks you to prove your software supply chain security posture, not just describe it. If you're already running OWASP Dependency-Check on your Kotlin projects, Safeguard slots in as the layer that turns that scan into an ongoing, auditable program.