Safeguard
Security Guides

JVM Supply Chain Security: Securing the Path from Source to Artifact

Your JVM supply chain spans repositories, build tools, plugins, and artifacts. Here's how to secure each link — from dependency confusion to artifact signing.

Marcus Chen
AppSec Engineer
5 min read

When people say "we got breached through a dependency," they usually picture a single vulnerable library. But the JVM supply chain is a whole pipeline, and an attacker can strike at any link: the public repository you download from, the build tool that fetches and executes plugins, the CI runner that has credentials to everything, and the artifact registry you publish to. Log4Shell (CVE-2021-44228) got the headlines as a vulnerability, but supply-chain attacks — where adversaries deliberately poison a component before you ever compile it — are the faster-growing threat, and the JVM ecosystem's deep transitive trees make them hard to spot. This guide maps the links and how to defend each one.

What is the JVM software supply chain?

It's every step and system that contributes code or configuration to your final artifact: the Maven Central or internal repositories you resolve from, the transitive dependency graph, the build tool (Maven/Gradle) and its plugins, the JDK itself, the CI/CD environment, and the registry where the built JAR or container lands. Security means integrity and provenance at each link — being able to answer "where did this byte come from, and can I prove it wasn't tampered with?" for everything in the artifact.

Defend against dependency confusion and typosquatting

Dependency-confusion attacks — publicized broadly by security researcher Alex Birsan in 2021 — exploit build tools that resolve internal package names from public repositories. If your build looks up com.acme.internal-lib and a public repo happens to serve a malicious package under that coordinate, you can pull the attacker's code. Defenses:

  • Host internal artifacts in a private repository and configure your build to resolve internal groupId namespaces only from it.
  • Never let a public mirror satisfy an internal coordinate. In Gradle, use exclusiveContent/repositories filtering; in Maven, use mirror and repository ordering with a curated proxy.
  • Watch for typosquats of popular coordinates (a transposed letter in a groupId).
repositories {
    exclusiveContent {
        forRepository { maven { url = uri("https://nexus.acme.internal/repo") } }
        filter { includeGroupByRegex("com\\.acme\\..*") }  // internal only from Nexus
    }
    mavenCentral()   // everything else
}

Verify integrity: signatures and checksums

Every artifact you consume should be verified, and every artifact you produce should be signed. Maven Central already requires PGP signatures on published artifacts — verify them rather than trusting transport security alone. For your own outputs, sign artifacts and, increasingly, use keyless signing via Sigstore's cosign so consumers can verify provenance without managing long-lived keys. Treat a checksum or signature mismatch as a hard failure, never a warning.

Adopt SLSA and reproducible builds

The SLSA framework (Supply-chain Levels for Software Artifacts) gives you a graded target for build integrity — from "provenance exists" up to "builds are hermetic and non-falsifiable." Two practical steps get you most of the value:

  • Generate signed build provenance describing what source and dependencies produced each artifact, emitted by your CI system.
  • Pursue reproducible builds so the same source yields byte-identical output. Reproducibility means a tampered build stands out because it doesn't match the expected hash. Maven's reproducible-build settings and Gradle's normalization support make this achievable for most projects.

Lock down the build environment

The CI runner is the highest-value target in the chain — it typically holds credentials to your registry, cloud, and source control. Harden it:

  • Pin build tool and plugin versions; a Maven or Gradle plugin runs arbitrary code during your build.
  • Run builds in ephemeral, least-privilege environments; scope registry tokens to the narrowest push path.
  • Keep a Gradle/Maven dependency lockfile and verification metadata under source control so build inputs are reviewable.

Maintain a live SBOM as your source of truth

You cannot secure what you cannot enumerate. Generate a CycloneDX or SPDX software bill of materials on every build — not quarterly — capturing every direct and transitive component with its exact version. When the next Log4Shell-class disclosure lands, an up-to-date SBOM is the difference between answering "are we affected?" in minutes versus days of frantic grep.

Crucially, the SBOM must describe what was actually built, not what a manifest declared. Fat JARs (Spring Boot's executable JARs, shaded uber-JARs) and container images bundle dependencies that no pom.xml or build.gradle enumerates directly, and repackaging tools can flatten or relocate classes in ways that break naive matching. Generate the bill of materials from the resolved build output and store it as a build artifact alongside the binary it describes, so provenance travels with the thing you actually ship rather than the source you hoped produced it.

JVM supply chain checklist

  • Internal coordinates resolve only from a private, curated repository
  • Artifact signatures and checksums verified on consume, signed on produce
  • Signed build provenance emitted; reproducible builds pursued (SLSA target set)
  • Build tool and plugin versions pinned; CI runs ephemeral and least-privilege
  • Dependency lockfiles and verification metadata committed and reviewed
  • CycloneDX/SPDX SBOM generated on every build and stored

How Safeguard helps

Securing the JVM supply chain is fundamentally about visibility and provenance across links that don't naturally talk to each other, and that's what Safeguard ties together. SBOM Studio generates a signed CycloneDX bill of materials on every build and ingests SBOMs from elsewhere in your pipeline, giving you the live, queryable inventory that turns a zero-day disclosure into a fast lookup. Software composition analysis continuously matches that inventory against new CVEs and advisories, while the reachability engine prioritizes by whether a flagged component is actually reachable in your code. The Safeguard CLI embeds these checks directly in your build and CI so provenance and scanning happen on every run, and auto-fix pull requests close the loop when a remediation is available. For a side-by-side on SBOM and supply-chain coverage, see the comparison hub.

Get started free at app.safeguard.sh/register, and explore the SBOM and pipeline docs at docs.safeguard.sh.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.