google-site-verification: google61fe8ba583a51912.html
Black Duck Hub in Java: Complete Software Composition Analysis Guide

Introduction to Black Duck Hub

Black Duck Hub (now part of Synopsys) is a comprehensive Software Composition Analysis (SCA) tool that helps identify and manage open-source security vulnerabilities, license compliance risks, and operational risks in your software supply chain.

Key Features

  • Open Source Detection: Automatically discovers open-source components
  • Vulnerability Scanning: Identifies known security vulnerabilities
  • License Compliance: Manages open-source license obligations
  • Policy Management: Enforces security and license policies
  • Integration: REST API for automation and CI/CD integration

Implementation Guide

Dependencies

Add to your pom.xml:

<properties> <blackduck.version>2023.4.0</blackduck.version> <jackson.version>2.15.2</jackson.version> <okhttp.version>4.11.0</okhttp.version> </properties> <dependencies> <!-- Black Duck Hub REST Client --> <dependency> <groupId>com.synopsys.integration</groupId> <artifactId>blackduck-common</artifactId> <version>${blackduck.version}</version> </dependency> <dependency> <groupId>com.synopsys.integration</groupId> <artifactId>blackduck-api</artifactId> <version>${blackduck.version}</version> </dependency> <dependency> <groupId>com.synopsys.integration</groupId> <artifactId>blackduck-http-client</artifactId> <version>${blackduck.version}</version> </dependency> <!-- 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> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.7</version> </dependency> <!-- For file operations --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.13.0</version> </dependency> </dependencies>

Core Black Duck Hub Integration

Configuration and Client Setup

package com.example.blackduck; import com.synopsys.integration.blackduck.api.generated.discovery.ApiDiscovery; import com.synopsys.integration.blackduck.configuration.BlackDuckServerConfig; import com.synopsys.integration.blackduck.configuration.BlackDuckServerConfigBuilder; import com.synopsys.integration.blackduck.service.BlackDuckApiClient; import com.synopsys.integration.blackduck.service.BlackDuckServicesFactory; import com.synopsys.integration.exception.IntegrationException; import com.synopsys.integration.log.Slf4jIntLogger; import com.synopsys.integration.rest.client.IntHttpClient; import com.synopsys.integration.rest.credentials.Credentials; import com.synopsys.integration.rest.credentials.CredentialsBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URL; import java.util.Optional; public class BlackDuckClient { private static final Logger logger = LoggerFactory.getLogger(BlackDuckClient.class); private final BlackDuckServicesFactory blackDuckServicesFactory; private final BlackDuckApiClient blackDuckApiClient; public BlackDuckClient(String hubUrl, String apiToken, int timeoutSeconds) throws IntegrationException { BlackDuckServerConfigBuilder configBuilder = new BlackDuckServerConfigBuilder(); configBuilder.setUrl(hubUrl); configBuilder.setApiToken(apiToken); configBuilder.setTimeout(timeoutSeconds); configBuilder.setTrustCert(true); // Only for testing BlackDuckServerConfig blackDuckServerConfig = configBuilder.build(); Slf4jIntLogger intLogger = new Slf4jIntLogger(logger); this.blackDuckServicesFactory = blackDuckServerConfig.createBlackDuckServicesFactory(intLogger); this.blackDuckApiClient = blackDuckServicesFactory.getBlackDuckApiClient(); logger.info("Black Duck Hub client initialized for: {}", hubUrl); } public BlackDuckClient(String hubUrl, String username, String password, int timeoutSeconds) throws IntegrationException { BlackDuckServerConfigBuilder configBuilder = new BlackDuckServerConfigBuilder(); configBuilder.setUrl(hubUrl); CredentialsBuilder credentialsBuilder = new CredentialsBuilder(); credentialsBuilder.setUsername(username); credentialsBuilder.setPassword(password); Credentials credentials = credentialsBuilder.build(); configBuilder.setCredentials(credentials); configBuilder.setTimeout(timeoutSeconds); configBuilder.setTrustCert(true); // Only for testing BlackDuckServerConfig blackDuckServerConfig = configBuilder.build(); Slf4jIntLogger intLogger = new Slf4jIntLogger(logger); this.blackDuckServicesFactory = blackDuckServerConfig.createBlackDuckServicesFactory(intLogger); this.blackDuckApiClient = blackDuckServicesFactory.getBlackDuckApiClient(); logger.info("Black Duck Hub client initialized for: {}", hubUrl); } public boolean testConnection() { try { ApiDiscovery apiDiscovery = new ApiDiscovery(blackDuckApiClient); apiDiscovery.getCurrentVersion(); logger.info("Black Duck Hub connection test successful"); return true; } catch (IntegrationException e) { logger.error("Black Duck Hub connection test failed", e); return false; } } public BlackDuckServicesFactory getServicesFactory() { return blackDuckServicesFactory; } public BlackDuckApiClient getApiClient() { return blackDuckApiClient; } public void close() { if (blackDuckApiClient != null) { try { blackDuckApiClient.close(); } catch (IOException e) { logger.warn("Error closing Black Duck client", e); } } } }

Project Management

package com.example.blackduck.projects; import com.synopsys.integration.blackduck.api.generated.component.ProjectRequest; import com.synopsys.integration.blackduck.api.generated.view.ProjectView; import com.synopsys.integration.blackduck.service.BlackDuckApiClient; import com.synopsys.integration.blackduck.service.dataservice.ProjectService; import com.synopsys.integration.blackduck.service.model.ProjectVersionWrapper; import com.synopsys.integration.exception.IntegrationException; import com.synopsys.integration.rest.HttpUrl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Optional; public class ProjectManager { private static final Logger logger = LoggerFactory.getLogger(ProjectManager.class); private final ProjectService projectService; private final BlackDuckApiClient blackDuckApiClient; public ProjectManager(ProjectService projectService, BlackDuckApiClient blackDuckApiClient) { this.projectService = projectService; this.blackDuckApiClient = blackDuckApiClient; } public ProjectVersionWrapper createProject(String projectName, String versionName, String description) throws IntegrationException { logger.info("Creating project: {} version: {}", projectName, versionName); ProjectRequest projectRequest = new ProjectRequest(); projectRequest.setName(projectName); projectRequest.setDescription(description); projectRequest.setProjectLevelAdjustments(true); projectRequest.setVersion(versionName); ProjectVersionWrapper projectVersionWrapper = projectService.createProject(projectRequest); logger.info("Successfully created project: {} with version: {}", projectName, versionName); return projectVersionWrapper; } public Optional<ProjectVersionWrapper> getProject(String projectName, String versionName) throws IntegrationException { logger.debug("Looking up project: {} version: {}", projectName, versionName); return projectService.getProjectVersion(projectName, versionName); } public List<ProjectView> getAllProjects() throws IntegrationException { logger.debug("Retrieving all projects"); return projectService.getAllProjects(); } public ProjectVersionWrapper getOrCreateProject(String projectName, String versionName, String description) throws IntegrationException { Optional<ProjectVersionWrapper> existingProject = getProject(projectName, versionName); if (existingProject.isPresent()) { logger.info("Found existing project: {} version: {}", projectName, versionName); return existingProject.get(); } else { logger.info("Project not found, creating new: {} version: {}", projectName, versionName); return createProject(projectName, versionName, description); } } public boolean deleteProject(String projectName, String versionName) throws IntegrationException { Optional<ProjectVersionWrapper> projectWrapper = getProject(projectName, versionName); if (projectWrapper.isPresent()) { ProjectVersionWrapper wrapper = projectWrapper.get(); HttpUrl projectVersionUrl = wrapper.getProjectVersionView().getHref(); blackDuckApiClient.delete(projectVersionUrl); logger.info("Successfully deleted project: {} version: {}", projectName, versionName); return true; } else { logger.warn("Project not found for deletion: {} version: {}", projectName, versionName); return false; } } public void updateProjectDescription(String projectName, String versionName, String newDescription) throws IntegrationException { Optional<ProjectVersionWrapper> projectWrapper = getProject(projectName, versionName); if (projectWrapper.isPresent()) { ProjectVersionWrapper wrapper = projectWrapper.get(); ProjectView projectView = wrapper.getProjectView(); projectView.setDescription(newDescription); blackDuckApiClient.put(projectView); logger.info("Updated project description for: {} version: {}", projectName, versionName); } else { logger.warn("Project not found for update: {} version: {}", projectName, versionName); } } }

Vulnerability and Component Analysis

Component Scanner

package com.example.blackduck.scan; import com.synopsys.integration.blackduck.api.generated.component.ProjectVersionComponentRequest; import com.synopsys.integration.blackduck.api.generated.view.ComponentSearchResultView; import com.synopsys.integration.blackduck.api.generated.view.ProjectVersionComponentView; import com.synopsys.integration.blackduck.api.generated.view.VulnerabilityView; import com.synopsys.integration.blackduck.service.BlackDuckApiClient; import com.synopsys.integration.blackduck.service.dataservice.ComponentService; import com.synopsys.integration.blackduck.service.dataservice.ProjectBomService; import com.synopsys.integration.blackduck.service.model.ComponentVersion; import com.synopsys.integration.blackduck.service.model.ProjectVersionWrapper; import com.synopsys.integration.exception.IntegrationException; import com.synopsys.integration.rest.HttpUrl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; public class ComponentScanner { private static final Logger logger = LoggerFactory.getLogger(ComponentScanner.class); private final ComponentService componentService; private final ProjectBomService projectBomService; private final BlackDuckApiClient blackDuckApiClient; public ComponentScanner(ComponentService componentService, ProjectBomService projectBomService, BlackDuckApiClient blackDuckApiClient) { this.componentService = componentService; this.projectBomService = projectBomService; this.blackDuckApiClient = blackDuckApiClient; } public List<ComponentSearchResultView> searchComponent(String componentName, String version) throws IntegrationException { logger.debug("Searching for component: {} version: {}", componentName, version); return componentService.getAllComponentSearchResults(componentName, version); } public ComponentVersion getComponentVersion(String componentName, String version) throws IntegrationException { logger.debug("Getting component version: {} version: {}", componentName, version); List<ComponentSearchResultView> searchResults = searchComponent(componentName, version); if (searchResults.isEmpty()) { logger.warn("Component not found: {} version: {}", componentName, version); return null; } // Get the first result (most relevant) ComponentSearchResultView searchResult = searchResults.get(0); HttpUrl componentVersionUrl = searchResult.getComponentVersion(); return componentService.getComponentVersion(componentVersionUrl); } public List<VulnerabilityView> getComponentVulnerabilities(String componentName, String version) throws IntegrationException { logger.debug("Getting vulnerabilities for component: {} version: {}", componentName, version); ComponentVersion componentVersion = getComponentVersion(componentName, version); if (componentVersion == null) { return Collections.emptyList(); } return componentService.getVulnerabilitiesForComponentVersion(componentVersion); } public void addComponentToProject(ProjectVersionWrapper projectWrapper, String componentName, String componentVersion, String usage) throws IntegrationException { logger.info("Adding component {}:{} to project", componentName, componentVersion); ComponentVersion component = getComponentVersion(componentName, componentVersion); if (component == null) { logger.error("Component not found: {}:{}", componentName, componentVersion); throw new IllegalArgumentException("Component not found: " + componentName + ":" + componentVersion); } ProjectVersionComponentRequest componentRequest = new ProjectVersionComponentRequest(); componentRequest.setComponentVersion(component.getHref().string()); componentRequest.setComponentName(component.getComponentName()); componentRequest.setComponentVersionName(component.getVersionName()); componentRequest.setComponentUsage(usage != null ? usage : "DYNAMICALLY_LINKED"); projectBomService.addComponentToProjectVersion(componentRequest, projectWrapper.getProjectVersionView()); logger.info("Successfully added component {}:{} to project", componentName, componentVersion); } public List<ProjectVersionComponentView> getProjectBom(ProjectVersionWrapper projectWrapper) throws IntegrationException { logger.debug("Getting BOM for project: {}", projectWrapper.getProjectView().getName()); return projectBomService.getComponentsForProjectVersion(projectWrapper.getProjectVersionView()); } public VulnerabilityReport generateVulnerabilityReport(ProjectVersionWrapper projectWrapper) throws IntegrationException { logger.info("Generating vulnerability report for project: {}", projectWrapper.getProjectView().getName()); List<ProjectVersionComponentView> bomComponents = getProjectBom(projectWrapper); VulnerabilityReport report = new VulnerabilityReport(projectWrapper); for (ProjectVersionComponentView component : bomComponents) { ComponentVulnerability componentVuln = analyzeComponentVulnerabilities(component); report.addComponentVulnerability(componentVuln); } report.calculateSummary(); logger.info("Vulnerability report generated with {} components", bomComponents.size()); return report; } private ComponentVulnerability analyzeComponentVulnerabilities(ProjectVersionComponentView component) throws IntegrationException { ComponentVulnerability componentVuln = new ComponentVulnerability( component.getComponentName(), component.getComponentVersionName(), component.getComponentVersion() ); // Get vulnerability data List<VulnerabilityView> vulnerabilities = componentService.getVulnerabilitiesForComponent( component.getComponentVersion() ); for (VulnerabilityView vulnerability : vulnerabilities) { VulnerabilityDetail vulnDetail = new VulnerabilityDetail( vulnerability.getName(), vulnerability.getDescription(), vulnerability.getBaseScore(), vulnerability.getSeverity(), vulnerability.getCweId(), vulnerability.getPublishedDate(), vulnerability.getUpdatedDate() ); componentVuln.addVulnerability(vulnDetail); } return componentVuln; } public Map<String, Integer> getVulnerabilitySummary(ProjectVersionWrapper projectWrapper) throws IntegrationException { VulnerabilityReport report = generateVulnerabilityReport(projectWrapper); return report.getVulnerabilitySummary(); } }

Vulnerability Reporting

package com.example.blackduck.scan; import com.synopsys.integration.blackduck.service.model.ProjectVersionWrapper; import java.util.*; public class VulnerabilityReport { private final ProjectVersionWrapper projectWrapper; private final List<ComponentVulnerability> componentVulnerabilities; private final Map<String, Integer> vulnerabilitySummary; private final Date reportDate; public VulnerabilityReport(ProjectVersionWrapper projectWrapper) { this.projectWrapper = projectWrapper; this.componentVulnerabilities = new ArrayList<>(); this.vulnerabilitySummary = new HashMap<>(); this.reportDate = new Date(); } public void addComponentVulnerability(ComponentVulnerability componentVuln) { componentVulnerabilities.add(componentVuln); } public void calculateSummary() { vulnerabilitySummary.clear(); for (ComponentVulnerability componentVuln : componentVulnerabilities) { for (VulnerabilityDetail vuln : componentVuln.getVulnerabilities()) { String severity = vuln.getSeverity(); vulnerabilitySummary.put(severity, vulnerabilitySummary.getOrDefault(severity, 0) + 1); } } } public List<ComponentVulnerability> getCriticalComponents() { List<ComponentVulnerability> criticalComponents = new ArrayList<>(); for (ComponentVulnerability component : componentVulnerabilities) { if (component.hasCriticalVulnerabilities()) { criticalComponents.add(component); } } return criticalComponents; } public List<ComponentVulnerability> getComponentsWithVulnerabilities() { List<ComponentVulnerability> componentsWithVulns = new ArrayList<>(); for (ComponentVulnerability component : componentVulnerabilities) { if (component.hasVulnerabilities()) { componentsWithVulns.add(component); } } return componentsWithVulns; } // Getters public ProjectVersionWrapper getProjectWrapper() { return projectWrapper; } public List<ComponentVulnerability> getComponentVulnerabilities() { return componentVulnerabilities; } public Map<String, Integer> getVulnerabilitySummary() { return vulnerabilitySummary; } public Date getReportDate() { return reportDate; } public int getTotalVulnerabilities() { return vulnerabilitySummary.values().stream().mapToInt(Integer::intValue).sum(); } public String getProjectName() { return projectWrapper.getProjectView().getName(); } public String getProjectVersion() { return projectWrapper.getProjectVersionView().getVersionName(); } } class ComponentVulnerability { private final String componentName; private final String componentVersion; private final String componentUrl; private final List<VulnerabilityDetail> vulnerabilities; public ComponentVulnerability(String componentName, String componentVersion, String componentUrl) { this.componentName = componentName; this.componentVersion = componentVersion; this.componentUrl = componentUrl; this.vulnerabilities = new ArrayList<>(); } public void addVulnerability(VulnerabilityDetail vulnerability) { vulnerabilities.add(vulnerability); } public boolean hasVulnerabilities() { return !vulnerabilities.isEmpty(); } public boolean hasCriticalVulnerabilities() { return vulnerabilities.stream() .anyMatch(vuln -> "CRITICAL".equalsIgnoreCase(vuln.getSeverity())); } public boolean hasHighVulnerabilities() { return vulnerabilities.stream() .anyMatch(vuln -> "HIGH".equalsIgnoreCase(vuln.getSeverity())); } public int getVulnerabilityCount() { return vulnerabilities.size(); } public int getVulnerabilityCountBySeverity(String severity) { return (int) vulnerabilities.stream() .filter(vuln -> severity.equalsIgnoreCase(vuln.getSeverity())) .count(); } // Getters public String getComponentName() { return componentName; } public String getComponentVersion() { return componentVersion; } public String getComponentUrl() { return componentUrl; } public List<VulnerabilityDetail> getVulnerabilities() { return vulnerabilities; } } class VulnerabilityDetail { private final String name; private final String description; private final Double baseScore; private final String severity; private final String cweId; private final Date publishedDate; private final Date updatedDate; public VulnerabilityDetail(String name, String description, Double baseScore, String severity, String cweId, Date publishedDate, Date updatedDate) { this.name = name; this.description = description; this.baseScore = baseScore; this.severity = severity; this.cweId = cweId; this.publishedDate = publishedDate; this.updatedDate = updatedDate; } // Getters public String getName() { return name; } public String getDescription() { return description; } public Double getBaseScore() { return baseScore; } public String getSeverity() { return severity; } public String getCweId() { return cweId; } public Date getPublishedDate() { return publishedDate; } public Date getUpdatedDate() { return updatedDate; } public boolean isCritical() { return "CRITICAL".equalsIgnoreCase(severity); } public boolean isHigh() { return "HIGH".equalsIgnoreCase(severity); } public boolean isMedium() { return "MEDIUM".equalsIgnoreCase(severity); } public boolean isLow() { return "LOW".equalsIgnoreCase(severity); } }

License Compliance Management

package com.example.blackduck.license; import com.synopsys.integration.blackduck.api.generated.view.LicenseView; import com.synopsys.integration.blackduck.api.generated.view.ProjectVersionComponentView; import com.synopsys.integration.blackduck.service.BlackDuckApiClient; import com.synopsys.integration.blackduck.service.dataservice.LicenseService; import com.synopsys.integration.blackduck.service.dataservice.ProjectBomService; import com.synopsys.integration.blackduck.service.model.LicenseDefinition; import com.synopsys.integration.blackduck.service.model.ProjectVersionWrapper; import com.synopsys.integration.exception.IntegrationException; import com.synopsys.integration.rest.HttpUrl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; public class LicenseComplianceManager { private static final Logger logger = LoggerFactory.getLogger(LicenseComplianceManager.class); private final LicenseService licenseService; private final ProjectBomService projectBomService; private final BlackDuckApiClient blackDuckApiClient; public LicenseComplianceManager(LicenseService licenseService, ProjectBomService projectBomService, BlackDuckApiClient blackDuckApiClient) { this.licenseService = licenseService; this.projectBomService = projectBomService; this.blackDuckApiClient = blackDuckApiClient; } public List<LicenseView> getAllLicenses() throws IntegrationException { logger.debug("Retrieving all licenses"); return licenseService.getAllLicenses(); } public Optional<LicenseView> getLicenseByName(String licenseName) throws IntegrationException { logger.debug("Looking up license: {}", licenseName); List<LicenseView> allLicenses = getAllLicenses(); return allLicenses.stream() .filter(license -> licenseName.equals(license.getName())) .findFirst(); } public LicenseView getLicenseById(String licenseId) throws IntegrationException { logger.debug("Looking up license by ID: {}", licenseId); List<LicenseView> allLicenses = getAllLicenses(); return allLicenses.stream() .filter(license -> licenseId.equals(license.getLicenseId())) .findFirst() .orElse(null); } public LicenseReport generateLicenseReport(ProjectVersionWrapper projectWrapper) throws IntegrationException { logger.info("Generating license report for project: {}", projectWrapper.getProjectView().getName()); List<ProjectVersionComponentView> bomComponents = projectBomService.getComponentsForProjectVersion(projectWrapper.getProjectVersionView()); LicenseReport report = new LicenseReport(projectWrapper); for (ProjectVersionComponentView component : bomComponents) { ComponentLicenseInfo licenseInfo = analyzeComponentLicenses(component); report.addComponentLicense(licenseInfo); } report.calculateSummary(); logger.info("License report generated with {} components", bomComponents.size()); return report; } private ComponentLicenseInfo analyzeComponentLicenses(ProjectVersionComponentView component) throws IntegrationException { ComponentLicenseInfo licenseInfo = new ComponentLicenseInfo( component.getComponentName(), component.getComponentVersionName(), component.getComponentVersion() ); // Get license information List<LicenseDefinition> licenses = componentService.getLicensesForComponent( component.getComponentVersion() ); for (LicenseDefinition license : licenses) { LicenseDetail licenseDetail = new LicenseDetail( license.getName(), license.getLicenseId(), license.getLicenseType(), license.getLicenseFamilyName(), license.getLicenseDisplay() ); licenseInfo.addLicense(licenseDetail); } return licenseInfo; } public List<ComponentLicenseInfo> getComponentsWithRestrictiveLicenses(ProjectVersionWrapper projectWrapper) throws IntegrationException { LicenseReport report = generateLicenseReport(projectWrapper); return report.getComponentsWithRestrictiveLicenses(); } public Map<String, Integer> getLicenseDistribution(ProjectVersionWrapper projectWrapper) throws IntegrationException { LicenseReport report = generateLicenseReport(projectWrapper); return report.getLicenseDistribution(); } public boolean hasLicenseViolations(ProjectVersionWrapper projectWrapper) throws IntegrationException { LicenseReport report = generateLicenseReport(projectWrapper); return report.hasRestrictiveLicenses(); } } class LicenseReport { private final ProjectVersionWrapper projectWrapper; private final List<ComponentLicenseInfo> componentLicenses; private final Map<String, Integer> licenseDistribution; private final Date reportDate; public LicenseReport(ProjectVersionWrapper projectWrapper) { this.projectWrapper = projectWrapper; this.componentLicenses = new ArrayList<>(); this.licenseDistribution = new HashMap<>(); this.reportDate = new Date(); } public void addComponentLicense(ComponentLicenseInfo licenseInfo) { componentLicenses.add(licenseInfo); } public void calculateSummary() { licenseDistribution.clear(); for (ComponentLicenseInfo componentLicense : componentLicenses) { for (LicenseDetail license : componentLicense.getLicenses()) { String licenseName = license.getName(); licenseDistribution.put(licenseName, licenseDistribution.getOrDefault(licenseName, 0) + 1); } } } public List<ComponentLicenseInfo> getComponentsWithRestrictiveLicenses() { List<ComponentLicenseInfo> restrictiveComponents = new ArrayList<>(); for (ComponentLicenseInfo component : componentLicenses) { if (component.hasRestrictiveLicense()) { restrictiveComponents.add(component); } } return restrictiveComponents; } public boolean hasRestrictiveLicenses() { return !getComponentsWithRestrictiveLicenses().isEmpty(); } // Getters public ProjectVersionWrapper getProjectWrapper() { return projectWrapper; } public List<ComponentLicenseInfo> getComponentLicenses() { return componentLicenses; } public Map<String, Integer> getLicenseDistribution() { return licenseDistribution; } public Date getReportDate() { return reportDate; } public int getTotalComponents() { return componentLicenses.size(); } public String getProjectName() { return projectWrapper.getProjectView().getName(); } public String getProjectVersion() { return projectWrapper.getProjectVersionView().getVersionName(); } } class ComponentLicenseInfo { private final String componentName; private final String componentVersion; private final String componentUrl; private final List<LicenseDetail> licenses; public ComponentLicenseInfo(String componentName, String componentVersion, String componentUrl) { this.componentName = componentName; this.componentVersion = componentVersion; this.componentUrl = componentUrl; this.licenses = new ArrayList<>(); } public void addLicense(LicenseDetail license) { licenses.add(license); } public boolean hasLicenses() { return !licenses.isEmpty(); } public boolean hasRestrictiveLicense() { // Define what constitutes a restrictive license return licenses.stream() .anyMatch(license -> isRestrictiveLicense(license.getName())); } private boolean isRestrictiveLicense(String licenseName) { String lowerLicense = licenseName.toLowerCase(); return lowerLicense.contains("gpl") || lowerLicense.contains("agpl") || lowerLicense.contains("lgpl") || lowerLicense.contains("copyleft") || lowerLicense.contains("proprietary"); } // Getters public String getComponentName() { return componentName; } public String getComponentVersion() { return componentVersion; } public String getComponentUrl() { return componentUrl; } public List<LicenseDetail> getLicenses() { return licenses; } } class LicenseDetail { private final String name; private final String licenseId; private final String licenseType; private final String licenseFamily; private final String licenseDisplay; public LicenseDetail(String name, String licenseId, String licenseType, String licenseFamily, String licenseDisplay) { this.name = name; this.licenseId = licenseId; this.licenseType = licenseType; this.licenseFamily = licenseFamily; this.licenseDisplay = licenseDisplay; } // Getters public String getName() { return name; } public String getLicenseId() { return licenseId; } public String getLicenseType() { return licenseType; } public String getLicenseFamily() { return licenseFamily; } public String getLicenseDisplay() { return licenseDisplay; } public boolean isOpenSource() { return "OPEN_SOURCE".equalsIgnoreCase(licenseType); } public boolean isProprietary() { return "PROPRIETARY".equalsIgnoreCase(licenseType); } }

Policy Management

package com.example.blackduck.policy; import com.synopsys.integration.blackduck.api.generated.view.PolicyRuleView; import com.synopsys.integration.blackduck.api.generated.view.PolicyView; import com.synopsys.integration.blackduck.service.BlackDuckApiClient; import com.synopsys.integration.blackduck.service.dataservice.PolicyService; import com.synopsys.integration.blackduck.service.model.PolicyRuleExpressionSetBuilder; import com.synopsys.integration.blackduck.service.model.ProjectVersionWrapper; import com.synopsys.integration.exception.IntegrationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Optional; public class PolicyManager { private static final Logger logger = LoggerFactory.getLogger(PolicyManager.class); private final PolicyService policyService; private final BlackDuckApiClient blackDuckApiClient; public PolicyManager(PolicyService policyService, BlackDuckApiClient blackDuckApiClient) { this.policyService = policyService; this.blackDuckApiClient = blackDuckApiClient; } public List<PolicyView> getAllPolicies() throws IntegrationException { logger.debug("Retrieving all policies"); return policyService.getAllPolicies(); } public Optional<PolicyView> getPolicyByName(String policyName) throws IntegrationException { logger.debug("Looking up policy: {}", policyName); List<PolicyView> allPolicies = getAllPolicies(); return allPolicies.stream() .filter(policy -> policyName.equals(policy.getName())) .findFirst(); } public PolicyView createPolicy(String policyName, String description) throws IntegrationException { logger.info("Creating policy: {}", policyName); PolicyView policy = new PolicyView(); policy.setName(policyName); policy.setDescription(description); PolicyView createdPolicy = policyService.createPolicy(policy); logger.info("Successfully created policy: {}", policyName); return createdPolicy; } public PolicyRuleView createSecurityPolicyRule(String policyName, int maxCriticalVulns, int maxHighVulns) throws IntegrationException { logger.info("Creating security policy rule for policy: {}", policyName); Optional<PolicyView> policy = getPolicyByName(policyName); if (!policy.isPresent()) { throw new IllegalArgumentException("Policy not found: " + policyName); } PolicyRuleExpressionSetBuilder expressionBuilder = new PolicyRuleExpressionSetBuilder(); // Add vulnerability severity conditions if (maxCriticalVulns >= 0) { expressionBuilder.addVulnerabilitySeverityCondition("CRITICAL", maxCriticalVulns); } if (maxHighVulns >= 0) { expressionBuilder.addVulnerabilitySeverityCondition("HIGH", maxHighVulns); } PolicyRuleView policyRule = new PolicyRuleView(); policyRule.setName("Security Vulnerability Rule"); policyRule.setEnabled(true); policyRule.setOverridable(true); policyRule.setExpression(expressionBuilder.build()); PolicyRuleView createdRule = policyService.createPolicyRule(policy.get(), policyRule); logger.info("Successfully created security policy rule"); return createdRule; } public PolicyRuleView createLicensePolicyRule(String policyName, List<String> bannedLicenses) throws IntegrationException { logger.info("Creating license policy rule for policy: {}", policyName); Optional<PolicyView> policy = getPolicyByName(policyName); if (!policy.isPresent()) { throw new IllegalArgumentException("Policy not found: " + policyName); } PolicyRuleExpressionSetBuilder expressionBuilder = new PolicyRuleExpressionSetBuilder(); // Add license conditions for (String license : bannedLicenses) { expressionBuilder.addLicenseCondition(license); } PolicyRuleView policyRule = new PolicyRuleView(); policyRule.setName("License Compliance Rule"); policyRule.setEnabled(true); policyRule.setOverridable(true); policyRule.setExpression(expressionBuilder.build()); PolicyRuleView createdRule = policyService.createPolicyRule(policy.get(), policyRule); logger.info("Successfully created license policy rule"); return createdRule; } public PolicyStatus checkPolicyCompliance(ProjectVersionWrapper projectWrapper) throws IntegrationException { logger.info("Checking policy compliance for project: {}", projectWrapper.getProjectView().getName()); return policyService.getPolicyStatusForProjectVersion(projectWrapper.getProjectVersionView()); } public boolean isPolicyCompliant(ProjectVersionWrapper projectWrapper) throws IntegrationException { PolicyStatus policyStatus = checkPolicyCompliance(projectWrapper); return policyStatus.getOverallStatus().isPassed(); } public List<PolicyRuleView> getViolatedPolicies(ProjectVersionWrapper projectWrapper) throws IntegrationException { PolicyStatus policyStatus = checkPolicyCompliance(projectWrapper); return policyStatus.getViolatedPolicies(); } }

Automated Scanners and Integrations

Maven Project Scanner

package com.example.blackduck.scan; import com.example.blackduck.BlackDuckClient; import com.example.blackduck.projects.ProjectManager; import com.synopsys.integration.blackduck.service.dataservice.ProjectBomService; import com.synopsys.integration.blackduck.service.dataservice.ProjectService; import com.synopsys.integration.blackduck.service.model.ProjectVersionWrapper; import com.synopsys.integration.exception.IntegrationException; import org.apache.maven.model.Dependency; import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.nio.file.Path; import java.util.List; public class MavenProjectScanner { private static final Logger logger = LoggerFactory.getLogger(MavenProjectScanner.class); private final ProjectManager projectManager; private final ComponentScanner componentScanner; public MavenProjectScanner(ProjectManager projectManager, ComponentScanner componentScanner) { this.projectManager = projectManager; this.componentScanner = componentScanner; } public ScanResult scanMavenProject(Path pomFilePath, String projectName, String version) throws IntegrationException, IOException, XmlPullParserException { logger.info("Scanning Maven project: {} from {}", projectName, pomFilePath); // Parse Maven POM MavenProject mavenProject = parseMavenPom(pomFilePath); // Create or get Black Duck project ProjectVersionWrapper projectWrapper = projectManager.getOrCreateProject( projectName, version, mavenProject.getDescription()); // Add dependencies to Black Duck project int addedComponents = addDependenciesToProject(projectWrapper, mavenProject.getDependencies()); // Generate reports VulnerabilityReport vulnReport = componentScanner.generateVulnerabilityReport(projectWrapper); return new ScanResult(projectWrapper, mavenProject, vulnReport, addedComponents); } private MavenProject parseMavenPom(Path pomFilePath) throws IOException, XmlPullParserException { MavenXpp3Reader reader = new MavenXpp3Reader(); Model model = reader.read(new FileReader(pomFilePath.toFile())); MavenProject mavenProject = new MavenProject(); mavenProject.setGroupId(model.getGroupId()); mavenProject.setArtifactId(model.getArtifactId()); mavenProject.setVersion(model.getVersion()); mavenProject.setDescription(model.getDescription()); mavenProject.setDependencies(model.getDependencies()); logger.debug("Parsed Maven project: {}:{}:{}", mavenProject.getGroupId(), mavenProject.getArtifactId(), mavenProject.getVersion()); return mavenProject; } private int addDependenciesToProject(ProjectVersionWrapper projectWrapper, List<Dependency> dependencies) throws IntegrationException { int addedCount = 0; for (Dependency dependency : dependencies) { try { String componentName = dependency.getGroupId() + ":" + dependency.getArtifactId(); String componentVersion = dependency.getVersion(); // Clean version string (remove Maven properties, etc.) componentVersion = cleanVersion(componentVersion); if (componentVersion != null && !componentVersion.isEmpty()) { componentScanner.addComponentToProject(projectWrapper, componentName, componentVersion, "DYNAMICALLY_LINKED"); addedCount++; logger.debug("Added component: {}:{}", componentName, componentVersion); } } catch (Exception e) { logger.warn("Failed to add dependency: {}:{}", dependency.getGroupId(), dependency.getArtifactId(), e); } } logger.info("Added {} dependencies to project", addedCount); return addedCount; } private String cleanVersion(String version) { if (version == null) return null; // Remove Maven properties like ${project.version} if (version.startsWith("${") && version.endsWith("}")) { return null; } // Remove version ranges if (version.contains("[") || version.contains("(")) { return version.replaceAll("[[\\]()]", "").split(",")[0].trim(); } return version.trim(); } public void scanMavenMultiModule(Path rootPomPath, String baseProjectName) throws IntegrationException, IOException, XmlPullParserException { File rootDir = rootPomPath.getParent().toFile(); // Find all POM files List<File> pomFiles = findPomFiles(rootDir); for (File pomFile : pomFiles) { try { MavenProject mavenProject = parseMavenPom(pomFile.toPath()); String projectName = baseProjectName + " - " + mavenProject.getArtifactId(); scanMavenProject(pomFile.toPath(), projectName, mavenProject.getVersion()); } catch (Exception e) { logger.error("Failed to scan module: {}", pomFile.getName(), e); } } } private List<File> findPomFiles(File directory) { List<File> pomFiles = new java.util.ArrayList<>(); findPomFilesRecursive(directory, pomFiles); return pomFiles; } private void findPomFilesRecursive(File directory, List<File> pomFiles) { File[] files = directory.listFiles(); if (files == null) return; for (File file : files) { if (file.isDirectory()) { findPomFilesRecursive(file, pomFiles); } else if (file.getName().equals("pom.xml")) { pomFiles.add(file); } } } } class MavenProject { private String groupId; private String artifactId; private String version; private String description; private List<Dependency> dependencies; // Getters and setters public String getGroupId() { return groupId; } public void setGroupId(String groupId) { this.groupId = groupId; } public String getArtifactId() { return artifactId; } public void setArtifactId(String artifactId) { this.artifactId = artifactId; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public List<Dependency> getDependencies() { return dependencies; } public void setDependencies(List<Dependency> dependencies) { this.dependencies = dependencies; } } class ScanResult { private final ProjectVersionWrapper projectWrapper; private final MavenProject mavenProject; private final VulnerabilityReport vulnerabilityReport; private final int componentsAdded; public ScanResult(ProjectVersionWrapper projectWrapper, MavenProject mavenProject, VulnerabilityReport vulnerabilityReport, int componentsAdded) { this.projectWrapper = projectWrapper; this.mavenProject = mavenProject; this.vulnerabilityReport = vulnerabilityReport; this.componentsAdded = componentsAdded; } // Getters public ProjectVersionWrapper getProjectWrapper() { return projectWrapper; } public MavenProject getMavenProject() { return mavenProject; } public VulnerabilityReport getVulnerabilityReport() { return vulnerabilityReport; } public int getComponentsAdded() { return componentsAdded; } public boolean hasVulnerabilities() { return vulnerabilityReport.getTotalVulnerabilities() > 0; } public boolean hasCriticalVulnerabilities() { return !vulnerabilityReport.getCriticalComponents().isEmpty(); } }

Report Generation

package com.example.blackduck.report; import com.example.blackduck.license.LicenseReport; import com.example.blackduck.scan.VulnerabilityReport; 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.Map; public class ReportGenerator { private static final Logger logger = LoggerFactory.getLogger(ReportGenerator.class); private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); private final ObjectMapper objectMapper; public ReportGenerator() { this.objectMapper = new ObjectMapper(); this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); } public void generateHtmlReport(VulnerabilityReport vulnReport, LicenseReport licenseReport, Path outputDir) throws IOException { String timestamp = DATE_FORMAT.format(new Date()); String filename = "blackduck_report_" + timestamp + ".html"; Path outputPath = outputDir.resolve(filename); String htmlContent = generateHtmlContent(vulnReport, licenseReport); try (FileWriter writer = new FileWriter(outputPath.toFile())) { writer.write(htmlContent); } logger.info("HTML report generated: {}", outputPath); } public void generateJsonReport(VulnerabilityReport vulnReport, LicenseReport licenseReport, Path outputDir) throws IOException { String timestamp = DATE_FORMAT.format(new Date()); String filename = "blackduck_report_" + timestamp + ".json"; Path outputPath = outputDir.resolve(filename); CombinedReport combinedReport = new CombinedReport(vulnReport, licenseReport); objectMapper.writeValue(outputPath.toFile(), combinedReport); logger.info("JSON report generated: {}", outputPath); } public void generateConsoleReport(VulnerabilityReport vulnReport, LicenseReport licenseReport) { System.out.println("=== BLACK DUCK SECURITY REPORT ==="); System.out.printf("Project: %s%n", vulnReport.getProjectName()); System.out.printf("Version: %s%n", vulnReport.getProjectVersion()); System.out.printf("Report Date: %s%n", vulnReport.getReportDate()); System.out.println(); // Vulnerability Summary System.out.println("VULNERABILITY SUMMARY:"); Map<String, Integer> vulnSummary = vulnReport.getVulnerabilitySummary(); for (Map.Entry<String, Integer> entry : vulnSummary.entrySet()) { System.out.printf(" %s: %d%n", entry.getKey(), entry.getValue()); } System.out.printf("Total Vulnerabilities: %d%n", vulnReport.getTotalVulnerabilities()); System.out.println(); // Critical Components if (!vulnReport.getCriticalComponents().isEmpty()) { System.out.println("CRITICAL COMPONENTS:"); vulnReport.getCriticalComponents().forEach(component -> { System.out.printf(" %s:%s - %d vulnerabilities%n", component.getComponentName(), component.getComponentVersion(), component.getVulnerabilityCount()); }); System.out.println(); } // License Summary System.out.println("LICENSE SUMMARY:"); Map<String, Integer> licenseDistribution = licenseReport.getLicenseDistribution(); for (Map.Entry<String, Integer> entry : licenseDistribution.entrySet()) { System.out.printf(" %s: %d%n", entry.getKey(), entry.getValue()); } System.out.printf("Total Components: %d%n", licenseReport.getTotalComponents()); if (licenseReport.hasRestrictiveLicenses()) { System.out.println("WARNING: Restrictive licenses detected!"); } } private String generateHtmlContent(VulnerabilityReport vulnReport, LicenseReport licenseReport) { return """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Black Duck 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; } 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>Black Duck Security Report</h1> <p>Project: """ + vulnReport.getProjectName() + """</p> <p>Version: """ + vulnReport.getProjectVersion() + """</p> <p>Generated: """ + vulnReport.getReportDate() + """</p> </div> <div class="summary"> <h2>Executive Summary</h2> <p>Total Vulnerabilities: """ + vulnReport.getTotalVulnerabilities() + """</p> <p>Components with Critical Vulnerabilities: """ + vulnReport.getCriticalComponents().size() + """</p> <p>Restrictive Licenses: """ + (licenseReport.hasRestrictiveLicenses() ? "Yes" : "No") + """</p> </div> <div class="section"> <h2>Vulnerability Summary</h2> <table> <tr><th>Severity</th><th>Count</th></tr> """ + generateVulnerabilitySummaryRows(vulnReport) + """ </table> </div> <div class="section"> <h2>License Distribution</h2> <table> <tr><th>License</th><th>Count</th></tr> """ + generateLicenseDistributionRows(licenseReport) + """ </table> </div> <div class="section"> <h2>Critical Components</h2> """ + generateCriticalComponentsList(vulnReport) + """ </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 generateLicenseDistributionRows(LicenseReport licenseReport) { StringBuilder rows = new StringBuilder(); Map<String, Integer> distribution = licenseReport.getLicenseDistribution(); for (Map.Entry<String, Integer> entry : distribution.entrySet()) { rows.append(String.format("<tr><td>%s</td><td>%d</td></tr>", entry.getKey(), entry.getValue())); } return rows.toString(); } private String generateCriticalComponentsList(VulnerabilityReport vulnReport) { if (vulnReport.getCriticalComponents().isEmpty()) { return "<p>No critical components found.</p>"; } StringBuilder list = new StringBuilder("<ul>"); for (var component : vulnReport.getCriticalComponents()) { list.append(String.format("<li>%s:%s - %d vulnerabilities</li>", component.getComponentName(), component.getComponentVersion(), component.getVulnerabilityCount())); } list.append("</ul>"); return list.toString(); } 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"; }; } } class CombinedReport { private final VulnerabilityReport vulnerabilityReport; private final LicenseReport licenseReport; private final Date generationDate; public CombinedReport(VulnerabilityReport vulnerabilityReport, LicenseReport licenseReport) { this.vulnerabilityReport = vulnerabilityReport; this.licenseReport = licenseReport; this.generationDate = new Date(); } // Getters for JSON serialization public VulnerabilityReport getVulnerabilityReport() { return vulnerabilityReport; } public LicenseReport getLicenseReport() { return licenseReport; } public Date getGenerationDate() { return generationDate; } }

Usage Example

package com.example.blackduck; import com.example.blackduck.license.LicenseComplianceManager; import com.example.blackduck.license.LicenseReport; import com.example.blackduck.projects.ProjectManager; import com.example.blackduck.report.ReportGenerator; import com.example.blackduck.scan.*; import com.synopsys.integration.blackduck.service.dataservice.*; import com.synopsys.integration.exception.IntegrationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; public class BlackDuckExample { private static final Logger logger = LoggerFactory.getLogger(BlackDuckExample.class); public static void main(String[] args) { // Configuration String hubUrl = "https://your-blackduck-hub.com"; String apiToken = "your-api-token"; String projectName = "Example Project"; String projectVersion = "1.0.0"; Path pomFilePath = Paths.get("path/to/pom.xml"); Path reportOutputDir = Paths.get("reports"); BlackDuckClient blackDuckClient = null; try { // Initialize Black Duck client blackDuckClient = new BlackDuckClient(hubUrl, apiToken, 120); if (!blackDuckClient.testConnection()) { logger.error("Failed to connect to Black Duck Hub"); System.exit(1); } // Get services BlackDuckServicesFactory servicesFactory = blackDuckClient.getServicesFactory(); ProjectService projectService = servicesFactory.createProjectService(); ProjectBomService projectBomService = servicesFactory.createProjectBomService(); ComponentService componentService = servicesFactory.createComponentService(); LicenseService licenseService = servicesFactory.createLicenseService(); PolicyService policyService = servicesFactory.createPolicyService(); // Initialize managers ProjectManager projectManager = new ProjectManager(projectService, blackDuckClient.getApiClient()); ComponentScanner componentScanner = new ComponentScanner(componentService, projectBomService, blackDuckClient.getApiClient()); LicenseComplianceManager licenseManager = new LicenseComplianceManager(licenseService, projectBomService, blackDuckClient.getApiClient()); ReportGenerator reportGenerator = new ReportGenerator(); // Scan Maven project MavenProjectScanner mavenScanner = new MavenProjectScanner(projectManager, componentScanner); ScanResult scanResult = mavenScanner.scanMavenProject(pomFilePath, projectName, projectVersion); // Generate license report LicenseReport licenseReport = licenseManager.generateLicenseReport( scanResult.getProjectWrapper()); // Generate reports reportGenerator.generateConsoleReport(scanResult.getVulnerabilityReport(), licenseReport); reportGenerator.generateHtmlReport(scanResult.getVulnerabilityReport(), licenseReport, reportOutputDir); reportGenerator.generateJsonReport(scanResult.getVulnerabilityReport(), licenseReport, reportOutputDir); // Check for critical issues if (scanResult.hasCriticalVulnerabilities()) { logger.warn("CRITICAL: Project contains components with critical vulnerabilities"); System.exit(1); } if (licenseReport.hasRestrictiveLicenses()) { logger.warn("WARNING: Project contains restrictive licenses"); } logger.info("Black Duck analysis completed successfully"); } catch (Exception e) { logger.error("Black Duck analysis failed", e); System.exit(1); } finally { if (blackDuckClient != null) { blackDuckClient.close(); } } } }

GitHub Actions Integration

name: Black Duck Security Scan on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: - cron: '0 0 * * 0' # Weekly on Sunday jobs: blackduck-scan: name: "Black Duck 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: Build project run: mvn -B compile -DskipTests - name: Run Black Duck Scan run: | mvn -B exec:java -Dexec.mainClass="com.example.blackduck.BlackDuckExample" \ -Dexec.args="--pom pom.xml --output reports" env: BLACKDUCK_URL: ${{ secrets.BLACKDUCK_URL }} BLACKDUCK_TOKEN: ${{ secrets.BLACKDUCK_TOKEN }} - name: Upload Security Report uses: actions/upload-artifact@v3 with: name: blackduck-security-report path: reports/ - name: Check for Critical Vulnerabilities run: | # This would check the exit code or report contents # and fail the build if critical vulnerabilities are found if grep -q "CRITICAL" reports/blackduck_report_*.json; then echo "Critical vulnerabilities found - failing build" exit 1 fi

Conclusion

This comprehensive Black Duck Hub implementation for Java provides:

  • Complete integration with Black Duck Hub REST API
  • Automated dependency scanning for Maven projects
  • Vulnerability analysis with severity classification
  • License compliance management with restrictive license detection
  • Policy management for security and compliance enforcement
  • Comprehensive reporting in HTML, JSON, and console formats
  • CI/CD integration with GitHub Actions

Key benefits include:

  1. Automated Open Source Management - Continuous monitoring of dependencies
  2. Security Vulnerability Detection - Early identification of security risks
  3. License Compliance - Avoid legal issues with open-source licenses
  4. Policy Enforcement - Automated compliance checking
  5. Comprehensive Reporting - Actionable insights for development teams

The implementation follows industry best practices and provides a solid foundation for integrating Black Duck Hub into Java development workflows for comprehensive software composition analysis.

Secure Java Supply Chain, Minimal Containers & Runtime Security (Alpine, Distroless, Signing, SBOM & Kubernetes Controls)

https://macronepal.com/blog/alpine-linux-security-in-java-complete-guide/
Explains how Alpine Linux is used as a lightweight base for Java containers to reduce image size and attack surface, while discussing tradeoffs like musl compatibility, CVE handling, and additional hardening requirements for production security.

https://macronepal.com/blog/the-minimalists-approach-building-ultra-secure-java-applications-with-scratch-base-images/
Explains using scratch base images for Java applications to create extremely minimal containers with almost zero attack surface, where only the compiled Java application and runtime dependencies exist.

https://macronepal.com/blog/distroless-containers-in-java-minimal-secure-containers-for-jvm-applications/
Explains distroless Java containers that remove shells, package managers, and unnecessary OS tools, significantly reducing vulnerabilities while improving security posture for JVM workloads.

https://macronepal.com/blog/revolutionizing-container-security-implementing-chainguard-images-for-java-applications/
Explains Chainguard images for Java, which are secure-by-default, CVE-minimized container images with SBOMs and cryptographic signing, designed for modern supply-chain security.

https://macronepal.com/blog/seccomp-filtering-in-java-comprehensive-security-sandboxing/
Explains seccomp syscall filtering in Linux to restrict what system calls Java applications can make, reducing the impact of exploits by limiting kernel-level access.

https://macronepal.com/blog/in-toto-attestations-in-java/
Explains in-toto framework integration in Java to create cryptographically verifiable attestations across the software supply chain, ensuring every build step is trusted and auditable.

https://macronepal.com/blog/fulcio-integration-in-java-code-signing-certificate-infrastructure/
Explains Fulcio integration for Java, which issues short-lived certificates for code signing in a zero-trust supply chain, enabling secure identity-based signing of artifacts.

https://macronepal.com/blog/tekton-supply-chain-in-java-comprehensive-ci-cd-pipeline-implementation/
Explains using Tekton CI/CD pipelines for Java applications to automate secure builds, testing, signing, and deployment with supply-chain security controls built in.

https://macronepal.com/blog/slsa-provenance-in-java-complete-guide-to-supply-chain-security-2/
Explains SLSA (Supply-chain Levels for Software Artifacts) provenance in Java builds, ensuring traceability of how software is built, from source code to final container image.

https://macronepal.com/blog/notary-project-in-java-complete-implementation-guide/
Explains the Notary Project for Java container security, enabling cryptographic signing and verification of container images and artifacts to prevent tampering in deployment pipelines.

Leave a Reply

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


Macro Nepal Helper