OSS Index Vulnerability Check in Java

Introduction to OSS Index

OSS Index is a free service by Sonatype that provides vulnerability information for open-source software components. It helps identify security vulnerabilities in your project dependencies, similar to services like Snyk and GitHub Security Advisories.

Table of Contents

  1. OSS Index Integration Approaches
  2. Maven Plugin Implementation
  3. Gradle Plugin Implementation
  4. REST API Client
  5. Dependency Analysis
  6. CI/CD Integration
  7. Enterprise Vulnerability Scanner

OSS Index Integration Approaches

1. REST API Integration

2. Maven Plugin

3. Gradle Plugin

4. Standalone Scanner

Maven Dependencies

<dependencies>
<!-- REST Client -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- Maven Integration -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.9.5</version>
</dependency>
<!-- Gradle Tooling API -->
<dependency>
<groupId>org.gradle</groupId>
<artifactId>gradle-tooling-api</artifactId>
<version>8.4</version>
</dependency>
<!-- Cache Implementation -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>

REST API Client Implementation

Core OSS Index Client

package com.security.ossindex;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class OSSIndexClient {
private static final String OSS_INDEX_API_URL = "https://ossindex.sonatype.org/api/v3/component-report";
private static final int BATCH_SIZE = 128; // OSS Index batch limit
private final CloseableHttpClient httpClient;
private final ObjectMapper objectMapper;
private final String userAgent;
private final RateLimiter rateLimiter;
public OSSIndexClient() {
this(HttpClients.createDefault(), "Java-OSS-Index-Client/1.0");
}
public OSSIndexClient(CloseableHttpClient httpClient, String userAgent) {
this.httpClient = httpClient;
this.objectMapper = new ObjectMapper();
this.userAgent = userAgent;
this.rateLimiter = new RateLimiter(100, TimeUnit.MINUTES); // 100 requests per minute
}
public List<ComponentReport> getVulnerabilityReports(List<Component> components) 
throws OSSIndexException {
List<ComponentReport> allReports = new ArrayList<>();
// Process components in batches
for (int i = 0; i < components.size(); i += BATCH_SIZE) {
List<Component> batch = components.subList(i, 
Math.min(i + BATCH_SIZE, components.size()));
List<ComponentReport> batchReports = getBatchVulnerabilityReports(batch);
allReports.addAll(batchReports);
// Respect rate limits
rateLimiter.acquire();
}
return allReports;
}
private List<ComponentReport> getBatchVulnerabilityReports(List<Component> components) 
throws OSSIndexException {
try {
HttpPost request = new HttpPost(OSS_INDEX_API_URL);
request.setHeader("User-Agent", userAgent);
request.setHeader("Content-Type", "application/json");
request.setHeader("Accept", "application/json");
// Build request payload
List<Map<String, String>> coordinates = new ArrayList<>();
for (Component component : components) {
coordinates.add(Map.of(
"coordinates", component.getCoordinates()
));
}
String requestBody = objectMapper.writeValueAsString(coordinates);
request.setEntity(new StringEntity(requestBody));
try (CloseableHttpResponse response = httpClient.execute(request)) {
int statusCode = response.getCode();
String responseBody = EntityUtils.toString(response.getEntity());
if (statusCode == 200) {
ComponentReport[] reports = objectMapper.readValue(
responseBody, ComponentReport[].class);
return Arrays.asList(reports);
} else if (statusCode == 429) {
throw new RateLimitExceededException("Rate limit exceeded");
} else {
throw new OSSIndexException(
"OSS Index API returned status: " + statusCode + " - " + responseBody);
}
}
} catch (RateLimitExceededException e) {
throw e;
} catch (Exception e) {
throw new OSSIndexException("Failed to get vulnerability reports", e);
}
}
public ComponentReport getVulnerabilityReport(Component component) 
throws OSSIndexException {
List<ComponentReport> reports = getVulnerabilityReports(List.of(component));
return reports.isEmpty() ? null : reports.get(0);
}
public void close() throws Exception {
httpClient.close();
}
// Data classes
public static class Component {
private final String groupId;
private final String artifactId;
private final String version;
private final String packaging;
public Component(String groupId, String artifactId, String version) {
this(groupId, artifactId, version, "jar");
}
public Component(String groupId, String artifactId, String version, String packaging) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.packaging = packaging;
}
public String getCoordinates() {
return String.format("pkg:maven/%s/%s@%s?type=%s", 
groupId, artifactId, version, packaging);
}
// Getters
public String getGroupId() { return groupId; }
public String getArtifactId() { return artifactId; }
public String getVersion() { return version; }
public String getPackaging() { return packaging; }
@Override
public String toString() {
return groupId + ":" + artifactId + ":" + version;
}
}
public static class ComponentReport {
private String coordinates;
private String description;
private String reference;
private List<Vulnerability> vulnerabilities;
private double cvssScore;
// Getters and setters
public String getCoordinates() { return coordinates; }
public void setCoordinates(String coordinates) { this.coordinates = coordinates; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getReference() { return reference; }
public void setReference(String reference) { this.reference = reference; }
public List<Vulnerability> getVulnerabilities() { return vulnerabilities; }
public void setVulnerabilities(List<Vulnerability> vulnerabilities) { this.vulnerabilities = vulnerabilities; }
public double getCvssScore() { return cvssScore; }
public void setCvssScore(double cvssScore) { this.cvssScore = cvssScore; }
public boolean hasVulnerabilities() {
return vulnerabilities != null && !vulnerabilities.isEmpty();
}
public List<Vulnerability> getCriticalVulnerabilities() {
return vulnerabilities != null ? 
vulnerabilities.stream()
.filter(v -> v.getCvssScore() >= 9.0)
.toList() : 
Collections.emptyList();
}
public List<Vulnerability> getHighVulnerabilities() {
return vulnerabilities != null ? 
vulnerabilities.stream()
.filter(v -> v.getCvssScore() >= 7.0 && v.getCvssScore() < 9.0)
.toList() : 
Collections.emptyList();
}
}
public static class Vulnerability {
private String id;
private String title;
private String description;
private double cvssScore;
private String cvssVector;
private String cwe;
private String cve;
private String reference;
private List<String> externalReferences;
private Date published;
private Date updated;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public double getCvssScore() { return cvssScore; }
public void setCvssScore(double cvssScore) { this.cvssScore = cvssScore; }
public String getCvssVector() { return cvssVector; }
public void setCvssVector(String cvssVector) { this.cvssVector = cvssVector; }
public String getCwe() { return cwe; }
public void setCwe(String cwe) { this.cwe = cwe; }
public String getCve() { return cve; }
public void setCve(String cve) { this.cve = cve; }
public String getReference() { return reference; }
public void setReference(String reference) { this.reference = reference; }
public List<String> getExternalReferences() { return externalReferences; }
public void setExternalReferences(List<String> externalReferences) { this.externalReferences = externalReferences; }
public Date getPublished() { return published; }
public void setPublished(Date published) { this.published = published; }
public Date getUpdated() { return updated; }
public void setUpdated(Date updated) { this.updated = updated; }
public String getSeverity() {
if (cvssScore >= 9.0) return "CRITICAL";
if (cvssScore >= 7.0) return "HIGH";
if (cvssScore >= 4.0) return "MEDIUM";
return "LOW";
}
@Override
public String toString() {
return String.format("%s (CVSS: %.1f, %s)", id, cvssScore, getSeverity());
}
}
public static class OSSIndexException extends Exception {
public OSSIndexException(String message) {
super(message);
}
public OSSIndexException(String message, Throwable cause) {
super(message, cause);
}
}
public static class RateLimitExceededException extends OSSIndexException {
public RateLimitExceededException(String message) {
super(message);
}
}
// Simple rate limiter
private static class RateLimiter {
private final int permits;
private final long periodMillis;
private long lastRefillTime;
private int availablePermits;
public RateLimiter(int permits, TimeUnit timeUnit) {
this.permits = permits;
this.periodMillis = timeUnit.toMillis(1);
this.availablePermits = permits;
this.lastRefillTime = System.currentTimeMillis();
}
public synchronized void acquire() throws InterruptedException {
refill();
while (availablePermits <= 0) {
long waitTime = periodMillis - (System.currentTimeMillis() - lastRefillTime);
if (waitTime > 0) {
Thread.sleep(waitTime);
}
refill();
}
availablePermits--;
}
private void refill() {
long currentTime = System.currentTimeMillis();
long timeSinceLastRefill = currentTime - lastRefillTime;
if (timeSinceLastRefill >= periodMillis) {
availablePermits = permits;
lastRefillTime = currentTime;
}
}
}
}

Maven Plugin Implementation

Custom Maven Plugin for Vulnerability Scanning

package com.security.ossindex.maven;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.artifact.Artifact;
import java.util.*;
import java.util.stream.Collectors;
@Mojo(
name = "vulnerability-scan", 
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
threadSafe = true
)
public class VulnerabilityScanMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
@Parameter(property = "failOnCritical", defaultValue = "true")
private boolean failOnCritical;
@Parameter(property = "failOnHigh", defaultValue = "false")
private boolean failOnHigh;
@Parameter(property = "includeScopes", defaultValue = "compile,runtime")
private String includeScopes;
@Parameter(property = "excludeScopes", defaultValue = "test,system,provided")
private String excludeScopes;
@Parameter(property = "outputFormat", defaultValue = "console")
private String outputFormat;
@Parameter(property = "reportFile", defaultValue = "target/vulnerability-report.json")
private String reportFile;
@Parameter(property = "cacheResults", defaultValue = "true")
private boolean cacheResults;
private OSSIndexClient client;
private VulnerabilityCache cache;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("Starting OSS Index vulnerability scan...");
try {
initializeClient();
List<OSSIndexClient.Component> components = collectDependencies();
getLog().info("Found " + components.size() + " dependencies to scan");
ScanResult result = scanDependencies(components);
generateReport(result);
if (shouldFailBuild(result)) {
throw new MojoFailureException(
"Vulnerabilities found that exceed threshold: " + 
getFailureSummary(result));
}
} catch (OSSIndexClient.OSSIndexException e) {
throw new MojoExecutionException("Failed to scan vulnerabilities", e);
} finally {
try {
if (client != null) {
client.close();
}
} catch (Exception e) {
getLog().warn("Failed to close OSS Index client", e);
}
}
}
private void initializeClient() {
this.client = new OSSIndexClient();
this.cache = new VulnerabilityCache();
}
private List<OSSIndexClient.Component> collectDependencies() {
Set<String> includedScopes = parseScopes(includeScopes);
Set<String> excludedScopes = parseScopes(excludeScopes);
return project.getArtifacts().stream()
.filter(artifact -> includedScopes.contains(artifact.getScope()))
.filter(artifact -> !excludedScopes.contains(artifact.getScope()))
.map(this::convertToComponent)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private OSSIndexClient.Component convertToComponent(Artifact artifact) {
try {
return new OSSIndexClient.Component(
artifact.getGroupId(),
artifact.getArtifactId(),
artifact.getVersion(),
artifact.getType()
);
} catch (Exception e) {
getLog().warn("Failed to convert artifact: " + artifact, e);
return null;
}
}
private Set<String> parseScopes(String scopes) {
return Arrays.stream(scopes.split(","))
.map(String::trim)
.collect(Collectors.toSet());
}
private ScanResult scanDependencies(List<OSSIndexClient.Component> components) 
throws OSSIndexClient.OSSIndexException {
ScanResult result = new ScanResult();
for (OSSIndexClient.Component component : components) {
OSSIndexClient.ComponentReport report = null;
// Check cache first
if (cacheResults) {
report = cache.get(component.getCoordinates());
}
// Fetch from OSS Index if not cached
if (report == null) {
try {
report = client.getVulnerabilityReport(component);
if (cacheResults && report != null) {
cache.put(component.getCoordinates(), report);
}
} catch (OSSIndexClient.RateLimitExceededException e) {
getLog().warn("Rate limit exceeded, waiting before continuing...");
try {
Thread.sleep(60000); // Wait 1 minute
report = client.getVulnerabilityReport(component);
} catch (Exception ex) {
getLog().error("Failed to get report after rate limit wait", ex);
}
}
}
if (report != null && report.hasVulnerabilities()) {
result.addVulnerableComponent(component, report);
logVulnerabilities(component, report);
}
}
return result;
}
private void logVulnerabilities(OSSIndexClient.Component component, 
OSSIndexClient.ComponentReport report) {
getLog().warn("Vulnerabilities found in " + component + ":");
for (OSSIndexClient.Vulnerability vuln : report.getVulnerabilities()) {
getLog().warn(String.format("  %s - %s (CVSS: %.1f, %s)", 
vuln.getId(), vuln.getTitle(), vuln.getCvssScore(), vuln.getSeverity()));
if (getLog().isDebugEnabled()) {
getLog().debug("    Description: " + 
(vuln.getDescription() != null ? 
vuln.getDescription().substring(0, Math.min(100, vuln.getDescription().length())) : "N/A"));
getLog().debug("    Reference: " + vuln.getReference());
}
}
}
private void generateReport(ScanResult result) {
// Generate report based on output format
switch (outputFormat.toLowerCase()) {
case "json":
generateJsonReport(result);
break;
case "html":
generateHtmlReport(result);
break;
case "console":
default:
generateConsoleReport(result);
}
}
private void generateConsoleReport(ScanResult result) {
getLog().info("=== Vulnerability Scan Report ===");
getLog().info("Scanned components: " + result.getTotalComponents());
getLog().info("Vulnerable components: " + result.getVulnerableComponents().size());
getLog().info("Total vulnerabilities: " + result.getTotalVulnerabilities());
getLog().info("Critical vulnerabilities: " + result.getCriticalCount());
getLog().info("High vulnerabilities: " + result.getHighCount());
getLog().info("Medium vulnerabilities: " + result.getMediumCount());
getLog().info("Low vulnerabilities: " + result.getLowCount());
if (!result.getVulnerableComponents().isEmpty()) {
getLog().info("");
getLog().info("Vulnerable Components:");
result.getVulnerableComponents().forEach((component, report) -> {
getLog().info("  " + component + ": " + 
report.getVulnerabilities().size() + " vulnerabilities");
});
}
}
private void generateJsonReport(ScanResult result) {
try {
// Implementation for JSON report generation
// Would use Jackson to write result to reportFile
getLog().info("JSON report generated at: " + reportFile);
} catch (Exception e) {
getLog().error("Failed to generate JSON report", e);
}
}
private void generateHtmlReport(ScanResult result) {
try {
// Implementation for HTML report generation
getLog().info("HTML report generated at: " + reportFile);
} catch (Exception e) {
getLog().error("Failed to generate HTML report", e);
}
}
private boolean shouldFailBuild(ScanResult result) {
if (failOnCritical && result.getCriticalCount() > 0) {
return true;
}
if (failOnHigh && result.getHighCount() > 0) {
return true;
}
return false;
}
private String getFailureSummary(ScanResult result) {
StringBuilder summary = new StringBuilder();
if (result.getCriticalCount() > 0) {
summary.append(result.getCriticalCount()).append(" critical");
}
if (result.getHighCount() > 0) {
if (summary.length() > 0) summary.append(", ");
summary.append(result.getHighCount()).append(" high");
}
summary.append(" vulnerabilities found");
return summary.toString();
}
// Scan result container
private static class ScanResult {
private final Map<OSSIndexClient.Component, OSSIndexClient.ComponentReport> vulnerableComponents;
private int totalComponents;
public ScanResult() {
this.vulnerableComponents = new HashMap<>();
}
public void addVulnerableComponent(OSSIndexClient.Component component, 
OSSIndexClient.ComponentReport report) {
vulnerableComponents.put(component, report);
}
public void setTotalComponents(int totalComponents) {
this.totalComponents = totalComponents;
}
// Getters
public Map<OSSIndexClient.Component, OSSIndexClient.ComponentReport> getVulnerableComponents() {
return vulnerableComponents;
}
public int getTotalComponents() {
return totalComponents;
}
public int getTotalVulnerabilities() {
return vulnerableComponents.values().stream()
.mapToInt(report -> report.getVulnerabilities().size())
.sum();
}
public int getCriticalCount() {
return vulnerableComponents.values().stream()
.mapToInt(report -> report.getCriticalVulnerabilities().size())
.sum();
}
public int getHighCount() {
return vulnerableComponents.values().stream()
.mapToInt(report -> report.getHighVulnerabilities().size())
.sum();
}
public int getMediumCount() {
return vulnerableComponents.values().stream()
.mapToInt(report -> (int) report.getVulnerabilities().stream()
.filter(v -> v.getCvssScore() >= 4.0 && v.getCvssScore() < 7.0)
.count())
.sum();
}
public int getLowCount() {
return vulnerableComponents.values().stream()
.mapToInt(report -> (int) report.getVulnerabilities().stream()
.filter(v -> v.getCvssScore() < 4.0)
.count())
.sum();
}
}
}

Gradle Plugin Implementation

Custom Gradle Plugin for Vulnerability Scanning

package com.security.ossindex.gradle;
import org.gradle.api.DefaultTask;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedConfiguration;
import org.gradle.api.artifacts.ResolvedDependency;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Optional;
import java.util.*;
import java.util.stream.Collectors;
public class VulnerabilityScanTask extends DefaultTask {
private boolean failOnCritical = true;
private boolean failOnHigh = false;
private List<String> includeConfigurations = Arrays.asList("compileClasspath", "runtimeClasspath");
private List<String> excludeConfigurations = Arrays.asList("testCompileClasspath", "testRuntimeClasspath");
private String outputFormat = "console";
private String reportFile = "build/reports/vulnerability-scan/report.json";
private boolean cacheResults = true;
private OSSIndexClient client;
private VulnerabilityCache cache;
@TaskAction
public void scanVulnerabilities() {
getLogger().info("Starting OSS Index vulnerability scan...");
try {
initializeClient();
List<OSSIndexClient.Component> components = collectDependencies();
getLogger().info("Found {} dependencies to scan", components.size());
ScanResult result = scanDependencies(components);
generateReport(result);
if (shouldFailBuild(result)) {
throw new RuntimeException(
"Vulnerabilities found that exceed threshold: " + 
getFailureSummary(result));
}
} catch (OSSIndexClient.OSSIndexException e) {
throw new RuntimeException("Failed to scan vulnerabilities", e);
} finally {
try {
if (client != null) {
client.close();
}
} catch (Exception e) {
getLogger().warn("Failed to close OSS Index client", e);
}
}
}
private void initializeClient() {
this.client = new OSSIndexClient();
this.cache = new VulnerabilityCache();
}
private List<OSSIndexClient.Component> collectDependencies() {
Set<Configuration> configurations = getProject().getConfigurations().stream()
.filter(config -> includeConfigurations.contains(config.getName()))
.filter(config -> !excludeConfigurations.contains(config.getName()))
.collect(Collectors.toSet());
List<OSSIndexClient.Component> components = new ArrayList<>();
for (Configuration config : configurations) {
try {
ResolvedConfiguration resolvedConfig = config.getResolvedConfiguration();
Set<ResolvedArtifact> artifacts = resolvedConfig.getResolvedArtifacts();
for (ResolvedArtifact artifact : artifacts) {
OSSIndexClient.Component component = convertToComponent(artifact);
if (component != null) {
components.add(component);
}
}
} catch (Exception e) {
getLogger().warn("Failed to resolve configuration: " + config.getName(), e);
}
}
return components;
}
private OSSIndexClient.Component convertToComponent(ResolvedArtifact artifact) {
try {
return new OSSIndexClient.Component(
artifact.getModuleVersion().getId().getGroup(),
artifact.getModuleVersion().getId().getName(),
artifact.getModuleVersion().getId().getVersion(),
artifact.getExtension()
);
} catch (Exception e) {
getLogger().warn("Failed to convert artifact: " + artifact, e);
return null;
}
}
private ScanResult scanDependencies(List<OSSIndexClient.Component> components) 
throws OSSIndexClient.OSSIndexException {
ScanResult result = new ScanResult();
result.setTotalComponents(components.size());
for (OSSIndexClient.Component component : components) {
OSSIndexClient.ComponentReport report = null;
// Check cache first
if (cacheResults) {
report = cache.get(component.getCoordinates());
}
// Fetch from OSS Index if not cached
if (report == null) {
try {
report = client.getVulnerabilityReport(component);
if (cacheResults && report != null) {
cache.put(component.getCoordinates(), report);
}
} catch (OSSIndexClient.RateLimitExceededException e) {
getLogger().warn("Rate limit exceeded, waiting before continuing...");
try {
Thread.sleep(60000); // Wait 1 minute
report = client.getVulnerabilityReport(component);
} catch (Exception ex) {
getLogger().error("Failed to get report after rate limit wait", ex);
}
}
}
if (report != null && report.hasVulnerabilities()) {
result.addVulnerableComponent(component, report);
logVulnerabilities(component, report);
}
}
return result;
}
private void logVulnerabilities(OSSIndexClient.Component component, 
OSSIndexClient.ComponentReport report) {
getLogger().warn("Vulnerabilities found in {}:", component);
for (OSSIndexClient.Vulnerability vuln : report.getVulnerabilities()) {
getLogger().warn("  {} - {} (CVSS: {:.1f}, {})", 
vuln.getId(), vuln.getTitle(), vuln.getCvssScore(), vuln.getSeverity());
}
}
private void generateReport(ScanResult result) {
// Similar to Maven implementation
switch (outputFormat.toLowerCase()) {
case "json":
generateJsonReport(result);
break;
case "html":
generateHtmlReport(result);
break;
case "console":
default:
generateConsoleReport(result);
}
}
private void generateConsoleReport(ScanResult result) {
getLogger().info("=== Vulnerability Scan Report ===");
getLogger().info("Scanned components: {}", result.getTotalComponents());
getLogger().info("Vulnerable components: {}", result.getVulnerableComponents().size());
getLogger().info("Total vulnerabilities: {}", result.getTotalVulnerabilities());
getLogger().info("Critical vulnerabilities: {}", result.getCriticalCount());
getLogger().info("High vulnerabilities: {}", result.getHighCount());
getLogger().info("Medium vulnerabilities: {}", result.getMediumCount());
getLogger().info("Low vulnerabilities: {}", result.getLowCount());
}
private void generateJsonReport(ScanResult result) {
// JSON report implementation
}
private void generateHtmlReport(ScanResult result) {
// HTML report implementation
}
private boolean shouldFailBuild(ScanResult result) {
if (failOnCritical && result.getCriticalCount() > 0) {
return true;
}
if (failOnHigh && result.getHighCount() > 0) {
return true;
}
return false;
}
private String getFailureSummary(ScanResult result) {
StringBuilder summary = new StringBuilder();
if (result.getCriticalCount() > 0) {
summary.append(result.getCriticalCount()).append(" critical");
}
if (result.getHighCount() > 0) {
if (summary.length() > 0) summary.append(", ");
summary.append(result.getHighCount()).append(" high");
}
summary.append(" vulnerabilities found");
return summary.toString();
}
// Getters and setters for Gradle properties
@Input
public boolean getFailOnCritical() { return failOnCritical; }
public void setFailOnCritical(boolean failOnCritical) { this.failOnCritical = failOnCritical; }
@Input
public boolean getFailOnHigh() { return failOnHigh; }
public void setFailOnHigh(boolean failOnHigh) { this.failOnHigh = failOnHigh; }
@Input
public List<String> getIncludeConfigurations() { return includeConfigurations; }
public void setIncludeConfigurations(List<String> includeConfigurations) { this.includeConfigurations = includeConfigurations; }
@Input
public List<String> getExcludeConfigurations() { return excludeConfigurations; }
public void setExcludeConfigurations(List<String> excludeConfigurations) { this.excludeConfigurations = excludeConfigurations; }
@Input
public String getOutputFormat() { return outputFormat; }
public void setOutputFormat(String outputFormat) { this.outputFormat = outputFormat; }
@Input
@Optional
public String getReportFile() { return reportFile; }
public void setReportFile(String reportFile) { this.reportFile = reportFile; }
@Input
public boolean getCacheResults() { return cacheResults; }
public void setCacheResults(boolean cacheResults) { this.cacheResults = cacheResults; }
}

Vulnerability Cache Implementation

Efficient Caching for OSS Index Results

package com.security.ossindex.cache;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.security.ossindex.OSSIndexClient;
import java.util.concurrent.TimeUnit;
public class VulnerabilityCache {
private final Cache<String, OSSIndexClient.ComponentReport> cache;
public VulnerabilityCache() {
this.cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(24, TimeUnit.HOURS) // Cache for 24 hours
.build();
}
public VulnerabilityCache(int maxSize, int expireHours) {
this.cache = Caffeine.newBuilder()
.maximumSize(maxSize)
.expireAfterWrite(expireHours, TimeUnit.HOURS)
.build();
}
public OSSIndexClient.ComponentReport get(String coordinates) {
return cache.getIfPresent(coordinates);
}
public void put(String coordinates, OSSIndexClient.ComponentReport report) {
cache.put(coordinates, report);
}
public void invalidate(String coordinates) {
cache.invalidate(coordinates);
}
public void clear() {
cache.invalidateAll();
}
public long size() {
return cache.estimatedSize();
}
}

Enterprise Vulnerability Scanner

Comprehensive Vulnerability Management

package com.security.ossindex.enterprise;
import com.security.ossindex.OSSIndexClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class EnterpriseVulnerabilityScanner {
private final OSSIndexClient ossIndexClient;
private final VulnerabilityCache cache;
private final ObjectMapper objectMapper;
private final ExecutorService executorService;
private final String reportsDirectory;
public EnterpriseVulnerabilityScanner() {
this.ossIndexClient = new OSSIndexClient();
this.cache = new VulnerabilityCache();
this.objectMapper = new ObjectMapper();
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
this.executorService = Executors.newFixedThreadPool(10);
this.reportsDirectory = "./vulnerability-reports";
createReportsDirectory();
}
public ProjectScanResult scanProject(Project project) throws Exception {
ProjectScanResult result = new ProjectScanResult(project);
result.setScanStartTime(LocalDateTime.now());
try {
List<OSSIndexClient.Component> components = project.collectDependencies();
result.setTotalDependencies(components.size());
List<OSSIndexClient.ComponentReport> reports = 
ossIndexClient.getVulnerabilityReports(components);
for (OSSIndexClient.ComponentReport report : reports) {
if (report.hasVulnerabilities()) {
result.addVulnerableComponent(report);
// Cache the result
cache.put(report.getCoordinates(), report);
}
}
result.setScanEndTime(LocalDateTime.now());
generateProjectReport(result);
} catch (Exception e) {
result.setError(e.getMessage());
result.setScanEndTime(LocalDateTime.now());
throw e;
}
return result;
}
public OrganizationScanResult scanOrganization(List<Project> projects) {
OrganizationScanResult orgResult = new OrganizationScanResult();
orgResult.setScanStartTime(LocalDateTime.now());
List<Future<ProjectScanResult>> futures = new ArrayList<>();
for (Project project : projects) {
Future<ProjectScanResult> future = executorService.submit(() -> {
try {
return scanProject(project);
} catch (Exception e) {
ProjectScanResult errorResult = new ProjectScanResult(project);
errorResult.setError(e.getMessage());
return errorResult;
}
});
futures.add(future);
}
for (Future<ProjectScanResult> future : futures) {
try {
ProjectScanResult projectResult = future.get(30, TimeUnit.MINUTES);
orgResult.addProjectResult(projectResult);
} catch (TimeoutException e) {
getLogger().error("Project scan timed out", e);
} catch (Exception e) {
getLogger().error("Project scan failed", e);
}
}
orgResult.setScanEndTime(LocalDateTime.now());
generateOrganizationReport(orgResult);
return orgResult;
}
public List<VulnerabilityTrend> analyzeTrends(OrganizationScanResult currentScan, 
OrganizationScanResult previousScan) {
List<VulnerabilityTrend> trends = new ArrayList<>();
// Analyze new vulnerabilities
Set<String> currentVulnerabilities = currentScan.getAllVulnerabilities().stream()
.map(OSSIndexClient.Vulnerability::getId)
.collect(Collectors.toSet());
Set<String> previousVulnerabilities = previousScan.getAllVulnerabilities().stream()
.map(OSSIndexClient.Vulnerability::getId)
.collect(Collectors.toSet());
// Find new vulnerabilities
currentVulnerabilities.stream()
.filter(vulnId -> !previousVulnerabilities.contains(vulnId))
.forEach(vulnId -> {
VulnerabilityTrend trend = new VulnerabilityTrend();
trend.setVulnerabilityId(vulnId);
trend.setTrendType(TrendType.NEW);
trend.setSeverityChange("NEW");
trends.add(trend);
});
// Find fixed vulnerabilities
previousVulnerabilities.stream()
.filter(vulnId -> !currentVulnerabilities.contains(vulnId))
.forEach(vulnId -> {
VulnerabilityTrend trend = new VulnerabilityTrend();
trend.setVulnerabilityId(vulnId);
trend.setTrendType(TrendType.FIXED);
trend.setSeverityChange("FIXED");
trends.add(trend);
});
return trends;
}
public RemediationPlan generateRemediationPlan(ProjectScanResult scanResult) {
RemediationPlan plan = new RemediationPlan();
plan.setGeneratedDate(LocalDateTime.now());
// Group vulnerabilities by severity and component
Map<String, List<OSSIndexClient.Vulnerability>> vulnerabilitiesByComponent = 
scanResult.getVulnerableComponents().stream()
.collect(Collectors.toMap(
OSSIndexClient.ComponentReport::getCoordinates,
OSSIndexClient.ComponentReport::getVulnerabilities
));
// Prioritize critical and high vulnerabilities
List<RemediationAction> actions = new ArrayList<>();
for (Map.Entry<String, List<OSSIndexClient.Vulnerability>> entry : 
vulnerabilitiesByComponent.entrySet()) {
String component = entry.getKey();
List<OSSIndexClient.Vulnerability> vulnerabilities = entry.getValue();
// Find the highest severity vulnerability
Optional<OSSIndexClient.Vulnerability> highestSeverity = vulnerabilities.stream()
.max(Comparator.comparingDouble(OSSIndexClient.Vulnerability::getCvssScore));
if (highestSeverity.isPresent()) {
RemediationAction action = new RemediationAction();
action.setComponent(component);
action.setHighestSeverity(highestSeverity.get().getSeverity());
action.setVulnerabilityCount(vulnerabilities.size());
action.setRecommendedAction(generateRecommendedAction(component, vulnerabilities));
action.setPriority(calculatePriority(highestSeverity.get()));
actions.add(action);
}
}
// Sort by priority
actions.sort(Comparator.comparingInt(RemediationAction::getPriority).reversed());
plan.setActions(actions);
return plan;
}
private String generateRecommendedAction(String component, 
List<OSSIndexClient.Vulnerability> vulnerabilities) {
// Parse component coordinates
// pkg:maven/groupId/artifactId@version
String[] parts = component.split("/");
if (parts.length >= 3) {
String groupId = parts[1];
String artifactVersion = parts[2];
String[] artifactParts = artifactVersion.split("@");
String artifactId = artifactParts[0];
String version = artifactParts.length > 1 ? artifactParts[1].split("\\?")[0] : "unknown";
return String.format("Upgrade %s:%s from version %s to a secure version", 
groupId, artifactId, version);
}
return "Review component for available security updates";
}
private int calculatePriority(OSSIndexClient.Vulnerability vulnerability) {
switch (vulnerability.getSeverity()) {
case "CRITICAL": return 100;
case "HIGH": return 75;
case "MEDIUM": return 50;
case "LOW": return 25;
default: return 0;
}
}
private void generateProjectReport(ProjectScanResult result) {
try {
String timestamp = LocalDateTime.now().format(
java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
String filename = String.format("project-scan-%s-%s.json", 
result.getProject().getName(), timestamp);
Path reportPath = Paths.get(reportsDirectory, "projects", filename);
Files.createDirectories(reportPath.getParent());
objectMapper.writeValue(reportPath.toFile(), result);
result.setReportPath(reportPath.toString());
} catch (Exception e) {
getLogger().error("Failed to generate project report", e);
}
}
private void generateOrganizationReport(OrganizationScanResult result) {
try {
String timestamp = LocalDateTime.now().format(
java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
String filename = String.format("organization-scan-%s.json", timestamp);
Path reportPath = Paths.get(reportsDirectory, "organization", filename);
Files.createDirectories(reportPath.getParent());
objectMapper.writeValue(reportPath.toFile(), result);
result.setReportPath(reportPath.toString());
} catch (Exception e) {
getLogger().error("Failed to generate organization report", e);
}
}
private void createReportsDirectory() {
try {
Files.createDirectories(Paths.get(reportsDirectory, "projects"));
Files.createDirectories(Paths.get(reportsDirectory, "organization"));
Files.createDirectories(Paths.get(reportsDirectory, "remediation"));
} catch (Exception e) {
throw new RuntimeException("Failed to create reports directory", e);
}
}
public void shutdown() {
executorService.shutdown();
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
try {
ossIndexClient.close();
} catch (Exception e) {
getLogger().error("Failed to close OSS Index client", e);
}
}
private org.slf4j.Logger getLogger() {
return org.slf4j.LoggerFactory.getLogger(EnterpriseVulnerabilityScanner.class);
}
// Data classes
public static class Project {
private String name;
private String version;
private String repositoryUrl;
private String branch;
private Path projectPath;
private BuildTool buildTool;
public List<OSSIndexClient.Component> collectDependencies() throws Exception {
// Implementation would vary based on build tool
switch (buildTool) {
case MAVEN:
return collectMavenDependencies();
case GRADLE:
return collectGradleDependencies();
case SBT:
return collectSbtDependencies();
default:
throw new UnsupportedOperationException("Unsupported build tool: " + buildTool);
}
}
private List<OSSIndexClient.Component> collectMavenDependencies() throws Exception {
// Use Maven dependency plugin or parse pom.xml
return Collections.emptyList();
}
private List<OSSIndexClient.Component> collectGradleDependencies() throws Exception {
// Use Gradle dependency insight or parse build.gradle
return Collections.emptyList();
}
private List<OSSIndexClient.Component> collectSbtDependencies() throws Exception {
// Parse build.sbt or use sbt-dependency-graph
return Collections.emptyList();
}
// 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 getRepositoryUrl() { return repositoryUrl; }
public void setRepositoryUrl(String repositoryUrl) { this.repositoryUrl = repositoryUrl; }
public String getBranch() { return branch; }
public void setBranch(String branch) { this.branch = branch; }
public Path getProjectPath() { return projectPath; }
public void setProjectPath(Path projectPath) { this.projectPath = projectPath; }
public BuildTool getBuildTool() { return buildTool; }
public void setBuildTool(BuildTool buildTool) { this.buildTool = buildTool; }
}
public static class ProjectScanResult {
private Project project;
private LocalDateTime scanStartTime;
private LocalDateTime scanEndTime;
private int totalDependencies;
private List<OSSIndexClient.ComponentReport> vulnerableComponents = new ArrayList<>();
private String error;
private String reportPath;
public ProjectScanResult(Project project) {
this.project = project;
}
// Getters and setters
public void addVulnerableComponent(OSSIndexClient.ComponentReport report) {
vulnerableComponents.add(report);
}
public int getTotalVulnerabilities() {
return vulnerableComponents.stream()
.mapToInt(report -> report.getVulnerabilities().size())
.sum();
}
public int getCriticalCount() {
return vulnerableComponents.stream()
.mapToInt(report -> report.getCriticalVulnerabilities().size())
.sum();
}
// ... other getters and setters
}
public static class OrganizationScanResult {
private LocalDateTime scanStartTime;
private LocalDateTime scanEndTime;
private List<ProjectScanResult> projectResults = new ArrayList<>();
private String reportPath;
public List<OSSIndexClient.Vulnerability> getAllVulnerabilities() {
return projectResults.stream()
.flatMap(projectResult -> projectResult.getVulnerableComponents().stream())
.flatMap(component -> component.getVulnerabilities().stream())
.collect(Collectors.toList());
}
// Getters and setters
public void addProjectResult(ProjectScanResult result) {
projectResults.add(result);
}
}
public static class VulnerabilityTrend {
private String vulnerabilityId;
private TrendType trendType;
private String severityChange;
// Getters and setters
}
public static class RemediationPlan {
private LocalDateTime generatedDate;
private List<RemediationAction> actions = new ArrayList<>();
// Getters and setters
}
public static class RemediationAction {
private String component;
private String highestSeverity;
private int vulnerabilityCount;
private String recommendedAction;
private int priority;
// Getters and setters
}
public enum BuildTool {
MAVEN, GRADLE, SBT, UNKNOWN
}
public enum TrendType {
NEW, FIXED, WORSENED, IMPROVED
}
}

Usage Examples

Basic Usage

public class OSSIndexExample {
public static void main(String[] args) {
try {
// Initialize OSS Index client
OSSIndexClient client = new OSSIndexClient();
// Create components to scan
List<OSSIndexClient.Component> components = Arrays.asList(
new OSSIndexClient.Component("org.springframework", "spring-core", "5.3.23"),
new OSSIndexClient.Component("com.fasterxml.jackson.core", "jackson-databind", "2.14.2"),
new OSSIndexClient.Component("org.apache.logging.log4j", "log4j-core", "2.20.0")
);
// Get vulnerability reports
List<OSSIndexClient.ComponentReport> reports = client.getVulnerabilityReports(components);
// Analyze results
for (OSSIndexClient.ComponentReport report : reports) {
if (report.hasVulnerabilities()) {
System.out.println("Vulnerabilities found in " + report.getCoordinates() + ":");
for (OSSIndexClient.Vulnerability vuln : report.getVulnerabilities()) {
System.out.println("  - " + vuln);
}
}
}
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

Best Practices

  1. Rate Limiting: Always respect OSS Index rate limits (100 requests per minute)
  2. Caching: Cache results to avoid unnecessary API calls
  3. Error Handling: Implement robust error handling for network issues
  4. Dependency Analysis: Scan all dependencies including transitive ones
  5. Regular Scanning: Integrate into CI/CD for continuous monitoring
  6. Remediation Workflow: Establish processes for fixing vulnerabilities
  7. Reporting: Generate comprehensive reports for different stakeholders

This comprehensive OSS Index integration provides enterprise-grade vulnerability scanning for Java applications, helping to identify and remediate security issues in open-source dependencies.

Leave a Reply

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


Macro Nepal Helper