Preamble
OWASP Dependency-Check ingests Maven (and other) dependencies and matches them against NVD-style vulnerability data. It is the Java-side companion to Poetry for Dependency Management and Packaging’s Python lockfile discipline: your classpath is part of the attack surface.
Run a scan locally (step by step)
-
Install a JDK and Maven on your machine (the same major Java version you use in CI helps avoid classpath surprises).
-
From the repo root that contains
pom.xml, compile dependencies so the classpath exists:mvn -B -DskipTests compile(orpackageif your modules need it). -
Run the Maven plugin without editing
pom.xml(good for a first try):mvn -B org.owasp:dependency-check-maven:10.0.4:checkPin the version to whatever you standardize on in CI; newer releases appear on Maven Central under
org.owasp:dependency-check-maven. -
Open the report: by default you get
target/dependency-check-report.html(single-module). Multi-module reactors often use the aggregate goal from the parent so one report covers the tree—see the next section. -
Expect the first run to be slow: the tool downloads NVD data. In automation, set
NVD_API_KEY(free key from NIST) so API rate limits do not stall builds; export it locally when iterating.
Optional CLI path (no Maven plugin): install the OWASP Dependency-Check distribution, then run dependency-check.sh --project "MyApp" --scan ./target/lib --format HTML—Maven projects usually stay with the plugin so coordinates line up with the reactor.
Wire it into Maven (repeatable builds)
Add the plugin so mvn verify (or a dedicated profile) runs the scan in every environment:
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>10.0.4</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
<suppressionFiles>
<suppressionFile>dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
For a multi-module build, bind aggregate on the parent POM instead of check per leaf, or invoke org.owasp:dependency-check-maven:aggregate once from the root—otherwise you duplicate work and split reports.
CI integration
Attach HTML/JSON reports to CI artifacts for triage. Fail builds on CVSS thresholds the team agrees on; suppress false positives with expiring entries and owner notes—silent forever-suppressions are how debt hides.
Example: Terraform + AWS CodeBuild
Terraform typically provisions the runner (IAM, project, triggers); the commands that invoke OWASP live in CodeBuild’s buildspec. Below is a minimal sketch: swap GITHUB source for CODECOMMIT, S3, or BITBUCKET, and store NVD_API_KEY in SSM Parameter Store or Secrets Manager instead of plain variables.
buildspec.yml (in the application repo, referenced by the project):
version: 0.2
phases:
install:
runtime-versions:
java: corretto17
pre_build:
commands:
- java -version
- mvn -version
build:
commands:
- mvn -B -DskipTests compile
- mvn -B org.owasp:dependency-check-maven:10.0.4:check -DfailBuildOnCVSS=7
artifacts:
files:
- target/dependency-check-report.html
name: dependency-check/$CODEBUILD_BUILD_NUMBER
If the CodeBuild project is NO_ARTIFACTS, omit the artifacts block above—AWS rejects a buildspec that declares artifacts when the project does not.
Terraform (separate infra repo or module):
variable "nvd_api_key" {
type = string
sensitive = true
description = "NVD 2.0 API key; create at https://nvd.nist.gov/developers/request-an-api-key"
}
resource "aws_s3_bucket" "codebuild_artifacts" {
bucket = "your-account-java-dependency-check-artifacts"
}
resource "aws_iam_role" "codebuild" {
name = "codebuild-dependency-check"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "codebuild.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy_attachment" "codebuild_managed" {
role = aws_iam_role.codebuild.name
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}
resource "aws_iam_role_policy" "codebuild_s3_artifacts" {
name = "codebuild-s3-artifacts"
role = aws_iam_role.codebuild.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"s3:PutObject",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
]
Resource = [
aws_s3_bucket.codebuild_artifacts.arn,
"${aws_s3_bucket.codebuild_artifacts.arn}/*"
]
}]
})
}
resource "aws_codebuild_project" "java_dependency_check" {
name = "java-dependency-check"
description = "Compile + OWASP Dependency-Check"
build_timeout = 60
service_role = aws_iam_role.codebuild.arn
artifacts {
type = "S3"
location = aws_s3_bucket.codebuild_artifacts.id
path = "dependency-check"
packaging = "NONE"
}
environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
type = "LINUX_CONTAINER"
privileged_mode = false
environment_variable {
name = "NVD_API_KEY"
value = var.nvd_api_key
type = "PLAINTEXT"
}
}
source {
type = "GITHUB"
location = "https://github.com/your-org/your-java-service.git"
git_clone_depth = 1
buildspec = "buildspec.yml"
}
logs_config {
cloudwatch_logs {
status = "ENABLED"
group_name = "/aws/codebuild/java-dependency-check"
stream_name = "build"
}
}
}
# Optional: trigger on push via CodePipeline + CodeStar Connections, or invoke this project from EventBridge.
Tighten PLAINTEXT before production: use environment_variable with type = "PARAMETER_STORE" or SECRETS_MANAGER. Same pattern ports to Azure DevOps (azurerm_devops_*) or Google Cloud Build (google_cloudbuild_trigger)—only the runner YAML and IAM equivalents change.
Ecosystem drift
Coordinates and classifiers change; scanners lag reality. I verify critical hits against upstream advisories before panic-upgrading everything.
Conclusion
Supply-chain visibility complements SpotBugs/Error Prone—code quality and dependency health are different lenses. Celery Basics: Tasks, Brokers, and Idempotency moves to Celery and distributed queues.