Introduction to Anchore Engine
Anchore Engine is an open-source tool for analyzing container images for security vulnerabilities, compliance issues, and best practices. This guide covers Java integration with Anchore Engine for container security scanning in CI/CD pipelines and runtime environments.
Key Anchore Engine Features
- Vulnerability Scanning - CVE detection in container images
- Policy Compliance - Custom security policies enforcement
- Image Analysis - Deep inspection of container contents
- SBOM Generation - Software Bill of Materials
- CI/CD Integration - Pipeline security gates
- REST API - Comprehensive API for automation
Dependencies and Setup
Maven Configuration
<properties> <anchore.client.version>1.0.0</anchore.client.version> <okhttp.version>4.10.0</okhttp.version> <jackson.version>2.15.2</jackson.version> </properties> <dependencies> <!-- HTTP Client --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>${okhttp.version}</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.7</version> </dependency> <!-- Testing --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency> <!-- Docker Java API --> <dependency> <groupId>com.github.docker-java</groupId> <artifactId>docker-java-core</artifactId> <version>3.3.0</version> </dependency> </dependencies> Core Anchore Engine Client
Anchore Client Implementation
package com.anchore.client; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.concurrent.TimeUnit; public class AnchoreClient { private static final Logger logger = LoggerFactory.getLogger(AnchoreClient.class); private final String baseUrl; private final String username; private final String password; private final OkHttpClient httpClient; private final ObjectMapper objectMapper; public AnchoreClient(String baseUrl, String username, String password) { this.baseUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/"; this.username = username; this.password = password; this.objectMapper = new ObjectMapper(); this.httpClient = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(300, TimeUnit.SECONDS) // Longer timeout for image analysis .writeTimeout(30, TimeUnit.SECONDS) .build(); } /** * Execute authenticated request to Anchore Engine */ private Response executeRequest(Request request) throws IOException { Response response = httpClient.newCall(request).execute(); if (!response.isSuccessful()) { String errorBody = response.body() != null ? response.body().string() : "No error body"; logger.error("Anchore API request failed: {} - {}", response.code(), errorBody); throw new AnchoreException("API request failed: " + response.code() + " - " + errorBody); } return response; } /** * Create authenticated request builder */ private Request.Builder createAuthenticatedRequest() { String credential = Credentials.basic(username, password); return new Request.Builder() .addHeader("Authorization", credential) .addHeader("Content-Type", "application/json"); } /** * Get system status */ public SystemStatus getSystemStatus() throws IOException { Request request = createAuthenticatedRequest() .url(baseUrl + "v1/system") .get() .build(); try (Response response = executeRequest(request)) { return objectMapper.readValue(response.body().string(), SystemStatus.class); } } /** * Add image for analysis */ public ImageAnalysisResponse addImage(String imageTag, boolean force) throws IOException { String json = objectMapper.writeValueAsString(new ImageAnalysisRequest(imageTag, force)); Request request = createAuthenticatedRequest() .url(baseUrl + "v1/images") .post(RequestBody.create(json, MediaType.parse("application/json"))) .build(); try (Response response = executeRequest(request)) { return objectMapper.readValue(response.body().string(), ImageAnalysisResponse.class); } } /** * Get image analysis status */ public ImageAnalysisStatus getImageStatus(String imageDigest) throws IOException { Request request = createAuthenticatedRequest() .url(baseUrl + "v1/images/" + imageDigest) .get() .build(); try (Response response = executeRequest(request)) { return objectMapper.readValue(response.body().string(), ImageAnalysisStatus.class); } } /** * Wait for image analysis to complete */ public void waitForAnalysis(String imageDigest, long timeoutMs) throws IOException, InterruptedException { long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startTime < timeoutMs) { ImageAnalysisStatus status = getImageStatus(imageDigest); if ("analyzed".equals(status.getAnalysisStatus())) { logger.info("Image analysis completed for: {}", imageDigest); return; } else if ("analysis_failed".equals(status.getAnalysisStatus())) { throw new AnchoreException("Image analysis failed for: " + imageDigest); } logger.info("Waiting for image analysis... Current status: {}", status.getAnalysisStatus()); Thread.sleep(5000); // Wait 5 seconds between checks } throw new AnchoreException("Image analysis timeout for: " + imageDigest); } /** * Get vulnerability report for image */ public VulnerabilityReport getVulnerabilities(String imageDigest) throws IOException { Request request = createAuthenticatedRequest() .url(baseUrl + "v1/images/" + imageDigest + "/vuln/all") .get() .build(); try (Response response = executeRequest(request)) { return objectMapper.readValue(response.body().string(), VulnerabilityReport.class); } } /** * Get policy evaluation for image */ public PolicyEvaluation evaluatePolicy(String imageDigest, String tag) throws IOException { Request request = createAuthenticatedRequest() .url(baseUrl + "v1/images/" + imageDigest + "/check?tag=" + tag + "&detail=true") .get() .build(); try (Response response = executeRequest(request)) { return objectMapper.readValue(response.body().string(), PolicyEvaluation.class); } } /** * Get image content (SBOM) */ public ImageContent getImageContent(String imageDigest) throws IOException { Request request = createAuthenticatedRequest() .url(baseUrl + "v1/images/" + imageDigest + "/content") .get() .build(); try (Response response = executeRequest(request)) { return objectMapper.readValue(response.body().string(), ImageContent.class); } } /** * List all policies */ public PolicyList listPolicies() throws IOException { Request request = createAuthenticatedRequest() .url(baseUrl + "v1/policies") .get() .build(); try (Response response = executeRequest(request)) { return objectMapper.readValue(response.body().string(), PolicyList.class); } } /** * Get specific policy */ public Policy getPolicy(String policyId) throws IOException { Request request = createAuthenticatedRequest() .url(baseUrl + "v1/policies/" + policyId) .get() .build(); try (Response response = executeRequest(request)) { return objectMapper.readValue(response.body().string(), Policy.class); } } // Custom exception public static class AnchoreException extends RuntimeException { public AnchoreException(String message) { super(message); } public AnchoreException(String message, Throwable cause) { super(message, cause); } } } Data Models
Response Data Classes
package com.anchore.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import java.util.Map; // System Status @JsonIgnoreProperties(ignoreUnknown = true) class SystemStatus { private String service; private String version; private String dbVersion; // Getters and setters public String getService() { return service; } public void setService(String service) { this.service = service; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getDbVersion() { return dbVersion; } public void setDbVersion(String dbVersion) { this.dbVersion = dbVersion; } } // Image Analysis Request class ImageAnalysisRequest { @JsonProperty("tag") private String imageTag; @JsonProperty("force") private boolean forceAnalysis; public ImageAnalysisRequest(String imageTag, boolean forceAnalysis) { this.imageTag = imageTag; this.forceAnalysis = forceAnalysis; } // Getters and setters public String getImageTag() { return imageTag; } public void setImageTag(String imageTag) { this.imageTag = imageTag; } public boolean isForceAnalysis() { return forceAnalysis; } public void setForceAnalysis(boolean forceAnalysis) { this.forceAnalysis = forceAnalysis; } } // Image Analysis Response @JsonIgnoreProperties(ignoreUnknown = true) class ImageAnalysisResponse { private String imageDigest; private List<AnalysisStatus> analysisStatus; // Getters and setters public String getImageDigest() { return imageDigest; } public void setImageDigest(String imageDigest) { this.imageDigest = imageDigest; } public List<AnalysisStatus> getAnalysisStatus() { return analysisStatus; } public void setAnalysisStatus(List<AnalysisStatus> analysisStatus) { this.analysisStatus = analysisStatus; } } // Analysis Status @JsonIgnoreProperties(ignoreUnknown = true) class AnalysisStatus { private String status; private String tag; // Getters and setters public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } } // Image Analysis Status @JsonIgnoreProperties(ignoreUnknown = true) class ImageAnalysisStatus { private String imageDigest; private String analysisStatus; private List<ImageDetail> imageDetail; // Getters and setters public String getImageDigest() { return imageDigest; } public void setImageDigest(String imageDigest) { this.imageDigest = imageDigest; } public String getAnalysisStatus() { return analysisStatus; } public void setAnalysisStatus(String analysisStatus) { this.analysisStatus = analysisStatus; } public List<ImageDetail> getImageDetail() { return imageDetail; } public void setImageDetail(List<ImageDetail> imageDetail) { this.imageDetail = imageDetail; } } // Image Detail @JsonIgnoreProperties(ignoreUnknown = true) class ImageDetail { private String tag; private String digest; private String createdAt; private String lastUpdated; // Getters and setters public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } public String getCreatedAt() { return createdAt; } public void setCreatedAt(String createdAt) { this.createdAt = createdAt; } public String getLastUpdated() { return lastUpdated; } public void setLastUpdated(String lastUpdated) { this.lastUpdated = lastUpdated; } } // Vulnerability Report @JsonIgnoreProperties(ignoreUnknown = true) class VulnerabilityReport { private String imageDigest; private List<Vulnerability> vulnerabilities; private VulnerabilitySummary vulnerabilitySummary; // Getters and setters public String getImageDigest() { return imageDigest; } public void setImageDigest(String imageDigest) { this.imageDigest = imageDigest; } public List<Vulnerability> getVulnerabilities() { return vulnerabilities; } public void setVulnerabilities(List<Vulnerability> vulnerabilities) { this.vulnerabilities = vulnerabilities; } public VulnerabilitySummary getVulnerabilitySummary() { return vulnerabilitySummary; } public void setVulnerabilitySummary(VulnerabilitySummary vulnerabilitySummary) { this.vulnerabilitySummary = vulnerabilitySummary; } } // Vulnerability @JsonIgnoreProperties(ignoreUnknown = true) class Vulnerability { private String vuln; private String severity; private String packageName; private String packageVersion; private String packageType; private String fix; private String url; private List<String> cweIds; // Getters and setters public String getVuln() { return vuln; } public void setVuln(String vuln) { this.vuln = vuln; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; } public String getPackageVersion() { return packageVersion; } public void setPackageVersion(String packageVersion) { this.packageVersion = packageVersion; } public String getPackageType() { return packageType; } public void setPackageType(String packageType) { this.packageType = packageType; } public String getFix() { return fix; } public void setFix(String fix) { this.fix = fix; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public List<String> getCweIds() { return cweIds; } public void setCweIds(List<String> cweIds) { this.cweIds = cweIds; } } // Vulnerability Summary @JsonIgnoreProperties(ignoreUnknown = true) class VulnerabilitySummary { private int critical; private int high; private int medium; private int low; private int negligible; private int unknown; private int total; // Getters and setters public int getCritical() { return critical; } public void setCritical(int critical) { this.critical = critical; } public int getHigh() { return high; } public void setHigh(int high) { this.high = high; } public int getMedium() { return medium; } public void setMedium(int medium) { this.medium = medium; } public int getLow() { return low; } public void setLow(int low) { this.low = low; } public int getNegligible() { return negligible; } public void setNegligible(int negligible) { this.negligible = negligible; } public int getUnknown() { return unknown; } public void setUnknown(int unknown) { this.unknown = unknown; } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } } // Policy Evaluation @JsonIgnoreProperties(ignoreUnknown = true) class PolicyEvaluation { private String imageDigest; private List<PolicyResult> policyResults; // Getters and setters public String getImageDigest() { return imageDigest; } public void setImageDigest(String imageDigest) { this.imageDigest = imageDigest; } public List<PolicyResult> getPolicyResults() { return policyResults; } public void setPolicyResults(List<PolicyResult> policyResults) { this.policyResults = policyResults; } } // Policy Result @JsonIgnoreProperties(ignoreUnknown = true) class PolicyResult { private String policyId; private String status; private String tag; private List<PolicyGateResult> gateResults; // Getters and setters public String getPolicyId() { return policyId; } public void setPolicyId(String policyId) { this.policyId = policyId; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public List<PolicyGateResult> getGateResults() { return gateResults; } public void setGateResults(List<PolicyGateResult> gateResults) { this.gateResults = gateResults; } } // Policy Gate Result @JsonIgnoreProperties(ignoreUnknown = true) class PolicyGateResult { private String gate; private String trigger; private String status; private String detail; // Getters and setters public String getGate() { return gate; } public void setGate(String gate) { this.gate = gate; } public String getTrigger() { return trigger; } public void setTrigger(String trigger) { this.trigger = trigger; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } } // Image Content (SBOM) @JsonIgnoreProperties(ignoreUnknown = true) class ImageContent { private String imageDigest; private List<ContentEntry> content; // Getters and setters public String getImageDigest() { return imageDigest; } public void setImageDigest(String imageDigest) { this.imageDigest = imageDigest; } public List<ContentEntry> getContent() { return content; } public void setContent(List<ContentEntry> content) { this.content = content; } } // Content Entry @JsonIgnoreProperties(ignoreUnknown = true) class ContentEntry { private String packageType; private String packageName; private String packageVersion; private String license; private String location; // Getters and setters public String getPackageType() { return packageType; } public void setPackageType(String packageType) { this.packageType = packageType; } public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; } public String getPackageVersion() { return packageVersion; } public void setPackageVersion(String packageVersion) { this.packageVersion = packageVersion; } public String getLicense() { return license; } public void setLicense(String license) { this.license = license; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } } // Policy List @JsonIgnoreProperties(ignoreUnknown = true) class PolicyList { private List<Policy> policies; // Getters and setters public List<Policy> getPolicies() { return policies; } public void setPolicies(List<Policy> policies) { this.policies = policies; } } // Policy @JsonIgnoreProperties(ignoreUnknown = true) class Policy { private String policyId; private String name; private String version; private String comment; private Map<String, Object> rules; // Getters and setters public String getPolicyId() { return policyId; } public void setPolicyId(String policyId) { this.policyId = policyId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public Map<String, Object> getRules() { return rules; } public void setRules(Map<String, Object> rules) { this.rules = rules; } } Anchore Service Layer
Comprehensive Anchore Service
package com.anchore.service; import com.anchore.client.AnchoreClient; import com.anchore.model.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.List; import java.util.stream.Collectors; public class AnchoreService { private static final Logger logger = LoggerFactory.getLogger(AnchoreService.class); private final AnchoreClient client; private final long analysisTimeoutMs; public AnchoreService(String baseUrl, String username, String password, long analysisTimeoutMs) { this.client = new AnchoreClient(baseUrl, username, password); this.analysisTimeoutMs = analysisTimeoutMs; } /** * Check if Anchore Engine is available */ public boolean isAvailable() { try { SystemStatus status = client.getSystemStatus(); logger.info("Anchore Engine status: {} - Version: {}", status.getService(), status.getVersion()); return true; } catch (Exception e) { logger.error("Anchore Engine is not available: {}", e.getMessage()); return false; } } /** * Analyze image and wait for completion */ public ImageAnalysisStatus analyzeImage(String imageTag, boolean force) throws IOException, InterruptedException { logger.info("Starting analysis for image: {}", imageTag); // Add image for analysis ImageAnalysisResponse response = client.addImage(imageTag, force); String imageDigest = response.getImageDigest(); logger.info("Image analysis started. Digest: {}", imageDigest); // Wait for analysis to complete client.waitForAnalysis(imageDigest, analysisTimeoutMs); // Get final status return client.getImageStatus(imageDigest); } /** * Get comprehensive security report for image */ public SecurityReport getSecurityReport(String imageTag) throws IOException, InterruptedException { // First, ensure image is analyzed ImageAnalysisStatus status = analyzeImage(imageTag, false); String imageDigest = status.getImageDigest(); // Get vulnerabilities VulnerabilityReport vulnReport = client.getVulnerabilities(imageDigest); // Get policy evaluation PolicyEvaluation policyEval = client.evaluatePolicy(imageDigest, imageTag); // Get SBOM ImageContent content = client.getImageContent(imageDigest); return new SecurityReport(imageTag, imageDigest, vulnReport, policyEval, content); } /** * Check if image passes security policy */ public PolicyCheckResult checkPolicyCompliance(String imageTag) throws IOException, InterruptedException { ImageAnalysisStatus status = analyzeImage(imageTag, false); String imageDigest = status.getImageDigest(); PolicyEvaluation policyEval = client.evaluatePolicy(imageDigest, imageTag); boolean passed = policyEval.getPolicyResults().stream() .allMatch(result -> "pass".equals(result.getStatus())); List<String> failedPolicies = policyEval.getPolicyResults().stream() .filter(result -> !"pass".equals(result.getStatus())) .map(PolicyResult::getPolicyId) .collect(Collectors.toList()); return new PolicyCheckResult(passed, failedPolicies, policyEval); } /** * Get critical and high vulnerabilities */ public List<Vulnerability> getCriticalVulnerabilities(String imageTag) throws IOException, InterruptedException { ImageAnalysisStatus status = analyzeImage(imageTag, false); String imageDigest = status.getImageDigest(); VulnerabilityReport vulnReport = client.getVulnerabilities(imageDigest); return vulnReport.getVulnerabilities().stream() .filter(vuln -> "Critical".equalsIgnoreCase(vuln.getSeverity()) || "High".equalsIgnoreCase(vuln.getSeverity())) .collect(Collectors.toList()); } /** * Generate SBOM for image */ public SBOM generateSBOM(String imageTag) throws IOException, InterruptedException { ImageAnalysisStatus status = analyzeImage(imageTag, false); String imageDigest = status.getImageDigest(); ImageContent content = client.getImageContent(imageDigest); List<SBOM.Component> components = content.getContent().stream() .map(entry -> new SBOM.Component( entry.getPackageName(), entry.getPackageVersion(), entry.getPackageType(), entry.getLicense(), entry.getLocation() )) .collect(Collectors.toList()); return new SBOM(imageTag, imageDigest, components); } // Custom result classes public static class SecurityReport { private final String imageTag; private final String imageDigest; private final VulnerabilityReport vulnerabilityReport; private final PolicyEvaluation policyEvaluation; private final ImageContent imageContent; public SecurityReport(String imageTag, String imageDigest, VulnerabilityReport vulnerabilityReport, PolicyEvaluation policyEvaluation, ImageContent imageContent) { this.imageTag = imageTag; this.imageDigest = imageDigest; this.vulnerabilityReport = vulnerabilityReport; this.policyEvaluation = policyEvaluation; this.imageContent = imageContent; } // Getters public String getImageTag() { return imageTag; } public String getImageDigest() { return imageDigest; } public VulnerabilityReport getVulnerabilityReport() { return vulnerabilityReport; } public PolicyEvaluation getPolicyEvaluation() { return policyEvaluation; } public ImageContent getImageContent() { return imageContent; } } public static class PolicyCheckResult { private final boolean passed; private final List<String> failedPolicies; private final PolicyEvaluation evaluation; public PolicyCheckResult(boolean passed, List<String> failedPolicies, PolicyEvaluation evaluation) { this.passed = passed; this.failedPolicies = failedPolicies; this.evaluation = evaluation; } // Getters public boolean isPassed() { return passed; } public List<String> getFailedPolicies() { return failedPolicies; } public PolicyEvaluation getEvaluation() { return evaluation; } } public static class SBOM { private final String imageTag; private final String imageDigest; private final List<Component> components; public SBOM(String imageTag, String imageDigest, List<Component> components) { this.imageTag = imageTag; this.imageDigest = imageDigest; this.components = components; } // Getters public String getImageTag() { return imageTag; } public String getImageDigest() { return imageDigest; } public List<Component> getComponents() { return components; } public static class Component { private final String name; private final String version; private final String type; private final String license; private final String location; public Component(String name, String version, String type, String license, String location) { this.name = name; this.version = version; this.type = type; this.license = license; this.location = location; } // Getters public String getName() { return name; } public String getVersion() { return version; } public String getType() { return type; } public String getLicense() { return license; } public String getLocation() { return location; } } } } Spring Boot Integration
Configuration Class
package com.anchore.config; import com.anchore.service.AnchoreService; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AnchoreConfig { @Value("${anchore.engine.url:http://localhost:8228}") private String anchoreUrl; @Value("${anchore.engine.username:admin}") private String anchoreUsername; @Value("${anchore.engine.password:foobar}") private String anchorePassword; @Value("${anchore.analysis.timeout.ms:300000}") // 5 minutes default private long analysisTimeoutMs; @Bean public AnchoreService anchoreService() { return new AnchoreService(anchoreUrl, anchoreUsername, anchorePassword, analysisTimeoutMs); } } REST Controller
package com.anchore.controller; import com.anchore.service.AnchoreService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/api/anchore") public class AnchoreController { private static final Logger logger = LoggerFactory.getLogger(AnchoreController.class); @Autowired private AnchoreService anchoreService; @GetMapping("/status") public ResponseEntity<Map<String, Object>> getStatus() { Map<String, Object> response = new HashMap<>(); boolean available = anchoreService.isAvailable(); response.put("available", available); response.put("service", "Anchore Engine"); return ResponseEntity.ok(response); } @PostMapping("/scan") public ResponseEntity<Map<String, Object>> scanImage( @RequestParam String image, @RequestParam(defaultValue = "false") boolean force) { Map<String, Object> response = new HashMap<>(); try { var status = anchoreService.analyzeImage(image, force); response.put("success", true); response.put("image", image); response.put("digest", status.getImageDigest()); response.put("status", status.getAnalysisStatus()); return ResponseEntity.ok(response); } catch (Exception e) { logger.error("Image scan failed: {}", e.getMessage(), e); response.put("success", false); response.put("error", e.getMessage()); return ResponseEntity.badRequest().body(response); } } @GetMapping("/report/{image}") public ResponseEntity<Map<String, Object>> getSecurityReport( @PathVariable String image, @RequestParam(defaultValue = "latest") String tag) { Map<String, Object> response = new HashMap<>(); String fullImage = image + ":" + tag; try { var report = anchoreService.getSecurityReport(fullImage); var vulnSummary = report.getVulnerabilityReport().getVulnerabilitySummary(); response.put("success", true); response.put("image", fullImage); response.put("digest", report.getImageDigest()); // Vulnerability summary Map<String, Object> vulnerabilities = new HashMap<>(); vulnerabilities.put("critical", vulnSummary.getCritical()); vulnerabilities.put("high", vulnSummary.getHigh()); vulnerabilities.put("medium", vulnSummary.getMedium()); vulnerabilities.put("low", vulnSummary.getLow()); vulnerabilities.put("total", vulnSummary.getTotal()); response.put("vulnerabilities", vulnerabilities); // Policy status boolean policyPassed = report.getPolicyEvaluation().getPolicyResults().stream() .allMatch(result -> "pass".equals(result.getStatus())); response.put("policyPassed", policyPassed); return ResponseEntity.ok(response); } catch (Exception e) { logger.error("Security report generation failed: {}", e.getMessage(), e); response.put("success", false); response.put("error", e.getMessage()); return ResponseEntity.badRequest().body(response); } } @GetMapping("/compliance/{image}") public ResponseEntity<Map<String, Object>> checkCompliance( @PathVariable String image, @RequestParam(defaultValue = "latest") String tag) { Map<String, Object> response = new HashMap<>(); String fullImage = image + ":" + tag; try { var result = anchoreService.checkPolicyCompliance(fullImage); response.put("success", true); response.put("image", fullImage); response.put("policyPassed", result.isPassed()); response.put("failedPolicies", result.getFailedPolicies()); return ResponseEntity.ok(response); } catch (Exception e) { logger.error("Compliance check failed: {}", e.getMessage(), e); response.put("success", false); response.put("error", e.getMessage()); return ResponseEntity.badRequest().body(response); } } @GetMapping("/sbom/{image}") public ResponseEntity<Map<String, Object>> getSBOM( @PathVariable String image, @RequestParam(defaultValue = "latest") String tag) { Map<String, Object> response = new HashMap<>(); String fullImage = image + ":" + tag; try { var sbom = anchoreService.generateSBOM(fullImage); response.put("success", true); response.put("image", fullImage); response.put("digest", sbom.getImageDigest()); response.put("componentCount", sbom.getComponents().size()); response.put("components", sbom.getComponents()); return ResponseEntity.ok(response); } catch (Exception e) { logger.error("SBOM generation failed: {}", e.getMessage(), e); response.put("success", false); response.put("error", e.getMessage()); return ResponseEntity.badRequest().body(response); } } @GetMapping("/critical/{image}") public ResponseEntity<Map<String, Object>> getCriticalVulnerabilities( @PathVariable String image, @RequestParam(defaultValue = "latest") String tag) { Map<String, Object> response = new HashMap<>(); String fullImage = image + ":" + tag; try { var criticalVulns = anchoreService.getCriticalVulnerabilities(fullImage); response.put("success", true); response.put("image", fullImage); response.put("criticalVulnerabilities", criticalVulns.size()); response.put("vulnerabilities", criticalVulns); return ResponseEntity.ok(response); } catch (Exception e) { logger.error("Critical vulnerabilities check failed: {}", e.getMessage(), e); response.put("success", false); response.put("error", e.getMessage()); return ResponseEntity.badRequest().body(response); } } } CI/CD Pipeline Integration
Jenkins Pipeline Integration
package com.anchore.jenkins; import com.anchore.service.AnchoreService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JenkinsAnchoreIntegration { private static final Logger logger = LoggerFactory.getLogger(JenkinsAnchoreIntegration.class); private final AnchoreService anchoreService; private final String breakBuildOnCritical; private final String breakBuildOnHigh; private final String breakBuildOnPolicyFail; public JenkinsAnchoreIntegration(AnchoreService anchoreService, String breakBuildOnCritical, String breakBuildOnHigh, String breakBuildOnPolicyFail) { this.anchoreService = anchoreService; this.breakBuildOnCritical = breakBuildOnCritical; this.breakBuildOnHigh = breakBuildOnHigh; this.breakBuildOnPolicyFail = breakBuildOnPolicyFail; } /** * Execute security gate in CI/CD pipeline */ public PipelineResult executeSecurityGate(String imageTag) { PipelineResult result = new PipelineResult(); try { // Get security report var report = anchoreService.getSecurityReport(imageTag); var vulnSummary = report.getVulnerabilityReport().getVulnerabilitySummary(); // Check vulnerabilities if ("true".equals(breakBuildOnCritical) && vulnSummary.getCritical() > 0) { result.setPassed(false); result.setReason("Critical vulnerabilities found: " + vulnSummary.getCritical()); } else if ("true".equals(breakBuildOnHigh) && vulnSummary.getHigh() > 0) { result.setPassed(false); result.setReason("High vulnerabilities found: " + vulnSummary.getHigh()); } // Check policy compliance if ("true".equals(breakBuildOnPolicyFail)) { var policyResult = anchoreService.checkPolicyCompliance(imageTag); if (!policyResult.isPassed()) { result.setPassed(false); result.setReason("Policy compliance failed: " + String.join(", ", policyResult.getFailedPolicies())); } } // Set report data result.setVulnerabilitySummary(vulnSummary); result.setPolicyEvaluation(report.getPolicyEvaluation()); if (result.isPassed()) { logger.info("Security gate passed for image: {}", imageTag); } else { logger.warn("Security gate failed for image: {} - {}", imageTag, result.getReason()); } } catch (Exception e) { result.setPassed(false); result.setReason("Security analysis failed: " + e.getMessage()); logger.error("Security gate execution failed: {}", e.getMessage(), e); } return result; } public static class PipelineResult { private boolean passed = true; private String reason; private VulnerabilitySummary vulnerabilitySummary; private PolicyEvaluation policyEvaluation; // Getters and setters public boolean isPassed() { return passed; } public void setPassed(boolean passed) { this.passed = passed; } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } public VulnerabilitySummary getVulnerabilitySummary() { return vulnerabilitySummary; } public void setVulnerabilitySummary(VulnerabilitySummary vulnerabilitySummary) { this.vulnerabilitySummary = vulnerabilitySummary; } public PolicyEvaluation getPolicyEvaluation() { return policyEvaluation; } public void setPolicyEvaluation(PolicyEvaluation policyEvaluation) { this.policyEvaluation = policyEvaluation; } } } Advanced Features
Custom Policy Management
package com.anchore.policy; import com.anchore.client.AnchoreClient; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class PolicyManager { private static final Logger logger = LoggerFactory.getLogger(PolicyManager.class); private final AnchoreClient client; private final ObjectMapper objectMapper; public PolicyManager(AnchoreClient client) { this.client = client; this.objectMapper = new ObjectMapper(); } /** * Create a custom security policy */ public boolean createPolicy(String policyId, String policyName, PolicyRules rules) throws IOException { Map<String, Object> policyData = new HashMap<>(); policyData.put("policyId", policyId); policyData.put("name", policyName); policyData.put("version", "1.0"); policyData.put("comment", "Custom security policy"); policyData.put("rules", rules.toMap()); // This would use the actual Anchore policy API // For demonstration, we're showing the structure logger.info("Creating policy: {} with rules: {}", policyName, rules); return true; } /** * Policy rules builder */ public static class PolicyRules { private boolean blockCriticalVulns = true; private boolean blockHighVulns = false; private boolean requireNonRoot = true; private boolean blockSecrets = true; private int maxImageAgeDays = 90; public Map<String, Object> toMap() { Map<String, Object> rules = new HashMap<>(); // Vulnerability rules if (blockCriticalVulns) { rules.put("vulnerabilities_critical", "stop"); } if (blockHighVulns) { rules.put("vulnerabilities_high", "stop"); } // Security rules if (requireNonRoot) { rules.put("dockerfile_user", "warn"); } if (blockSecrets) { rules.put("secret_scan", "stop"); } // Image age rule rules.put("freshness", maxImageAgeDays + " days"); return rules; } // Builder methods public PolicyRules blockCriticalVulns(boolean block) { this.blockCriticalVulns = block; return this; } public PolicyRules blockHighVulns(boolean block) { this.blockHighVulns = block; return this; } public PolicyRules requireNonRoot(boolean require) { this.requireNonRoot = require; return this; } public PolicyRules blockSecrets(boolean block) { this.blockSecrets = block; return this; } public PolicyRules maxImageAgeDays(int days) { this.maxImageAgeDays = days; return this; } } } Testing
Unit Tests
package com.anchore.test; import com.anchore.client.AnchoreClient; import com.anchore.service.AnchoreService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; class AnchoreServiceTest { @Mock private AnchoreClient mockClient; private AnchoreService anchoreService; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); anchoreService = new AnchoreService("http://localhost:8228", "admin", "password", 30000); // Would use reflection to inject mock client in real implementation } @Test void testServiceAvailable() { // Test service availability check // Implementation would mock the client calls } @Test void testImageAnalysis() { // Test image analysis flow // Mock client responses for addImage, getImageStatus, waitForAnalysis } @Test void testPolicyComplianceCheck() { // Test policy compliance checking // Mock policy evaluation responses } @Test void testVulnerabilityReporting() { // Test vulnerability report generation // Mock vulnerability data } } Application Properties
Spring Boot Configuration
# Anchore Engine Configuration anchore.engine.url=http://localhost:8228 anchore.engine.username=admin anchore.engine.password=foobar anchore.analysis.timeout.ms=300000 # Security Gate Settings security.gate.break-on-critical=true security.gate.break-on-high=false security.gate.break-on-policy-fail=true # Logging logging.level.com.anchore=DEBUG
Conclusion
This comprehensive Anchore Engine integration for Java provides:
- Complete API client for Anchore Engine
- Security scanning service for container images
- Policy compliance checking with customizable rules
- Vulnerability reporting with severity filtering
- SBOM generation for software transparency
- CI/CD integration for pipeline security gates
- Spring Boot integration for easy configuration
Key benefits:
- Automated security scanning in development pipelines
- Policy-based compliance enforcement
- Comprehensive vulnerability reporting
- Software supply chain transparency
- Production-ready integration
This implementation enables Java applications to leverage Anchore Engine for container security, making it ideal for DevOps teams implementing security in their CI/CD pipelines and runtime environments.