Article: Implementing ECR Image Scanning with Java
Amazon Elastic Container Registry (ECR) provides secure, scalable container image storage. In this article, we'll explore how to implement ECR image scanning using Java to enhance your container security posture.
Why ECR Image Scanning Matters
ECR image scanning helps identify software vulnerabilities in your container images, allowing you to:
- Detect known CVEs (Common Vulnerabilities and Exposures)
- Enforce security policies before deployment
- Maintain compliance with security standards
- Integrate security into your CI/CD pipeline
Java Implementation
Here's a comprehensive Java solution for ECR image scanning:
package com.titliel.ecr;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ecr.EcrClient;
import software.amazon.awssdk.services.ecr.model.*;
import java.util.List;
/**
* Titliel ECR Image Scanner
* Provides comprehensive scanning capabilities for Amazon ECR images
*/
public class TitlielEcrScanner {
private final EcrClient ecrClient;
public TitlielEcrScanner(String accessKeyId, String secretAccessKey, Region region) {
AwsBasicCredentials awsCreds = AwsBasicCredentials.create(accessKeyId, secretAccessKey);
this.ecrClient = EcrClient.builder()
.region(region)
.credentialsProvider(StaticCredentialsProvider.create(awsCreds))
.build();
}
/**
* Start vulnerability scanning for a specific image
*/
public void startImageScan(String repositoryName, ImageIdentifier imageId) {
try {
StartImageScanRequest request = StartImageScanRequest.builder()
.repositoryName(repositoryName)
.imageId(imageId)
.build();
StartImageScanResponse response = ecrClient.startImageScan(request);
System.out.println("Scan started for image: " + imageId.imageDigest());
} catch (EcrException e) {
System.err.println("Error starting scan: " + e.awsErrorDetails().errorMessage());
}
}
/**
* Get detailed scan findings for a scanned image
*/
public void describeImageScanFindings(String repositoryName, ImageIdentifier imageId) {
try {
DescribeImageScanFindingsRequest request = DescribeImageScanFindingsRequest.builder()
.repositoryName(repositoryName)
.imageId(imageId)
.maxResults(100)
.build();
DescribeImageScanFindingsResponse response = ecrClient.describeImageScanFindings(request);
ImageScanFindings findings = response.imageScanFindings();
if (findings != null) {
printVulnerabilities(findings);
}
} catch (EcrException e) {
System.err.println("Error retrieving findings: " + e.awsErrorDetails().errorMessage());
}
}
/**
* Print vulnerability details in a formatted way
*/
private void printVulnerabilities(ImageScanFindings findings) {
System.out.println("=== VULNERABILITY SCAN RESULTS ===");
System.out.println("Scan completed at: " + findings.imageScanCompletedAt());
System.out.println("Vulnerability count: " + findings.findingSeverityCounts());
List<ImageScanFinding> vulnerabilityFindings = findings.findings();
if (vulnerabilityFindings != null && !vulnerabilityFindings.isEmpty()) {
for (ImageScanFinding finding : vulnerabilityFindings) {
System.out.println("\n--- Finding ---");
System.out.println("Name: " + finding.name());
System.out.println("Severity: " + finding.severity());
System.out.println("URI: " + finding.uri());
System.out.println("Description: " + finding.description());
if (finding.attributes() != null) {
finding.attributes().forEach(attr ->
System.out.println(attr.key() + ": " + attr.value()));
}
}
} else {
System.out.println("No vulnerabilities found!");
}
}
/**
* Check if an image has been scanned
*/
public boolean isImageScanned(String repositoryName, ImageIdentifier imageId) {
try {
DescribeImagesRequest request = DescribeImagesRequest.builder()
.repositoryName(repositoryName)
.imageIds(imageId)
.build();
DescribeImagesResponse response = ecrClient.describeImages(request);
if (!response.imageDetails().isEmpty()) {
ImageDetail imageDetail = response.imageDetails().get(0);
return imageDetail.imageScanStatus() != null &&
imageDetail.imageScanStatus().status() == ScanStatus.COMPLETE;
}
} catch (EcrException e) {
System.err.println("Error checking scan status: " + e.awsErrorDetails().errorMessage());
}
return false;
}
/**
* Get a list of all images in a repository
*/
public List<ImageDetail> listRepositoryImages(String repositoryName) {
try {
DescribeImagesRequest request = DescribeImagesRequest.builder()
.repositoryName(repositoryName)
.build();
DescribeImagesResponse response = ecrClient.describeImages(request);
return response.imageDetails();
} catch (EcrException e) {
System.err.println("Error listing images: " + e.awsErrorDetails().errorMessage());
return List.of();
}
}
}
Advanced Scanning Utility Class
package com.titliel.ecr;
import software.amazon.awssdk.services.ecr.model.*;
import java.time.LocalDateTime;
import java.util.*;
/**
* Advanced ECR scanning utility with batch operations and reporting
*/
public class TitlielAdvancedScanner {
private final TitlielEcrScanner scanner;
public TitlielAdvancedScanner(TitlielEcrScanner scanner) {
this.scanner = scanner;
}
/**
* Scan all images in a repository
*/
public void scanAllImagesInRepository(String repositoryName) {
System.out.println("Scanning all images in repository: " + repositoryName);
List<ImageDetail> images = scanner.listRepositoryImages(repositoryName);
for (ImageDetail image : images) {
if (image.imageDigest() != null) {
ImageIdentifier imageId = ImageIdentifier.builder()
.imageDigest(image.imageDigest())
.build();
if (!scanner.isImageScanned(repositoryName, imageId)) {
System.out.println("Starting scan for image: " + image.imageDigest());
scanner.startImageScan(repositoryName, imageId);
} else {
System.out.println("Image already scanned: " + image.imageDigest());
scanner.describeImageScanFindings(repositoryName, imageId);
}
}
}
}
/**
* Generate security report for a repository
*/
public SecurityReport generateSecurityReport(String repositoryName) {
List<ImageDetail> images = scanner.listRepositoryImages(repositoryName);
SecurityReport report = new SecurityReport();
report.setReportDate(LocalDateTime.now());
report.setRepositoryName(repositoryName);
for (ImageDetail image : images) {
if (image.imageDigest() != null && image.imageTags() != null) {
ImageIdentifier imageId = ImageIdentifier.builder()
.imageDigest(image.imageDigest())
.build();
if (scanner.isImageScanned(repositoryName, imageId)) {
report.addScannedImage(image.imageTags().get(0), image.imageDigest());
} else {
report.addUnscannedImage(image.imageTags().get(0), image.imageDigest());
}
}
}
return report;
}
/**
* Security report data class
*/
public static class SecurityReport {
private LocalDateTime reportDate;
private String repositoryName;
private List<String> scannedImages = new ArrayList<>();
private List<String> unscannedImages = new ArrayList<>();
// Getters and setters
public LocalDateTime getReportDate() { return reportDate; }
public void setReportDate(LocalDateTime reportDate) { this.reportDate = reportDate; }
public String getRepositoryName() { return repositoryName; }
public void setRepositoryName(String repositoryName) { this.repositoryName = repositoryName; }
public List<String> getScannedImages() { return scannedImages; }
public void addScannedImage(String tag, String digest) {
scannedImages.add(tag + " (" + digest.substring(0, 16) + "...)");
}
public List<String> getUnscannedImages() { return unscannedImages; }
public void addUnscannedImage(String tag, String digest) {
unscannedImages.add(tag + " (" + digest.substring(0, 16) + "...)");
}
public void printReport() {
System.out.println("\n=== SECURITY REPORT ===");
System.out.println("Repository: " + repositoryName);
System.out.println("Generated: " + reportDate);
System.out.println("\nScanned Images: " + scannedImages.size());
scannedImages.forEach(img -> System.out.println(" ✓ " + img));
System.out.println("\nUnscanned Images: " + unscannedImages.size());
unscannedImages.forEach(img -> System.out.println(" ✗ " + img));
}
}
}
Usage Example
package com.titliel.ecr.demo;
import com.titliel.ecr.TitlielAdvancedScanner;
import com.titliel.ecr.TitlielEcrScanner;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ecr.model.ImageIdentifier;
/**
* Demo application showing Titliel ECR scanning capabilities
*/
public class EcrScanningDemo {
public static void main(String[] args) {
// Initialize scanner
TitlielEcrScanner scanner = new TitlielEcrScanner(
"YOUR_ACCESS_KEY",
"YOUR_SECRET_KEY",
Region.US_EAST_1
);
TitlielAdvancedScanner advancedScanner = new TitlielAdvancedScanner(scanner);
String repositoryName = "my-application";
// Generate security report
TitlielAdvancedScanner.SecurityReport report =
advancedScanner.generateSecurityReport(repositoryName);
report.printReport();
// Scan a specific image
ImageIdentifier imageId = ImageIdentifier.builder()
.imageTag("latest")
.build();
if (!scanner.isImageScanned(repositoryName, imageId)) {
scanner.startImageScan(repositoryName, imageId);
// Wait for scan to complete (in real implementation, use async polling)
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Get findings
scanner.describeImageScanFindings(repositoryName, imageId);
}
}
Maven Dependencies
<dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>ecr</artifactId> <version>2.20.0</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>auth</artifactId> <version>2.20.0</version> </dependency> </dependencies>
Key Features
- Automated Scanning: Start scans for ECR images programmatically
- Comprehensive Reporting: Generate detailed vulnerability reports
- Batch Operations: Scan multiple images in sequence
- Security Monitoring: Track scanned vs unscanned images
- Integration Ready: Easy to integrate into CI/CD pipelines
Best Practices
- Always handle AWS credentials securely using IAM roles when possible
- Implement proper error handling and retry logic
- Schedule regular scans as part of your deployment process
- Set up alerts for high-severity vulnerabilities
- Integrate with your existing security monitoring systems
This Titliel ECR scanning solution provides a robust foundation for maintaining container security in your Java applications.
Secure Java Dependency Management, Vulnerability Scanning & Software Supply Chain Protection (SBOM, SCA, CI Security & License Compliance)
https://macronepal.com/blog/github-code-scanning-in-java-complete-guide/
Explains GitHub Code Scanning for Java using tools like CodeQL to automatically analyze source code and detect security vulnerabilities directly inside CI/CD pipelines before deployment.
https://macronepal.com/blog/license-compliance-in-java-comprehensive-guide/
Explains software license compliance in Java projects, ensuring dependencies follow legal requirements (MIT, Apache, GPL, etc.) and preventing license violations in enterprise software.
https://macronepal.com/blog/container-security-for-java-uncovering-vulnerabilities-with-grype/
Explains using Grype to scan Java container images and filesystems for known CVEs in OS packages and application dependencies to improve container security.
https://macronepal.com/blog/syft-sbom-generation-in-java-comprehensive-software-bill-of-materials-for-jvm-applications/
Explains using Syft to generate SBOMs (Software Bill of Materials) for Java applications, listing all dependencies, libraries, and components for supply chain transparency.
https://macronepal.com/blog/comprehensive-dependency-analysis-generating-and-scanning-sboms-with-trivy-for-java/
Explains using Trivy to generate SBOMs and scan Java dependencies and container images for vulnerabilities, integrating security checks into CI/CD pipelines.
https://macronepal.com/blog/dependabot-for-java-in-java/
Explains GitHub Dependabot for Java projects, which automatically detects vulnerable dependencies and creates pull requests to update them securely.
https://macronepal.com/blog/parasoft-jtest-in-java-comprehensive-guide-to-code-analysis-and-testing/
Explains Parasoft Jtest, a static analysis and testing tool for Java that helps detect bugs, security issues, and code quality problems early in development.
https://macronepal.com/blog/snyk-open-source-in-java-comprehensive-dependency-vulnerability-management-2/
Explains Snyk Open Source for Java, which continuously scans dependencies for vulnerabilities and provides automated fix suggestions and monitoring.
https://macronepal.com/blog/owasp-dependency-check-in-java-complete-vulnerability-scanning-guide/
Explains OWASP Dependency-Check, which scans Java dependencies against the National Vulnerability Database (NVD) to detect known security vulnerabilities.
https://macronepal.com/blog/securing-your-dependencies-a-java-developers-guide-to-whitesource-mend-bolt/
Explains Mend (WhiteSource) Bolt for Java, a dependency management and SCA tool that provides vulnerability detection, license compliance, and security policy enforcement in enterprise environments.