Quay Security in Java: Complete Container Security Guide

Introduction to Quay Security

Quay Security refers to the security features and scanning capabilities of Red Hat Quay, a container registry that provides vulnerability scanning, security policy enforcement, and image signing. It helps secure container images throughout the development lifecycle.

Key Features

  • Vulnerability Scanning: Automated CVE scanning of container images
  • Security Policies: Enforce security standards across teams
  • Image Signing: Cryptographic verification of image integrity
  • Access Control: Fine-grained permissions and RBAC
  • Compliance Reporting: Generate security compliance reports

Implementation Guide

Dependencies

Add to your pom.xml:

<properties> <quay.client.version>1.0.0</quay.client.version> <jackson.version>2.15.2</jackson.version> <okhttp.version>4.11.0</okhttp.version> <jose4j.version>0.9.3</jose4j.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-databind</artifactId> <version>${jackson.version}</version> </dependency> <!-- JWT for authentication --> <dependency> <groupId>org.bitbucket.b_c</groupId> <artifactId>jose4j</artifactId> <version>${jose4j.version}</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.7</version> </dependency> <!-- Docker Java API --> <dependency> <groupId>com.github.docker-java</groupId> <artifactId>docker-java</artifactId> <version>3.3.0</version> </dependency> <!-- For file operations --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.13.0</version> </dependency> </dependencies>

Core Quay Security Implementation

Quay Client Setup

package com.example.quay.security; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeUnit; public class QuayClient { private static final Logger logger = LoggerFactory.getLogger(QuayClient.class); private final OkHttpClient httpClient; private final ObjectMapper objectMapper; private final String baseUrl; private final String authToken; public QuayClient(String baseUrl, String authToken, int timeoutSeconds) { this.baseUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/"; this.authToken = authToken; this.objectMapper = new ObjectMapper(); this.httpClient = new OkHttpClient.Builder() .connectTimeout(timeoutSeconds, TimeUnit.SECONDS) .readTimeout(timeoutSeconds, TimeUnit.SECONDS) .writeTimeout(timeoutSeconds, TimeUnit.SECONDS) .addInterceptor(new AuthInterceptor(authToken)) .build(); logger.info("Quay client initialized for: {}", baseUrl); } public QuayClient(String baseUrl, String username, String password, int timeoutSeconds) { this(baseUrl, generateBasicAuthToken(username, password), timeoutSeconds); } private static String generateBasicAuthToken(String username, String password) { String credentials = username + ":" + password; return "Basic " + java.util.Base64.getEncoder().encodeToString(credentials.getBytes()); } public boolean testConnection() throws IOException { Request request = new Request.Builder() .url(baseUrl + "api/v1/discovery") .get() .build(); try (Response response = httpClient.newCall(request).execute()) { return response.isSuccessful(); } } // Repository operations public RepositoryList listRepositories(String namespace) throws IOException { String url = namespace != null ? baseUrl + "api/v1/repository?namespace=" + namespace : baseUrl + "api/v1/repository"; Request request = new Request.Builder() .url(url) .get() .build(); try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Failed to list repositories: " + response.code()); } ResponseBody body = response.body(); if (body != null) { return objectMapper.readValue(body.string(), RepositoryList.class); } throw new IOException("Empty response body"); } } public RepositoryDetail getRepository(String namespace, String repository) throws IOException { String url = baseUrl + "api/v1/repository/" + namespace + "/" + repository; Request request = new Request.Builder() .url(url) .get() .build(); try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Failed to get repository: " + response.code()); } ResponseBody body = response.body(); if (body != null) { return objectMapper.readValue(body.string(), RepositoryDetail.class); } throw new IOException("Empty response body"); } } // Security scanning operations public SecurityScanResult getSecurityScan(String namespace, String repository, String tag) throws IOException { String url = baseUrl + "api/v1/repository/" + namespace + "/" + repository + "/manifest/" + tag + "/security"; Request request = new Request.Builder() .url(url) .get() .build(); try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Failed to get security scan: " + response.code()); } ResponseBody body = response.body(); if (body != null) { return objectMapper.readValue(body.string(), SecurityScanResult.class); } throw new IOException("Empty response body"); } } public void triggerSecurityScan(String namespace, String repository, String tag) throws IOException { String url = baseUrl + "api/v1/repository/" + namespace + "/" + repository + "/manifest/" + tag + "/security"; RequestBody body = RequestBody.create("{}", MediaType.parse("application/json")); Request request = new Request.Builder() .url(url) .post(body) .build(); try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Failed to trigger security scan: " + response.code()); } } } // Vulnerability operations public VulnerabilityReport getVulnerabilityReport(String namespace, String repository, String tag) throws IOException { SecurityScanResult scanResult = getSecurityScan(namespace, repository, tag); return new VulnerabilityReport(namespace, repository, tag, scanResult); } public ImageSigningStatus getSigningStatus(String namespace, String repository, String tag) throws IOException { String url = baseUrl + "api/v1/repository/" + namespace + "/" + repository + "/manifest/" + tag + "/signatures"; Request request = new Request.Builder() .url(url) .get() .build(); try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Failed to get signing status: " + response.code()); } ResponseBody body = response.body(); if (body != null) { return objectMapper.readValue(body.string(), ImageSigningStatus.class); } throw new IOException("Empty response body"); } } // Close the client public void close() { if (httpClient != null) { httpClient.dispatcher().executorService().shutdown(); httpClient.connectionPool().evictAll(); } } // Authentication interceptor private static class AuthInterceptor implements Interceptor { private final String authToken; public AuthInterceptor(String authToken) { this.authToken = authToken; } @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); Request authenticatedRequest = originalRequest.newBuilder() .header("Authorization", authToken) .header("Content-Type", "application/json") .build(); return chain.proceed(authenticatedRequest); } } }

Data Models

package com.example.quay.security; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import java.util.Map; // Repository models @JsonIgnoreProperties(ignoreUnknown = true) class RepositoryList { @JsonProperty("repositories") private List<Repository> repositories; public List<Repository> getRepositories() { return repositories; } public void setRepositories(List<Repository> repositories) { this.repositories = repositories; } } @JsonIgnoreProperties(ignoreUnknown = true) class Repository { @JsonProperty("namespace") private String namespace; @JsonProperty("name") private String name; @JsonProperty("description") private String description; @JsonProperty("is_public") private boolean isPublic; @JsonProperty("last_modified") private long lastModified; // Getters and setters public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isPublic() { return isPublic; } public void setPublic(boolean isPublic) { this.isPublic = isPublic; } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } } @JsonIgnoreProperties(ignoreUnknown = true) class RepositoryDetail { @JsonProperty("namespace") private String namespace; @JsonProperty("name") private String name; @JsonProperty("description") private String description; @JsonProperty("is_public") private boolean isPublic; @JsonProperty("is_organization") private boolean isOrganization; @JsonProperty("tags") private Map<String, TagInfo> tags; @JsonProperty("security_scanning_enabled") private boolean securityScanningEnabled; // Getters and setters public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isPublic() { return isPublic; } public void setPublic(boolean isPublic) { this.isPublic = isPublic; } public boolean isOrganization() { return isOrganization; } public void setOrganization(boolean isOrganization) { this.isOrganization = isOrganization; } public Map<String, TagInfo> getTags() { return tags; } public void setTags(Map<String, TagInfo> tags) { this.tags = tags; } public boolean isSecurityScanningEnabled() { return securityScanningEnabled; } public void setSecurityScanningEnabled(boolean securityScanningEnabled) { this.securityScanningEnabled = securityScanningEnabled; } } @JsonIgnoreProperties(ignoreUnknown = true) class TagInfo { @JsonProperty("name") private String name; @JsonProperty("manifest_digest") private String manifestDigest; @JsonProperty("size") private long size; @JsonProperty("last_modified") private String lastModified; @JsonProperty("is_manifest_list") private boolean isManifestList; // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getManifestDigest() { return manifestDigest; } public void setManifestDigest(String manifestDigest) { this.manifestDigest = manifestDigest; } public long getSize() { return size; } public void setSize(long size) { this.size = size; } public String getLastModified() { return lastModified; } public void setLastModified(String lastModified) { this.lastModified = lastModified; } public boolean isManifestList() { return isManifestList; } public void setManifestList(boolean isManifestList) { this.isManifestList = isManifestList; } } // Security scanning models @JsonIgnoreProperties(ignoreUnknown = true) class SecurityScanResult { @JsonProperty("status") private String status; @JsonProperty("data") private SecurityScanData data; @JsonProperty("scanned_by") private String scannedBy; @JsonProperty("scanned_date") private String scannedDate; // Getters and setters public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public SecurityScanData getData() { return data; } public void setData(SecurityScanData data) { this.data = data; } public String getScannedBy() { return scannedBy; } public void setScannedBy(String scannedBy) { this.scannedBy = scannedBy; } public String getScannedDate() { return scannedDate; } public void setScannedDate(String scannedDate) { this.scannedDate = scannedDate; } } @JsonIgnoreProperties(ignoreUnknown = true) class SecurityScanData { @JsonProperty("Layer") private Layer layer; @JsonProperty("Metadata") private ScanMetadata metadata; // Getters and setters public Layer getLayer() { return layer; } public void setLayer(Layer layer) { this.layer = layer; } public ScanMetadata getMetadata() { return metadata; } public void setMetadata(ScanMetadata metadata) { this.metadata = metadata; } } @JsonIgnoreProperties(ignoreUnknown = true) class Layer { @JsonProperty("Name") private String name; @JsonProperty("ParentName") private String parentName; @JsonProperty("NamespaceName") private String namespaceName; @JsonProperty("IndexedByVersion") private int indexedByVersion; @JsonProperty("Features") private List<Feature> features; // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getParentName() { return parentName; } public void setParentName(String parentName) { this.parentName = parentName; } public String getNamespaceName() { return namespaceName; } public void setNamespaceName(String namespaceName) { this.namespaceName = namespaceName; } public int getIndexedByVersion() { return indexedByVersion; } public void setIndexedByVersion(int indexedByVersion) { this.indexedByVersion = indexedByVersion; } public List<Feature> getFeatures() { return features; } public void setFeatures(List<Feature> features) { this.features = features; } } @JsonIgnoreProperties(ignoreUnknown = true) class Feature { @JsonProperty("Name") private String name; @JsonProperty("Version") private String version; @JsonProperty("VersionFormat") private String versionFormat; @JsonProperty("Vulnerabilities") private List<Vulnerability> vulnerabilities; @JsonProperty("AddedBy") private String addedBy; // Getters and setters 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 getVersionFormat() { return versionFormat; } public void setVersionFormat(String versionFormat) { this.versionFormat = versionFormat; } public List<Vulnerability> getVulnerabilities() { return vulnerabilities; } public void setVulnerabilities(List<Vulnerability> vulnerabilities) { this.vulnerabilities = vulnerabilities; } public String getAddedBy() { return addedBy; } public void setAddedBy(String addedBy) { this.addedBy = addedBy; } } @JsonIgnoreProperties(ignoreUnknown = true) class Vulnerability { @JsonProperty("Name") private String name; @JsonProperty("NamespaceName") private String namespaceName; @JsonProperty("Description") private String description; @JsonProperty("Link") private String link; @JsonProperty("Severity") private String severity; @JsonProperty("Metadata") private VulnerabilityMetadata metadata; @JsonProperty("FixedBy") private String fixedBy; // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNamespaceName() { return namespaceName; } public void setNamespaceName(String namespaceName) { this.namespaceName = namespaceName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public VulnerabilityMetadata getMetadata() { return metadata; } public void setMetadata(VulnerabilityMetadata metadata) { this.metadata = metadata; } public String getFixedBy() { return fixedBy; } public void setFixedBy(String fixedBy) { this.fixedBy = fixedBy; } } @JsonIgnoreProperties(ignoreUnknown = true) class VulnerabilityMetadata { @JsonProperty("UpdatedBy") private String updatedBy; @JsonProperty("RepoName") private String repoName; @JsonProperty("RepoLink") private String repoLink; @JsonProperty("DistroName") private String distroName; @JsonProperty("DistroVersion") private String distroVersion; @JsonProperty("NVD") private NVDMetadata nvd; // Getters and setters public String getUpdatedBy() { return updatedBy; } public void setUpdatedBy(String updatedBy) { this.updatedBy = updatedBy; } public String getRepoName() { return repoName; } public void setRepoName(String repoName) { this.repoName = repoName; } public String getRepoLink() { return repoLink; } public void setRepoLink(String repoLink) { this.repoLink = repoLink; } public String getDistroName() { return distroName; } public void setDistroName(String distroName) { this.distroName = distroName; } public String getDistroVersion() { return distroVersion; } public void setDistroVersion(String distroVersion) { this.distroVersion = distroVersion; } public NVDMetadata getNvd() { return nvd; } public void setNvd(NVDMetadata nvd) { this.nvd = nvd; } } @JsonIgnoreProperties(ignoreUnknown = true) class NVDMetadata { @JsonProperty("CVSSv2") private CVSSv2 cvssv2; @JsonProperty("CVSSv3") private CVSSv3 cvssv3; // Getters and setters public CVSSv2 getCvssv2() { return cvssv2; } public void setCvssv2(CVSSv2 cvssv2) { this.cvssv2 = cvssv2; } public CVSSv3 getCvssv3() { return cvssv3; } public void setCvssv3(CVSSv3 cvssv3) { this.cvssv3 = cvssv3; } } @JsonIgnoreProperties(ignoreUnknown = true) class CVSSv2 { @JsonProperty("Score") private double score; @JsonProperty("Vectors") private String vectors; // Getters and setters public double getScore() { return score; } public void setScore(double score) { this.score = score; } public String getVectors() { return vectors; } public void setVectors(String vectors) { this.vectors = vectors; } } @JsonIgnoreProperties(ignoreUnknown = true) class CVSSv3 { @JsonProperty("Score") private double score; @JsonProperty("Vectors") private String vectors; // Getters and setters public double getScore() { return score; } public void setScore(double score) { this.score = score; } public String getVectors() { return vectors; } public void setVectors(String vectors) { this.vectors = vectors; } } @JsonIgnoreProperties(ignoreUnknown = true) class ScanMetadata { @JsonProperty("ScannerVersion") private String scannerVersion; @JsonProperty("ScanStatus") private String scanStatus; @JsonProperty("ReportStatus") private String reportStatus; // Getters and setters public String getScannerVersion() { return scannerVersion; } public void setScannerVersion(String scannerVersion) { this.scannerVersion = scannerVersion; } public String getScanStatus() { return scanStatus; } public void setScanStatus(String scanStatus) { this.scannerVersion = scanStatus; } public String getReportStatus() { return reportStatus; } public void setReportStatus(String reportStatus) { this.reportStatus = reportStatus; } } // Image signing models @JsonIgnoreProperties(ignoreUnknown = true) class ImageSigningStatus { @JsonProperty("signatures") private List<Signature> signatures; @JsonProperty("is_signed") private boolean isSigned; // Getters and setters public List<Signature> getSignatures() { return signatures; } public void setSignatures(List<Signature> signatures) { this.signatures = signatures; } public boolean isSigned() { return isSigned; } public void setSigned(boolean isSigned) { this.isSigned = isSigned; } } @JsonIgnoreProperties(ignoreUnknown = true) class Signature { @JsonProperty("signer") private String signer; @JsonProperty("timestamp") private String timestamp; @JsonProperty("method") private String method; // Getters and setters public String getSigner() { return signer; } public void setSigner(String signer) { this.signer = signer; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } }

Security Scanner Implementation

Vulnerability Analysis Service

package com.example.quay.security; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.stream.Collectors; public class VulnerabilityAnalyzer { private static final Logger logger = LoggerFactory.getLogger(VulnerabilityAnalyzer.class); public VulnerabilityReport analyzeSecurityScan(String namespace, String repository, String tag, SecurityScanResult scanResult) { logger.info("Analyzing security scan for {}/{}/{}", namespace, repository, tag); VulnerabilityReport report = new VulnerabilityReport(namespace, repository, tag); if (scanResult == null || scanResult.getData() == null) { report.setStatus("NO_SCAN_DATA"); return report; } if (!"scanned".equals(scanResult.getStatus())) { report.setStatus(scanResult.getStatus().toUpperCase()); return report; } report.setStatus("SCANNED"); report.setScannedBy(scanResult.getScannedBy()); report.setScannedDate(scanResult.getScannedDate()); // Extract vulnerabilities from scan result extractVulnerabilities(scanResult, report); // Calculate summary calculateSummary(report); logger.info("Analysis complete: {} vulnerabilities found", report.getTotalVulnerabilities()); return report; } private void extractVulnerabilities(SecurityScanResult scanResult, VulnerabilityReport report) { SecurityScanData data = scanResult.getData(); if (data == null || data.getLayer() == null) { return; } Layer layer = data.getLayer(); if (layer.getFeatures() == null) { return; } for (Feature feature : layer.getFeatures()) { if (feature.getVulnerabilities() != null) { for (Vulnerability vulnerability : feature.getVulnerabilities()) { VulnerabilityDetail vulnDetail = createVulnerabilityDetail(feature, vulnerability); report.addVulnerability(vulnDetail); } } } } private VulnerabilityDetail createVulnerabilityDetail(Feature feature, Vulnerability vulnerability) { VulnerabilityDetail detail = new VulnerabilityDetail(); detail.setId(vulnerability.getName()); detail.setPackageName(feature.getName()); detail.setPackageVersion(feature.getVersion()); detail.setSeverity(vulnerability.getSeverity()); detail.setDescription(vulnerability.getDescription()); detail.setLink(vulnerability.getLink()); detail.setFixedBy(vulnerability.getFixedBy()); detail.setAddedBy(feature.getAddedBy()); // Extract CVSS scores if (vulnerability.getMetadata() != null && vulnerability.getMetadata().getNvd() != null) { NVDMetadata nvd = vulnerability.getMetadata().getNvd(); if (nvd.getCvssv2() != null) { detail.setCvssV2Score(nvd.getCvssv2().getScore()); } if (nvd.getCvssv3() != null) { detail.setCvssV3Score(nvd.getCvssv3().getScore()); } } return detail; } private void calculateSummary(VulnerabilityReport report) { Map<String, Integer> severityCount = new HashMap<>(); int totalVulnerabilities = 0; for (VulnerabilityDetail vuln : report.getVulnerabilities()) { String severity = vuln.getSeverity(); severityCount.put(severity, severityCount.getOrDefault(severity, 0) + 1); totalVulnerabilities++; } report.setVulnerabilitySummary(severityCount); report.setTotalVulnerabilities(totalVulnerabilities); // Calculate risk score (weighted by severity) double riskScore = calculateRiskScore(severityCount); report.setRiskScore(riskScore); // Determine overall status String overallStatus = determineOverallStatus(severityCount, riskScore); report.setOverallStatus(overallStatus); } private double calculateRiskScore(Map<String, Integer> severityCount) { double score = 0.0; // Weighted scoring score += severityCount.getOrDefault("Critical", 0) * 10.0; score += severityCount.getOrDefault("High", 0) * 7.5; score += severityCount.getOrDefault("Medium", 0) * 5.0; score += severityCount.getOrDefault("Low", 0) * 2.5; score += severityCount.getOrDefault("Negligible", 0) * 1.0; return score; } private String determineOverallStatus(Map<String, Integer> severityCount, double riskScore) { if (severityCount.getOrDefault("Critical", 0) > 0) { return "CRITICAL"; } else if (severityCount.getOrDefault("High", 0) > 0) { return "HIGH"; } else if (riskScore > 20.0) { return "MEDIUM"; } else if (riskScore > 5.0) { return "LOW"; } else { return "CLEAN"; } } public List<VulnerabilityDetail> getCriticalVulnerabilities(VulnerabilityReport report) { return report.getVulnerabilities().stream() .filter(vuln -> "Critical".equalsIgnoreCase(vuln.getSeverity())) .collect(Collectors.toList()); } public List<VulnerabilityDetail> getFixableVulnerabilities(VulnerabilityReport report) { return report.getVulnerabilities().stream() .filter(vuln -> vuln.getFixedBy() != null && !vuln.getFixedBy().isEmpty()) .collect(Collectors.toList()); } public Map<String, List<VulnerabilityDetail>> groupByPackage(VulnerabilityReport report) { return report.getVulnerabilities().stream() .collect(Collectors.groupingBy(VulnerabilityDetail::getPackageName)); } public SecurityCompliance checkCompliance(VulnerabilityReport report, SecurityPolicy policy) { SecurityCompliance compliance = new SecurityCompliance(); compliance.setReport(report); compliance.setPolicy(policy); // Check critical vulnerabilities List<VulnerabilityDetail> criticalVulns = getCriticalVulnerabilities(report); if (!criticalVulns.isEmpty() && !policy.isAllowCritical()) { compliance.setCompliant(false); compliance.getViolations().add("Critical vulnerabilities found: " + criticalVulns.size()); } // Check high vulnerabilities List<VulnerabilityDetail> highVulns = report.getVulnerabilities().stream() .filter(vuln -> "High".equalsIgnoreCase(vuln.getSeverity())) .collect(Collectors.toList()); if (highVulns.size() > policy.getMaxHighVulnerabilities()) { compliance.setCompliant(false); compliance.getViolations().add("High vulnerabilities exceed limit: " + highVulns.size() + " > " + policy.getMaxHighVulnerabilities()); } // Check risk score if (report.getRiskScore() > policy.getMaxRiskScore()) { compliance.setCompliant(false); compliance.getViolations().add("Risk score exceeds limit: " + report.getRiskScore() + " > " + policy.getMaxRiskScore()); } if (compliance.getViolations().isEmpty()) { compliance.setCompliant(true); } return compliance; } } // Enhanced report models class VulnerabilityReport { private final String namespace; private final String repository; private final String tag; private String status; private String scannedBy; private String scannedDate; private String overallStatus; private double riskScore; private int totalVulnerabilities; private Map<String, Integer> vulnerabilitySummary; private List<VulnerabilityDetail> vulnerabilities; public VulnerabilityReport(String namespace, String repository, String tag) { this.namespace = namespace; this.repository = repository; this.tag = tag; this.vulnerabilities = new ArrayList<>(); this.vulnerabilitySummary = new HashMap<>(); } public void addVulnerability(VulnerabilityDetail vulnerability) { vulnerabilities.add(vulnerability); } // Getters and setters public String getNamespace() { return namespace; } public String getRepository() { return repository; } public String getTag() { return tag; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getScannedBy() { return scannedBy; } public void setScannedBy(String scannedBy) { this.scannedBy = scannedBy; } public String getScannedDate() { return scannedDate; } public void setScannedDate(String scannedDate) { this.scannedDate = scannedDate; } public String getOverallStatus() { return overallStatus; } public void setOverallStatus(String overallStatus) { this.overallStatus = overallStatus; } public double getRiskScore() { return riskScore; } public void setRiskScore(double riskScore) { this.riskScore = riskScore; } public int getTotalVulnerabilities() { return totalVulnerabilities; } public void setTotalVulnerabilities(int totalVulnerabilities) { this.totalVulnerabilities = totalVulnerabilities; } public Map<String, Integer> getVulnerabilitySummary() { return vulnerabilitySummary; } public void setVulnerabilitySummary(Map<String, Integer> vulnerabilitySummary) { this.vulnerabilitySummary = vulnerabilitySummary; } public List<VulnerabilityDetail> getVulnerabilities() { return vulnerabilities; } public void setVulnerabilities(List<VulnerabilityDetail> vulnerabilities) { this.vulnerabilities = vulnerabilities; } public String getImageReference() { return namespace + "/" + repository + ":" + tag; } } class VulnerabilityDetail { private String id; private String packageName; private String packageVersion; private String severity; private String description; private String link; private String fixedBy; private String addedBy; private Double cvssV2Score; private Double cvssV3Score; // Getters and setters public String getId() { return id; } public void setId(String id) { this.id = id; } 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 getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getFixedBy() { return fixedBy; } public void setFixedBy(String fixedBy) { this.fixedBy = fixedBy; } public String getAddedBy() { return addedBy; } public void setAddedBy(String addedBy) { this.addedBy = addedBy; } public Double getCvssV2Score() { return cvssV2Score; } public void setCvssV2Score(Double cvssV2Score) { this.cvssV2Score = cvssV2Score; } public Double getCvssV3Score() { return cvssV3Score; } public void setCvssV3Score(Double cvssV3Score) { this.cvssV3Score = cvssV3Score; } public boolean isFixable() { return fixedBy != null && !fixedBy.isEmpty(); } } class SecurityPolicy { private boolean allowCritical = false; private int maxHighVulnerabilities = 0; private int maxMediumVulnerabilities = 10; private int maxLowVulnerabilities = 50; private double maxRiskScore = 25.0; private boolean requireSigned = false; private List<String> allowedLicenses = Arrays.asList("Apache-2.0", "MIT", "BSD-3-Clause"); private List<String> bannedPackages = new ArrayList<>(); // Getters and setters public boolean isAllowCritical() { return allowCritical; } public void setAllowCritical(boolean allowCritical) { this.allowCritical = allowCritical; } public int getMaxHighVulnerabilities() { return maxHighVulnerabilities; } public void setMaxHighVulnerabilities(int maxHighVulnerabilities) { this.maxHighVulnerabilities = maxHighVulnerabilities; } public int getMaxMediumVulnerabilities() { return maxMediumVulnerabilities; } public void setMaxMediumVulnerabilities(int maxMediumVulnerabilities) { this.maxMediumVulnerabilities = maxMediumVulnerabilities; } public int getMaxLowVulnerabilities() { return maxLowVulnerabilities; } public void setMaxLowVulnerabilities(int maxLowVulnerabilities) { this.maxLowVulnerabilities = maxLowVulnerabilities; } public double getMaxRiskScore() { return maxRiskScore; } public void setMaxRiskScore(double maxRiskScore) { this.maxRiskScore = maxRiskScore; } public boolean isRequireSigned() { return requireSigned; } public void setRequireSigned(boolean requireSigned) { this.requireSigned = requireSigned; } public List<String> getAllowedLicenses() { return allowedLicenses; } public void setAllowedLicenses(List<String> allowedLicenses) { this.allowedLicenses = allowedLicenses; } public List<String> getBannedPackages() { return bannedPackages; } public void setBannedPackages(List<String> bannedPackages) { this.bannedPackages = bannedPackages; } } class SecurityCompliance { private VulnerabilityReport report; private SecurityPolicy policy; private boolean isCompliant; private List<String> violations; public SecurityCompliance() { this.violations = new ArrayList<>(); } // Getters and setters public VulnerabilityReport getReport() { return report; } public void setReport(VulnerabilityReport report) { this.report = report; } public SecurityPolicy getPolicy() { return policy; } public void setPolicy(SecurityPolicy policy) { this.policy = policy; } public boolean isCompliant() { return isCompliant; } public void setCompliant(boolean isCompliant) { this.isCompliant = isCompliant; } public List<String> getViolations() { return violations; } public void setViolations(List<String> violations) { this.violations = violations; } }

Image Security Manager

package com.example.quay.security; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.*; public class ImageSecurityManager { private static final Logger logger = LoggerFactory.getLogger(ImageSecurityManager.class); private final QuayClient quayClient; private final VulnerabilityAnalyzer vulnerabilityAnalyzer; public ImageSecurityManager(QuayClient quayClient, VulnerabilityAnalyzer vulnerabilityAnalyzer) { this.quayClient = quayClient; this.vulnerabilityAnalyzer = vulnerabilityAnalyzer; } public SecurityAssessment assessImage(String namespace, String repository, String tag) throws IOException { logger.info("Assessing security for {}/{}/{}", namespace, repository, tag); SecurityAssessment assessment = new SecurityAssessment(namespace, repository, tag); // Get repository details RepositoryDetail repoDetail = quayClient.getRepository(namespace, repository); assessment.setRepositoryDetail(repoDetail); // Get security scan results SecurityScanResult scanResult = quayClient.getSecurityScan(namespace, repository, tag); VulnerabilityReport vulnReport = vulnerabilityAnalyzer.analyzeSecurityScan( namespace, repository, tag, scanResult); assessment.setVulnerabilityReport(vulnReport); // Get signing status ImageSigningStatus signingStatus = quayClient.getSigningStatus(namespace, repository, tag); assessment.setSigningStatus(signingStatus); // Calculate overall security score calculateSecurityScore(assessment); logger.info("Security assessment complete for {}/{}/{} - Score: {}", namespace, repository, tag, assessment.getSecurityScore()); return assessment; } public List<SecurityAssessment> assessRepository(String namespace, String repository) throws IOException { logger.info("Assessing security for repository {}/{}", namespace, repository); RepositoryDetail repoDetail = quayClient.getRepository(namespace, repository); List<SecurityAssessment> assessments = new ArrayList<>(); if (repoDetail.getTags() != null) { for (String tag : repoDetail.getTags().keySet()) { try { SecurityAssessment assessment = assessImage(namespace, repository, tag); assessments.add(assessment); } catch (Exception e) { logger.warn("Failed to assess image {}/{}:{}", namespace, repository, tag, e); } } } return assessments; } public SecurityCompliance checkCompliance(String namespace, String repository, String tag, SecurityPolicy policy) throws IOException { SecurityAssessment assessment = assessImage(namespace, repository, tag); return vulnerabilityAnalyzer.checkCompliance(assessment.getVulnerabilityReport(), policy); } public ComplianceReport checkRepositoryCompliance(String namespace, String repository, SecurityPolicy policy) throws IOException { List<SecurityAssessment> assessments = assessRepository(namespace, repository); ComplianceReport complianceReport = new ComplianceReport(namespace, repository, policy); for (SecurityAssessment assessment : assessments) { SecurityCompliance compliance = vulnerabilityAnalyzer.checkCompliance( assessment.getVulnerabilityReport(), policy); complianceReport.addImageCompliance(assessment.getTag(), compliance); } complianceReport.calculateSummary(); return complianceReport; } public void triggerSecurityScan(String namespace, String repository, String tag) throws IOException { logger.info("Triggering security scan for {}/{}/{}", namespace, repository, tag); quayClient.triggerSecurityScan(namespace, repository, tag); } public List<String> findVulnerableImages(String namespace, String severity) throws IOException { logger.info("Finding images with {} vulnerabilities in namespace {}", severity, namespace); RepositoryList repos = quayClient.listRepositories(namespace); List<String> vulnerableImages = new ArrayList<>(); for (Repository repo : repos.getRepositories()) { try { RepositoryDetail repoDetail = quayClient.getRepository( repo.getNamespace(), repo.getName()); if (repoDetail.getTags() != null) { for (String tag : repoDetail.getTags().keySet()) { try { SecurityScanResult scanResult = quayClient.getSecurityScan( repo.getNamespace(), repo.getName(), tag); if (scanResult != null && "scanned".equals(scanResult.getStatus())) { VulnerabilityReport report = vulnerabilityAnalyzer.analyzeSecurityScan( repo.getNamespace(), repo.getName(), tag, scanResult); if (hasSeverityVulnerabilities(report, severity)) { vulnerableImages.add(repo.getNamespace() + "/" + repo.getName() + ":" + tag); } } } catch (Exception e) { logger.debug("Skipping image {}/{}:{}", repo.getNamespace(), repo.getName(), tag, e); } } } } catch (Exception e) { logger.warn("Failed to process repository {}/{}", repo.getNamespace(), repo.getName(), e); } } return vulnerableImages; } private boolean hasSeverityVulnerabilities(VulnerabilityReport report, String severity) { if (report.getVulnerabilitySummary() == null) return false; return report.getVulnerabilitySummary().getOrDefault(severity, 0) > 0; } private void calculateSecurityScore(SecurityAssessment assessment) { double score = 100.0; // Start with perfect score VulnerabilityReport vulnReport = assessment.getVulnerabilityReport(); ImageSigningStatus signingStatus = assessment.getSigningStatus(); // Deduct for vulnerabilities if (vulnReport.getRiskScore() > 0) { score -= Math.min(vulnReport.getRiskScore() * 2, 60.0); } // Deduct for unsigned images if (signingStatus != null && !signingStatus.isSigned()) { score -= 10.0; } // Bonus for no vulnerabilities if (vulnReport.getTotalVulnerabilities() == 0) { score += 5.0; } // Ensure score is within bounds score = Math.max(0.0, Math.min(100.0, score)); assessment.setSecurityScore(score); // Set security level if (score >= 90.0) assessment.setSecurityLevel("EXCELLENT"); else if (score >= 75.0) assessment.setSecurityLevel("GOOD"); else if (score >= 60.0) assessment.setSecurityLevel("FAIR"); else if (score >= 40.0) assessment.setSecurityLevel("POOR"); else assessment.setSecurityLevel("CRITICAL"); } } // Assessment models class SecurityAssessment { private final String namespace; private final String repository; private final String tag; private RepositoryDetail repositoryDetail; private VulnerabilityReport vulnerabilityReport; private ImageSigningStatus signingStatus; private double securityScore; private String securityLevel; private Date assessmentDate; public SecurityAssessment(String namespace, String repository, String tag) { this.namespace = namespace; this.repository = repository; this.tag = tag; this.assessmentDate = new Date(); } // Getters and setters public String getNamespace() { return namespace; } public String getRepository() { return repository; } public String getTag() { return tag; } public RepositoryDetail getRepositoryDetail() { return repositoryDetail; } public void setRepositoryDetail(RepositoryDetail repositoryDetail) { this.repositoryDetail = repositoryDetail; } public VulnerabilityReport getVulnerabilityReport() { return vulnerabilityReport; } public void setVulnerabilityReport(VulnerabilityReport vulnerabilityReport) { this.vulnerabilityReport = vulnerabilityReport; } public ImageSigningStatus getSigningStatus() { return signingStatus; } public void setSigningStatus(ImageSigningStatus signingStatus) { this.signingStatus = signingStatus; } public double getSecurityScore() { return securityScore; } public void setSecurityScore(double securityScore) { this.securityScore = securityScore; } public String getSecurityLevel() { return securityLevel; } public void setSecurityLevel(String securityLevel) { this.securityLevel = securityLevel; } public Date getAssessmentDate() { return assessmentDate; } public void setAssessmentDate(Date assessmentDate) { this.assessmentDate = assessmentDate; } public String getImageReference() { return namespace + "/" + repository + ":" + tag; } } class ComplianceReport { private final String namespace; private final String repository; private final SecurityPolicy policy; private final Map<String, SecurityCompliance> imageCompliance; private int totalImages; private int compliantImages; private int nonCompliantImages; private Date reportDate; public ComplianceReport(String namespace, String repository, SecurityPolicy policy) { this.namespace = namespace; this.repository = repository; this.policy = policy; this.imageCompliance = new HashMap<>(); this.reportDate = new Date(); } public void addImageCompliance(String tag, SecurityCompliance compliance) { imageCompliance.put(tag, compliance); } public void calculateSummary() { totalImages = imageCompliance.size(); compliantImages = (int) imageCompliance.values().stream() .filter(SecurityCompliance::isCompliant) .count(); nonCompliantImages = totalImages - compliantImages; } // Getters public String getNamespace() { return namespace; } public String getRepository() { return repository; } public SecurityPolicy getPolicy() { return policy; } public Map<String, SecurityCompliance> getImageCompliance() { return imageCompliance; } public int getTotalImages() { return totalImages; } public int getCompliantImages() { return compliantImages; } public int getNonCompliantImages() { return nonCompliantImages; } public Date getReportDate() { return reportDate; } public double getCompliancePercentage() { return totalImages > 0 ? (compliantImages * 100.0) / totalImages : 0.0; } }

Report Generation

package com.example.quay.report; import com.example.quay.security.*; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; public class SecurityReportGenerator { private static final Logger logger = LoggerFactory.getLogger(SecurityReportGenerator.class); private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); private final ObjectMapper objectMapper; public SecurityReportGenerator() { this.objectMapper = new ObjectMapper(); this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); } public void generateHtmlReport(SecurityAssessment assessment, Path outputDir) throws IOException { String timestamp = DATE_FORMAT.format(new Date()); String filename = "quay_security_report_" + timestamp + ".html"; Path outputPath = outputDir.resolve(filename); String htmlContent = generateHtmlContent(assessment); try (FileWriter writer = new FileWriter(outputPath.toFile())) { writer.write(htmlContent); } logger.info("HTML security report generated: {}", outputPath); } public void generateHtmlReport(ComplianceReport complianceReport, Path outputDir) throws IOException { String timestamp = DATE_FORMAT.format(new Date()); String filename = "quay_compliance_report_" + timestamp + ".html"; Path outputPath = outputDir.resolve(filename); String htmlContent = generateComplianceHtmlContent(complianceReport); try (FileWriter writer = new FileWriter(outputPath.toFile())) { writer.write(htmlContent); } logger.info("HTML compliance report generated: {}", outputPath); } public void generateJsonReport(SecurityAssessment assessment, Path outputDir) throws IOException { String timestamp = DATE_FORMAT.format(new Date()); String filename = "quay_security_report_" + timestamp + ".json"; Path outputPath = outputDir.resolve(filename); objectMapper.writeValue(outputPath.toFile(), assessment); logger.info("JSON security report generated: {}", outputPath); } public void generateConsoleReport(SecurityAssessment assessment) { System.out.println("=== QUAY SECURITY ASSESSMENT ==="); System.out.printf("Image: %s%n", assessment.getImageReference()); System.out.printf("Security Score: %.1f/100 (%s)%n", assessment.getSecurityScore(), assessment.getSecurityLevel()); System.out.printf("Assessment Date: %s%n", assessment.getAssessmentDate()); System.out.println(); VulnerabilityReport vulnReport = assessment.getVulnerabilityReport(); System.out.println("VULNERABILITY SUMMARY:"); Map<String, Integer> summary = vulnReport.getVulnerabilitySummary(); for (Map.Entry<String, Integer> entry : summary.entrySet()) { System.out.printf(" %s: %d%n", entry.getKey(), entry.getValue()); } System.out.printf("Total Vulnerabilities: %d%n", vulnReport.getTotalVulnerabilities()); System.out.printf("Risk Score: %.1f%n", vulnReport.getRiskScore()); System.out.println(); // Critical vulnerabilities List<VulnerabilityDetail> criticalVulns = vulnReport.getVulnerabilities().stream() .filter(v -> "Critical".equalsIgnoreCase(v.getSeverity())) .toList(); if (!criticalVulns.isEmpty()) { System.out.println("CRITICAL VULNERABILITIES:"); for (VulnerabilityDetail vuln : criticalVulns) { System.out.printf(" %s - %s%n", vuln.getId(), vuln.getPackageName()); System.out.printf(" Fixed in: %s%n", vuln.getFixedBy() != null ? vuln.getFixedBy() : "Not available"); } System.out.println(); } // Signing status ImageSigningStatus signingStatus = assessment.getSigningStatus(); System.out.printf("Image Signed: %s%n", signingStatus != null && signingStatus.isSigned() ? "YES" : "NO"); if (signingStatus != null && signingStatus.isSigned()) { System.out.printf("Signatures: %d%n", signingStatus.getSignatures().size()); } } private String generateHtmlContent(SecurityAssessment assessment) { VulnerabilityReport vulnReport = assessment.getVulnerabilityReport(); ImageSigningStatus signingStatus = assessment.getSigningStatus(); return """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Quay Security Report</title> <style> body { font-family: Arial, sans-serif; margin: 20px; } .header { background: #2c3e50; color: white; padding: 20px; border-radius: 5px; } .summary { margin: 20px 0; } .section { margin: 20px 0; border: 1px solid #ddd; padding: 15px; border-radius: 5px; } .critical { color: #e74c3c; font-weight: bold; } .high { color: #e67e22; } .medium { color: #f39c12; } .low { color: #27ae60; } .security-score { font-size: 2em; font-weight: bold; margin: 10px 0; } .score-excellent { color: #27ae60; } .score-good { color: #f39c12; } .score-poor { color: #e74c3c; } table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #f2f2f2; } </style> </head> <body> <div class="header"> <h1>Quay Security Assessment Report</h1> <p>Image: """ + assessment.getImageReference() + """</p> <p>Assessment Date: """ + assessment.getAssessmentDate() + """</p> </div> <div class="summary"> <div class="security-score """ + getScoreClass(assessment.getSecurityScore()) + """"> Security Score: """ + String.format("%.1f", assessment.getSecurityScore()) + """/100 </div> <p>Security Level: """ + assessment.getSecurityLevel() + """</p> </div> <div class="section"> <h2>Vulnerability Summary</h2> <table> <tr><th>Severity</th><th>Count</th></tr> """ + generateVulnerabilitySummaryRows(vulnReport) + """ </table> <p>Total Vulnerabilities: """ + vulnReport.getTotalVulnerabilities() + """</p> <p>Risk Score: """ + String.format("%.1f", vulnReport.getRiskScore()) + """</p> </div> <div class="section"> <h2>Image Signing</h2> <p>Signed: """ + (signingStatus != null && signingStatus.isSigned() ? "YES" : "NO") + """</p> """ + (signingStatus != null && signingStatus.isSigned() ? "<p>Signatures: " + signingStatus.getSignatures().size() + "</p>" : "") + """ </div> <div class="section"> <h2>Critical Vulnerabilities</h2> """ + generateCriticalVulnerabilitiesList(vulnReport) + """ </div> </body> </html> """; } private String generateComplianceHtmlContent(ComplianceReport complianceReport) { return """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Quay Compliance Report</title> <style> body { font-family: Arial, sans-serif; margin: 20px; } .header { background: #2c3e50; color: white; padding: 20px; border-radius: 5px; } .summary { margin: 20px 0; } .section { margin: 20px 0; border: 1px solid #ddd; padding: 15px; border-radius: 5px; } .compliant { color: #27ae60; } .non-compliant { color: #e74c3c; } table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #f2f2f2; } </style> </head> <body> <div class="header"> <h1>Quay Compliance Report</h1> <p>Repository: """ + complianceReport.getNamespace() + "/" + complianceReport.getRepository() + """</p> <p>Report Date: """ + complianceReport.getReportDate() + """</p> </div> <div class="summary"> <h2>Compliance Summary</h2> <p>Total Images: """ + complianceReport.getTotalImages() + """</p> <p>Compliant Images: """ + complianceReport.getCompliantImages() + """</p> <p>Non-compliant Images: """ + complianceReport.getNonCompliantImages() + """</p> <p>Compliance Rate: """ + String.format("%.1f", complianceReport.getCompliancePercentage()) + """%</p> </div> <div class="section"> <h2>Image Compliance Status</h2> <table> <tr><th>Image Tag</th><th>Compliant</th><th>Violations</th></tr> """ + generateComplianceRows(complianceReport) + """ </table> </div> </body> </html> """; } private String generateVulnerabilitySummaryRows(VulnerabilityReport vulnReport) { StringBuilder rows = new StringBuilder(); Map<String, Integer> summary = vulnReport.getVulnerabilitySummary(); for (Map.Entry<String, Integer> entry : summary.entrySet()) { String severityClass = getSeverityClass(entry.getKey()); rows.append(String.format("<tr><td class='%s'>%s</td><td>%d</td></tr>", severityClass, entry.getKey(), entry.getValue())); } return rows.toString(); } private String generateCriticalVulnerabilitiesList(VulnerabilityReport vulnReport) { List<VulnerabilityDetail> criticalVulns = vulnReport.getVulnerabilities().stream() .filter(v -> "Critical".equalsIgnoreCase(v.getSeverity())) .toList(); if (criticalVulns.isEmpty()) { return "<p>No critical vulnerabilities found.</p>"; } StringBuilder list = new StringBuilder("<ul>"); for (VulnerabilityDetail vuln : criticalVulns) { list.append(String.format("<li><strong>%s</strong> - %s", vuln.getId(), vuln.getPackageName())); list.append(String.format("<br>Fixed in: %s", vuln.getFixedBy() != null ? vuln.getFixedBy() : "Not available")); list.append("</li>"); } list.append("</ul>"); return list.toString(); } private String generateComplianceRows(ComplianceReport complianceReport) { StringBuilder rows = new StringBuilder(); for (Map.Entry<String, SecurityCompliance> entry : complianceReport.getImageCompliance().entrySet()) { String tag = entry.getKey(); SecurityCompliance compliance = entry.getValue(); String statusClass = compliance.isCompliant() ? "compliant" : "non-compliant"; String statusText = compliance.isCompliant() ? "YES" : "NO"; String violations = String.join("; ", compliance.getViolations()); rows.append(String.format("<tr><td>%s</td><td class='%s'>%s</td><td>%s</td></tr>", tag, statusClass, statusText, violations)); } return rows.toString(); } private String getScoreClass(double score) { if (score >= 80.0) return "score-excellent"; else if (score >= 60.0) return "score-good"; else return "score-poor"; } private String getSeverityClass(String severity) { if (severity == null) return "low"; return switch (severity.toLowerCase()) { case "critical" -> "critical"; case "high" -> "high"; case "medium" -> "medium"; default -> "low"; }; } }

Usage Example

package com.example.quay; import com.example.quay.security.*; import com.example.quay.report.SecurityReportGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; public class QuaySecurityExample { private static final Logger logger = LoggerFactory.getLogger(QuaySecurityExample.class); public static void main(String[] args) { // Configuration String quayUrl = "https://quay.io"; String authToken = "your-auth-token"; String namespace = "your-namespace"; String repository = "your-repository"; String tag = "latest"; Path reportOutputDir = Paths.get("security-reports"); QuayClient quayClient = null; try { // Initialize Quay client quayClient = new QuayClient(quayUrl, authToken, 60); if (!quayClient.testConnection()) { logger.error("Failed to connect to Quay"); System.exit(1); } // Initialize services VulnerabilityAnalyzer vulnerabilityAnalyzer = new VulnerabilityAnalyzer(); ImageSecurityManager securityManager = new ImageSecurityManager(quayClient, vulnerabilityAnalyzer); SecurityReportGenerator reportGenerator = new SecurityReportGenerator(); // Assess image security SecurityAssessment assessment = securityManager.assessImage(namespace, repository, tag); // Generate reports reportGenerator.generateConsoleReport(assessment); reportGenerator.generateHtmlReport(assessment, reportOutputDir); reportGenerator.generateJsonReport(assessment, reportOutputDir); // Check compliance SecurityPolicy policy = new SecurityPolicy(); policy.setAllowCritical(false); policy.setMaxHighVulnerabilities(0); policy.setMaxRiskScore(20.0); SecurityCompliance compliance = securityManager.checkCompliance( namespace, repository, tag, policy); if (!compliance.isCompliant()) { logger.warn("Image is not compliant with security policy:"); for (String violation : compliance.getViolations()) { logger.warn(" - {}", violation); } System.exit(1); } logger.info("Security assessment completed successfully"); } catch (Exception e) { logger.error("Quay security analysis failed", e); System.exit(1); } finally { if (quayClient != null) { quayClient.close(); } } } }

GitHub Actions Integration

name: Quay Security Scan on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: - cron: '0 0 * * 0' # Weekly on Sunday jobs: quay-security-scan: name: "Quay Security Scan" runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Run Quay Security Scan run: | mvn -B exec:java -Dexec.mainClass="com.example.quay.QuaySecurityExample" \ -Dexec.args="--namespace my-namespace --repository my-app --tag latest" env: QUAY_URL: ${{ secrets.QUAY_URL }} QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} - name: Upload Security Report uses: actions/upload-artifact@v3 with: name: quay-security-report path: security-reports/ - name: Check for Critical Vulnerabilities run: | # Check if there are critical vulnerabilities if grep -q "CRITICAL" security-reports/*.json; then echo "Critical vulnerabilities found - failing build" exit 1 fi

Conclusion

This comprehensive Quay Security implementation for Java provides:

  • Complete Quay API integration for container registry operations
  • Automated vulnerability scanning and analysis
  • Security policy enforcement with compliance checking
  • Image signing verification for integrity assurance
  • Comprehensive reporting in HTML, JSON, and console formats
  • CI/CD integration with GitHub Actions

Key benefits include:

  1. Container Security Management - Comprehensive security for container images
  2. Automated Vulnerability Detection - Continuous monitoring of CVEs
  3. Policy Enforcement - Automated compliance checking
  4. Image Integrity - Verification through digital signatures
  5. Actionable Reporting - Clear insights for development and security teams

The implementation follows industry best practices and provides a solid foundation for integrating Quay Security into Java development workflows for comprehensive container security management.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper