Article
In the era of containerized Java applications, securing your final Docker image is as critical as securing your source code. While tools like OWASP Dependency-Check scan your pom.xml at build time, they miss the complete picture of what's actually deployed. Grype, an open-source vulnerability scanner from Anchore, addresses this by scanning container images and file systems for known CVEs, providing a definitive report on the security posture of your final, runnable artifact.
What is Grype?
Grype is a powerful, command-line vulnerability scanner designed for the software supply chain. It statically analyzes container images, directories, or archives to find known vulnerabilities in both your application dependencies and the underlying operating system packages. For Java teams, this means it can detect issues in your JAR files, your base JVM image, and the OS layer of your container, all in a single scan.
Why Grype is Essential for Java Container Security
- Comprehensive Dependency Analysis: Grype doesn't just look at your direct dependencies; it unpacks your final application JAR/WAR and scans all embedded libraries, including those added by your build tool (Maven/Gradle) as transitive dependencies.
- Base Image Auditing: Your choice of base image (e.g.,
eclipse-temurin:17,openjdk:8) can introduce hundreds of vulnerabilities. Grype scans the OS packages (likelibc6,openssl) within that image, which are invisible to traditional SCA tools. - CI/CD Integration: As a fast, single-binary tool, Grype integrates perfectly into CI/CD pipelines. You can fail a build if critical vulnerabilities are found in the container image before it's pushed to a registry.
- Truth in Deployment: It answers the critical question: "Based on the exact image I'm about to deploy, what known vulnerabilities does it contain?"
How to Use Grype with a Java Application
Using Grype is straightforward and follows a simple pattern: grype <target>, where the target can be a container image, a directory, or a file.
1. Install Grype
Follow the official instructions for your OS. For Linux/macOS, it's often a simple curl command:
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
2. Build Your Java Docker Image
First, create your application's Docker image as you normally would.
# Example multi-stage Dockerfile FROM maven:3.8.6-eclipse-temurin-17 AS builder WORKDIR /app COPY . . RUN mvn clean package -DskipTests FROM eclipse-temurin:17-jre-jammy RUN useradd -ms /bin/bash javauser USER javauser COPY --from=builder /app/target/my-app.jar app.jar ENTRYPOINT ["java", "-jar", "/app.jar"]
Build the image:
docker build -t my-company/my-java-app:latest .
3. Scan the Image with Grype
Run Grype against your newly built image:
grype my-company/my-java-app:latest
Understanding Grype's Output for Java
Grype provides a clear table output with crucial information:
✔ Parsed image ✔ Cataloged packages [102 packages] ✔ Scanned image [12 vulnerabilities] NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY tomcat 9.0.65 9.0.70 java CVE-2022-45143 High log4j-core 2.16.0 2.17.0 java CVE-2021-44832 Medium libc6 2.31-13 2.31-14 deb CVE-2022-23218 Critical openssl 1.1.1f-1 1.1.1n-1 deb CVE-2022-1292 High
Key columns for Java developers:
- NAME: The vulnerable package (
log4j-core,spring-core, etc.) - INSTALLED: The vulnerable version found in your image
- FIXED-IN: The first version that contains a fix
- TYPE: The package type (
javafor application dependencies,debfor OS packages) - VULNERABILITY: The CVE identifier
- SEVERITY: The assessed risk level
Advanced Grype Usage for Java Projects
1. Scan a Directory Instead of an Image
You can scan your build output directory directly, which is useful before building the container:
# After running mvn package grype dir:target/
2. Generate SARIF Reports for CI Integration
For better integration with GitHub Advanced Security or Azure DevOps, output results in SARIF format:
grype my-company/my-java-app:latest --output sarif --file grype-results.sarif
3. Fail the Build on Specific Severities
Use exit codes to fail your CI pipeline when critical vulnerabilities are found:
grype my-company/my-java-app:latest --fail-on high
This command will exit with code 1 if any High or Critical vulnerabilities are found.
4. Use with Docker Registry Authentication
Scan images from private registries by first authenticating with Docker:
docker login my-registry.com grype my-registry.com/my-company/my-java-app:latest
Best Practices for Java Teams
- Scan Early, Scan Often: Integrate Grype into your CI pipeline to scan every build, not just release candidates. Catch issues when they're introduced, not when they're discovered in production.
- Use Minimal Base Images: Start with slim, well-maintained base images like
eclipse-temurin:17-jre-jammyinstead of full JDK images. Smaller images have fewer packages and a smaller attack surface. - Multi-Stage Builds are Crucial: As shown in the example Dockerfile, use multi-stage builds to ensure your final image contains only the runtime JRE and your application JAR—not build tools, source code, or Maven dependencies.
- Prioritize Fixes Intelligently:
- Critical/High in OS packages: Update your base image or use
apt-get update && apt-get upgradein your Dockerfile. - Critical/High in Java dependencies: Update the version in your
pom.xmlorbuild.gradle. - Medium/Low: Assess based on whether the vulnerable functionality is actually used in your application.
- Critical/High in OS packages: Update your base image or use
- Combine with SCA Tools: Use Grype alongside traditional SCA tools like OWASP Dependency-Check. While Dependency-Check scans your source code dependencies, Grype verifies the final container, providing defense in depth.
- Regularly Update Base Images: Even if your application code doesn't change, your base image accumulates vulnerabilities. Rebuild and rescan your images regularly to pick up OS security patches.
Sample CI Pipeline Integration
Here's a GitHub Actions workflow example that builds a Java app and fails on critical vulnerabilities:
name: Build and Security Scan
on: [push, pull_request]
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Build Docker image
run: docker build -t my-app:${{ github.sha }} .
- name: Install Grype
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
- name: Scan for vulnerabilities
run: |
grype my-app:${{ github.sha }} --fail-on critical
Conclusion
For Java development teams embracing containers, Grype provides an essential layer of security assurance. By scanning the final deployment artifact—the container image—it reveals the true vulnerability state of what you're about to ship to production. Its speed, simplicity, and powerful CI integration make it an indispensable tool for modern Java development workflows, ensuring that security remains a continuous concern throughout the development lifecycle, not just a final checkpoint before deployment.
Advanced Java Supply Chain Security, Kubernetes Hardening & Runtime Threat Detection
Sigstore Rekor in Java – https://macronepal.com/blog/sigstore-rekor-in-java/
Explains integrating Sigstore Rekor into Java systems to create a transparent, tamper-proof log of software signatures and metadata for verifying supply chain integrity.
Securing Java Applications with Chainguard Wolfi – https://macronepal.com/blog/securing-java-applications-with-chainguard-wolfi-a-comprehensive-guide/
Explains using Chainguard Wolfi minimal container images to reduce vulnerabilities and secure Java applications with hardened, lightweight runtime environments.
Cosign Image Signing in Java Complete Guide – https://macronepal.com/blog/cosign-image-signing-in-java-complete-guide/
Explains how to digitally sign container images using Cosign in Java-based workflows to ensure authenticity and prevent unauthorized modifications.
Secure Supply Chain Enforcement Kyverno Image Verification for Java Containers – https://macronepal.com/blog/secure-supply-chain-enforcement-kyverno-image-verification-for-java-containers/
Explains enforcing Kubernetes policies with Kyverno to verify container image signatures and ensure only trusted Java container images are deployed.
Pod Security Admission in Java Securing Kubernetes Deployments for JVM Applications – https://macronepal.com/blog/pod-security-admission-in-java-securing-kubernetes-deployments-for-jvm-applications/
Explains Kubernetes Pod Security Admission policies that enforce security rules like restricted privileges and safe configurations for Java workloads.
Securing Java Applications at Runtime Kubernetes Security Context – https://macronepal.com/blog/securing-java-applications-at-runtime-a-guide-to-kubernetes-security-context/
Explains how Kubernetes security contexts control runtime permissions, user IDs, and access rights for Java containers to improve isolation.
Process Anomaly Detection in Java Behavioral Monitoring – https://macronepal.com/blog/process-anomaly-detection-in-java-comprehensive-behavioral-monitoring-2/
Explains detecting abnormal runtime behavior in Java applications to identify potential security threats using process monitoring techniques.
Achieving Security Excellence CIS Benchmark Compliance for Java Applications – https://macronepal.com/blog/achieving-security-excellence-implementing-cis-benchmark-compliance-for-java-applications/
Explains applying CIS security benchmarks to Java environments to standardize hardening and improve overall system security posture.
Process Anomaly Detection in Java Behavioral Monitoring – https://macronepal.com/blog/process-anomaly-detection-in-java-comprehensive-behavioral-monitoring/
Explains behavioral monitoring of Java processes to detect anomalies and improve runtime security through continuous observation and analysis.
JAVA CODE COMPILER