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.