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)

  1. Install a JDK and Maven on your machine (the same major Java version you use in CI helps avoid classpath surprises).

  2. From the repo root that contains pom.xml, compile dependencies so the classpath exists: mvn -B -DskipTests compile (or package if your modules need it).

  3. Run the Maven plugin without editing pom.xml (good for a first try):

    mvn -B org.owasp:dependency-check-maven:10.0.4:check
    

    Pin the version to whatever you standardize on in CI; newer releases appear on Maven Central under org.owasp:dependency-check-maven.

  4. 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.

  5. 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.