google-site-verification: google61fe8ba583a51912.html
FOSSA Integration in Java

Introduction to FOSSA

FOSSA is a dependency scanning and license compliance tool that helps organizations manage open source dependencies, detect vulnerabilities, and ensure license compliance across Java projects.

FOSSA Configuration

.fossa.yml Configuration

# FOSSA configuration file version: 3 # Project metadata project: name: my-java-app url: https://github.com/org/my-java-app team: java-development-team jiraProjectKey: JAVA releaseGroup: production # Build configuration build: # Maven configuration maven: goal: compile options: - -DskipTests - -q output: target/dependency # Custom dependency locations additionalDependencies: - path: "custom-libs/*.jar" - path: "lib/**/*.jar" # Gradle configuration gradle: task: dependencies output: build/dependencies config: ["--configuration", "runtimeClasspath"] # Analysis targets targets: only: - type: maven - type: gradle - type: sbt - type: docker # Dependency analysis analyze: # Include all dependencies allDependencies: true # Custom modules for multi-module projects modules: - name: "web-module" path: "web" type: maven target: "." - name: "service-module" path: "service" type: maven target: "." - name: "data-module" path: "data" type: maven target: "." # License compliance policies policy: # Default license policy default: allowed: - "Apache-2.0" - "MIT" - "BSD-3-Clause" - "BSD-2-Clause" - "ISC" - "EPL-2.0" - "CDDL-1.1" banned: - "AGPL-3.0" - "GPL-3.0" - "LGPL-3.0" allowedCopyleft: - "LGPL-2.1" - "MPL-2.0" reviewNeeded: - "Apache-1.1" - "CPL-1.0" # Custom policies for specific dependencies custom: - dependency: "com.example:proprietary-lib" policy: allowed: ["Proprietary"] notes: "Internally developed library" - dependency: "org.ow2.asm:asm" policy: allowed: ["BSD-3-Clause"] notes: "Required for bytecode manipulation" # Vulnerability scanning vulnerabilities: # Severity thresholds failOn: - critical - high warnOn: - medium ignore: - low # Ignore specific CVEs with justification exceptions: - cve: "CVE-2023-34462" reason: "False positive - not exploitable in our usage" expires: "2024-12-31" - cve: "CVE-2022-42889" reason: "Mitigated by configuration" expires: "2024-06-30" # Attribution requirements attribution: required: true template: | This project includes the following open source components: {{#dependencies}} - {{name}} ({{version}}) - {{license}} {{#url}}Project: {{.}}{{/url}} {{#copyright}}Copyright: {{.}}{{/copyright}} {{/dependencies}} # Export configuration export: formats: - spdx - cyclonedx output: directory: ./fossa-reports filename: fossa-report-${timestamp} # Notifications notifications: slack: channel: "#java-security" on: - policy_violation - new_vulnerability - scan_complete email: recipients: - [email protected] - [email protected] on: - policy_violation - critical_vulnerability

Maven Integration

Maven Plugin Configuration

<!-- pom.xml FOSSA configuration --> <project> <properties> <fossa.version>3.0.0</fossa.version> <fossa.api.key>${env.FOSSA_API_KEY}</fossa.api.key> </properties> <build> <plugins> <!-- FOSSA Maven Plugin --> <plugin> <groupId>com.fossa</groupId> <artifactId>fossa-maven-plugin</artifactId> <version>${fossa.version}</version> <configuration> <apiKey>${fossa.api.key}</apiKey> <project>my-java-app</project> <server>https://app.fossa.io</server> <branch>${git.branch}</branch> <revision>${git.commit.id}</revision> <includeAll>true</includeAll> <includeSubmodules>true</includeSubmodules> <outputDirectory>${project.build.directory}/fossa</outputDirectory> <failOnViolation>true</failOnViolation> <failOnVulnerability>true</failOnVulnerability> <policySeverity>high</policySeverity> </configuration> <executions> <execution> <id>fossa-analysis</id> <phase>verify</phase> <goals> <goal>analyze</goal> </goals> </execution> <execution> <id>fossa-test</id> <phase>test</phase> <goals> <goal>test</goal> </goals> </execution> </executions> </plugin> <!-- Maven Dependency Plugin for enhanced analysis --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.6.1</version> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/dependency</outputDirectory> <includeScope>runtime</includeScope> </configuration> </execution> <execution> <id>analyze-dependencies</id> <phase>compile</phase> <goals> <goal>analyze</goal> </goals> </execution> </executions> </plugin> <!-- Build Helper Plugin for additional sources --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.5.0</version> <executions> <execution> <id>add-fossa-source</id> <phase>generate-sources</phase> <goals> <goal>add-source</goal> </goals> <configuration> <sources> <source>src/fossa/java</source> </sources> </configuration> </execution> </executions> </plugin> </plugins> </build> <profiles> <!-- FOSSA Security Scan Profile --> <profile> <id>fossa-security</id> <build> <plugins> <plugin> <groupId>com.fossa</groupId> <artifactId>fossa-maven-plugin</artifactId> <executions> <execution> <id>fossa-security-scan</id> <phase>verify</phase> <goals> <goal>security-scan</goal> </goals> <configuration> <failOnSeverity>high</failOnSeverity> <generateReport>true</generateReport> <reportFormats>html,json</reportFormats> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles> </project>

Multi-module Maven Configuration

<!-- parent-pom.xml --> <project> <modules> <module>web-app</module> <module>service-api</module> <module>data-access</module> <module>common-utils</module> </modules> <build> <pluginManagement> <plugins> <plugin> <groupId>com.fossa</groupId> <artifactId>fossa-maven-plugin</artifactId> <version>${fossa.version}</version> <configuration> <apiKey>${fossa.api.key}</apiKey> <project>${project.artifactId}</project> <server>https://app.fossa.io</server> <branch>${git.branch}</branch> <revision>${git.commit.id}</revision> </configuration> </plugin> </plugins> </pluginManagement> </build> <profiles> <profile> <id>fossa-aggregate</id> <build> <plugins> <plugin> <groupId>com.fossa</groupId> <artifactId>fossa-maven-plugin</artifactId> <executions> <execution> <id>aggregate-analysis</id> <phase>verify</phase> <goals> <goal>aggregate</goal> </goals> <configuration> <includeModules>true</includeModules> <outputFile>${project.build.directory}/fossa-aggregate-report.json</outputFile> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles> </project>

Gradle Integration

build.gradle FOSSA Configuration

// build.gradle FOSSA configuration plugins { id 'java' id 'com.fossa' version '3.0.0' } // FOSSA configuration fossa { apiKey = System.getenv('FOSSA_API_KEY') project = 'my-java-app' server = 'https://app.fossa.io' branch = System.getenv('GIT_BRANCH') ?: 'main' revision = System.getenv('GIT_COMMIT') ?: 'unknown' // Analysis configuration includeAllDependencies = true includeSubprojects = true failOnPolicyViolation = true failOnVulnerability = true // Output configuration outputDir = file("${buildDir}/fossa") reportFormats = ['html', 'json', 'spdx'] // Custom dependency configurations dependencyConfigs { runtime { configurations = ['runtimeClasspath'] } test { configurations = ['testRuntimeClasspath'] } } } // Custom task for FOSSA analysis task fossaDependencyAnalysis(type: com.fossa.gradle.FossaAnalysisTask) { description = 'Analyze dependencies with FOSSA' group = 'Verification' dependsOn 'dependencies' doFirst { logger.lifecycle('Starting FOSSA dependency analysis...') } doLast { logger.lifecycle('FOSSA analysis completed. Check reports in: {}', fossa.outputDir) } } // Task to generate FOSSA attribution report task generateAttributionReport(type: com.fossa.gradle.FossaAttributionTask) { description = 'Generate open source attribution report' group = 'Reporting' template = file('fossa/attribution-template.txt') outputFile = file("${buildDir}/reports/attribution.txt") doLast { logger.lifecycle('Attribution report generated: {}', outputFile) } } // Security scan task task fossaSecurityScan(type: com.fossa.gradle.FossaSecurityTask) { description = 'Run FOSSA security vulnerability scan' group = 'Verification' failOnSeverity = 'high' generateReport = true reportFormats = ['html', 'json'] doLast { logger.lifecycle('Security scan completed. Reports available in: {}', "${buildDir}/reports/fossa/security") } } // Integrate with build check.dependsOn fossaDependencyAnalysis build.dependsOn generateAttributionReport // Multi-project configuration subprojects { apply plugin: 'com.fossa' fossa { project = "${rootProject.name}:${project.name}" includeAllDependencies = true } afterEvaluate { tasks.named('fossaDependencyAnalysis') { mustRunAfter tasks.named('test') } } }

FOSSA API Integration

Java Client for FOSSA API

/** * FOSSA API Client for programmatic integration. */ @Component @Slf4j public class FossaApiClient { private final RestTemplate restTemplate; private final String apiKey; private final String baseUrl; @Value("${fossa.api.key}") private String fossaApiKey; @Value("${fossa.api.url:https://app.fossa.io}") private String fossaApiUrl; public FossaApiClient(RestTemplateBuilder restTemplateBuilder) { this.apiKey = fossaApiKey; this.baseUrl = fossaApiUrl; this.restTemplate = restTemplateBuilder .defaultHeader("Authorization", "Bearer " + apiKey) .defaultHeader("Content-Type", "application/json") .build(); } /** * Submits a project for analysis. */ public AnalysisResult analyzeProject(String projectId, File sourceDirectory) { try { // Create analysis request AnalysisRequest request = new AnalysisRequest(projectId, sourceDirectory.getAbsolutePath()); // Submit to FOSSA ResponseEntity<AnalysisResponse> response = restTemplate.postForEntity( baseUrl + "/api/projects/analyze", request, AnalysisResponse.class ); if (response.getStatusCode().is2xxSuccessful()) { return pollAnalysisResult(response.getBody().getAnalysisId()); } else { throw new FossaException("Failed to submit analysis: " + response.getStatusCode()); } } catch (Exception e) { log.error("Failed to analyze project: {}", projectId, e); throw new FossaException("Analysis failed for project: " + projectId, e); } } /** * Polls for analysis completion. */ private AnalysisResult pollAnalysisResult(String analysisId) { int maxAttempts = 30; int attempt = 0; while (attempt < maxAttempts) { try { ResponseEntity<AnalysisStatus> statusResponse = restTemplate.getForEntity( baseUrl + "/api/analyses/" + analysisId + "/status", AnalysisStatus.class ); AnalysisStatus status = statusResponse.getBody(); if (status.isCompleted()) { return getAnalysisResult(analysisId); } else if (status.isFailed()) { throw new FossaException("Analysis failed: " + status.getError()); } // Wait before next poll Thread.sleep(5000); attempt++; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new FossaException("Analysis polling interrupted", e); } catch (Exception e) { log.warn("Failed to poll analysis status, attempt {}/{}", attempt, maxAttempts, e); attempt++; } } throw new FossaException("Analysis timed out after " + maxAttempts + " attempts"); } /** * Retrieves analysis results. */ public AnalysisResult getAnalysisResult(String analysisId) { try { ResponseEntity<AnalysisResult> response = restTemplate.getForEntity( baseUrl + "/api/analyses/" + analysisId, AnalysisResult.class ); return response.getBody(); } catch (Exception e) { log.error("Failed to get analysis result: {}", analysisId, e); throw new FossaException("Failed to retrieve analysis result", e); } } /** * Gets project dependencies. */ public List<Dependency> getProjectDependencies(String projectId) { try { ResponseEntity<ProjectDependencies> response = restTemplate.getForEntity( baseUrl + "/api/projects/" + projectId + "/dependencies", ProjectDependencies.class ); return response.getBody().getDependencies(); } catch (Exception e) { log.error("Failed to get project dependencies: {}", projectId, e); throw new FossaException("Failed to retrieve project dependencies", e); } } /** * Checks for policy violations. */ public PolicyReport checkPolicyCompliance(String projectId) { try { ResponseEntity<PolicyReport> response = restTemplate.getForEntity( baseUrl + "/api/projects/" + projectId + "/policy", PolicyReport.class ); return response.getBody(); } catch (Exception e) { log.error("Failed to check policy compliance: {}", projectId, e); throw new FossaException("Failed to check policy compliance", e); } } /** * Gets security vulnerabilities. */ public VulnerabilityReport getVulnerabilities(String projectId) { try { ResponseEntity<VulnerabilityReport> response = restTemplate.getForEntity( baseUrl + "/api/projects/" + projectId + "/vulnerabilities", VulnerabilityReport.class ); return response.getBody(); } catch (Exception e) { log.error("Failed to get vulnerabilities: {}", projectId, e); throw new FossaException("Failed to retrieve vulnerabilities", e); } } /** * Creates a FOSSA project. */ public Project createProject(Project project) { try { ResponseEntity<Project> response = restTemplate.postForEntity( baseUrl + "/api/projects", project, Project.class ); return response.getBody(); } catch (Exception e) { log.error("Failed to create project: {}", project.getName(), e); throw new FossaException("Failed to create project", e); } } } /** * FOSSA API data models. */ public class AnalysisRequest { private String projectId; private String sourceDirectory; private Map<String, String> options; public AnalysisRequest(String projectId, String sourceDirectory) { this.projectId = projectId; this.sourceDirectory = sourceDirectory; this.options = new HashMap<>(); this.options.put("includeAllDependencies", "true"); } // Getters and setters public String getProjectId() { return projectId; } public void setProjectId(String projectId) { this.projectId = projectId; } public String getSourceDirectory() { return sourceDirectory; } public void setSourceDirectory(String sourceDirectory) { this.sourceDirectory = sourceDirectory; } public Map<String, String> getOptions() { return options; } public void setOptions(Map<String, String> options) { this.options = options; } } public class AnalysisResponse { private String analysisId; private String status; private String message; // Getters and setters public String getAnalysisId() { return analysisId; } public void setAnalysisId(String analysisId) { this.analysisId = analysisId; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } public class AnalysisStatus { private String status; private String error; private int progress; public boolean isCompleted() { return "COMPLETED".equals(status); } public boolean isFailed() { return "FAILED".equals(status); } // Getters and setters public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getError() { return error; } public void setError(String error) { this.error = error; } public int getProgress() { return progress; } public void setProgress(int progress) { this.progress = progress; } } public class AnalysisResult { private String analysisId; private String projectId; private List<Dependency> dependencies; private List<Issue> issues; private List<LicenseFinding> licenseFindings; private LocalDateTime analyzedAt; // Getters and setters public String getAnalysisId() { return analysisId; } public void setAnalysisId(String analysisId) { this.analysisId = analysisId; } public String getProjectId() { return projectId; } public void setProjectId(String projectId) { this.projectId = projectId; } public List<Dependency> getDependencies() { return dependencies; } public void setDependencies(List<Dependency> dependencies) { this.dependencies = dependencies; } public List<Issue> getIssues() { return issues; } public void setIssues(List<Issue> issues) { this.issues = issues; } public List<LicenseFinding> getLicenseFindings() { return licenseFindings; } public void setLicenseFindings(List<LicenseFinding> licenseFindings) { this.licenseFindings = licenseFindings; } public LocalDateTime getAnalyzedAt() { return analyzedAt; } public void setAnalyzedAt(LocalDateTime analyzedAt) { this.analyzedAt = analyzedAt; } } public class Dependency { private String name; private String version; private String type; private List<License> licenses; private List<Vulnerability> vulnerabilities; // 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 getType() { return type; } public void setType(String type) { this.type = type; } public List<License> getLicenses() { return licenses; } public void setLicenses(List<License> licenses) { this.licenses = licenses; } public List<Vulnerability> getVulnerabilities() { return vulnerabilities; } public void setVulnerabilities(List<Vulnerability> vulnerabilities) { this.vulnerabilities = vulnerabilities; } } public class FossaException extends RuntimeException { public FossaException(String message) { super(message); } public FossaException(String message, Throwable cause) { super(message, cause); } }

License Compliance Service

Automated License Compliance Checker

/** * Service for managing license compliance using FOSSA. */ @Service @Slf4j public class LicenseComplianceService { private final FossaApiClient fossaClient; private final ProjectRepository projectRepository; private final NotificationService notificationService; public LicenseComplianceService(FossaApiClient fossaClient, ProjectRepository projectRepository, NotificationService notificationService) { this.fossaClient = fossaClient; this.projectRepository = projectRepository; this.notificationService = notificationService; } /** * Checks license compliance for all projects. */ @Scheduled(cron = "0 0 6 * * MON") // Weekly on Monday at 6 AM public void checkAllProjectsCompliance() { log.info("Starting license compliance check for all projects"); List<Project> projects = projectRepository.findAll(); ComplianceReport overallReport = new ComplianceReport(); for (Project project : projects) { try { ComplianceReport projectReport = checkProjectCompliance(project); overallReport.merge(projectReport); } catch (Exception e) { log.error("Failed to check compliance for project: {}", project.getName(), e); overallReport.addError(project.getName(), e.getMessage()); } } // Send compliance report sendComplianceReport(overallReport); log.info("License compliance check completed. Projects: {}, Violations: {}", projects.size(), overallReport.getViolationCount()); } /** * Checks license compliance for a single project. */ public ComplianceReport checkProjectCompliance(Project project) { ComplianceReport report = new ComplianceReport(project.getName()); try { // Get project dependencies from FOSSA List<Dependency> dependencies = fossaClient.getProjectDependencies(project.getFossaId()); // Check each dependency for compliance for (Dependency dependency : dependencies) { DependencyCompliance dependencyCompliance = checkDependencyCompliance(dependency); report.addDependencyCompliance(dependencyCompliance); } // Check for policy violations PolicyReport policyReport = fossaClient.checkPolicyCompliance(project.getFossaId()); report.setPolicyReport(policyReport); // Update project status updateProjectComplianceStatus(project, report); } catch (Exception e) { log.error("Failed to check compliance for project: {}", project.getName(), e); report.addError("FOSSA Analysis", e.getMessage()); } return report; } /** * Checks compliance for a single dependency. */ private DependencyCompliance checkDependencyCompliance(Dependency dependency) { DependencyCompliance compliance = new DependencyCompliance(dependency); // Check licenses for (License license : dependency.getLicenses()) { LicenseCompliance licenseCompliance = checkLicenseCompliance(license); compliance.addLicenseCompliance(licenseCompliance); } // Check vulnerabilities for (Vulnerability vulnerability : dependency.getVulnerabilities()) { if (isCriticalVulnerability(vulnerability)) { compliance.addCriticalVulnerability(vulnerability); } } return compliance; } /** * Checks if a license is compliant with company policy. */ private LicenseCompliance checkLicenseCompliance(License license) { LicenseCompliance compliance = new LicenseCompliance(license); // Check against allowed licenses if (isAllowedLicense(license)) { compliance.setStatus(ComplianceStatus.ALLOWED); } else if (isBannedLicense(license)) { compliance.setStatus(ComplianceStatus.BANNED); compliance.setActionRequired(true); } else if (isReviewRequired(license)) { compliance.setStatus(ComplianceStatus.REVIEW_REQUIRED); compliance.setActionRequired(true); } else { compliance.setStatus(ComplianceStatus.UNKNOWN); compliance.setActionRequired(true); } return compliance; } /** * Generates attribution documentation. */ public String generateAttributionDocumentation(String projectId) { try { List<Dependency> dependencies = fossaClient.getProjectDependencies(projectId); StringBuilder attribution = new StringBuilder(); attribution.append("# Open Source Attribution\n\n"); attribution.append("This project includes the following open source components:\n\n"); for (Dependency dependency : dependencies) { attribution.append(generateDependencyAttribution(dependency)); } return attribution.toString(); } catch (Exception e) { log.error("Failed to generate attribution documentation for project: {}", projectId, e); throw new ComplianceException("Failed to generate attribution documentation", e); } } /** * Generates attribution for a single dependency. */ private String generateDependencyAttribution(Dependency dependency) { return String.format( "## %s %s\n\n" + "**License:** %s\n\n" + "**Dependencies:**\n%s\n\n", dependency.getName(), dependency.getVersion(), formatLicenses(dependency.getLicenses()), formatVulnerabilities(dependency.getVulnerabilities()) ); } private boolean isAllowedLicense(License license) { Set<String> allowedLicenses = Set.of( "Apache-2.0", "MIT", "BSD-3-Clause", "BSD-2-Clause", "ISC", "EPL-2.0", "CDDL-1.1" ); return allowedLicenses.contains(license.getId()); } private boolean isBannedLicense(License license) { Set<String> bannedLicenses = Set.of("AGPL-3.0", "GPL-3.0", "LGPL-3.0"); return bannedLicenses.contains(license.getId()); } private boolean isReviewRequired(License license) { Set<String> reviewLicenses = Set.of("Apache-1.1", "CPL-1.0", "LGPL-2.1"); return reviewLicenses.contains(license.getId()); } private boolean isCriticalVulnerability(Vulnerability vulnerability) { return "CRITICAL".equals(vulnerability.getSeverity()) || "HIGH".equals(vulnerability.getSeverity()); } private void updateProjectComplianceStatus(Project project, ComplianceReport report) { project.setLastComplianceCheck(LocalDateTime.now()); project.setCompliant(report.isCompliant()); project.setViolationCount(report.getViolationCount()); projectRepository.save(project); } private void sendComplianceReport(ComplianceReport report) { if (report.hasViolations()) { notificationService.sendComplianceAlert(report); } // Send weekly summary regardless notificationService.sendWeeklyComplianceSummary(report); } private String formatLicenses(List<License> licenses) { return licenses.stream() .map(License::getId) .collect(Collectors.joining(", ")); } private String formatVulnerabilities(List<Vulnerability> vulnerabilities) { if (vulnerabilities.isEmpty()) { return "No known vulnerabilities"; } return vulnerabilities.stream() .map(v -> String.format("- %s (%s)", v.getId(), v.getSeverity())) .collect(Collectors.joining("\n")); } } /** * Compliance data models. */ public class ComplianceReport { private String projectName; private LocalDateTime generatedAt; private List<DependencyCompliance> dependencies; private PolicyReport policyReport; private List<String> errors; private boolean compliant; public ComplianceReport() { this.dependencies = new ArrayList<>(); this.errors = new ArrayList<>(); this.generatedAt = LocalDateTime.now(); } public ComplianceReport(String projectName) { this(); this.projectName = projectName; } public void addDependencyCompliance(DependencyCompliance compliance) { dependencies.add(compliance); if (!compliance.isCompliant()) { compliant = false; } } public void addError(String context, String error) { errors.add(context + ": " + error); compliant = false; } public void merge(ComplianceReport other) { this.dependencies.addAll(other.dependencies); this.errors.addAll(other.errors); this.compliant = this.compliant && other.compliant; } public boolean hasViolations() { return dependencies.stream().anyMatch(d -> !d.isCompliant()) || !errors.isEmpty(); } public int getViolationCount() { return (int) dependencies.stream().filter(d -> !d.isCompliant()).count() + errors.size(); } // Getters and setters public String getProjectName() { return projectName; } public void setProjectName(String projectName) { this.projectName = projectName; } public LocalDateTime getGeneratedAt() { return generatedAt; } public void setGeneratedAt(LocalDateTime generatedAt) { this.generatedAt = generatedAt; } public List<DependencyCompliance> getDependencies() { return dependencies; } public void setDependencies(List<DependencyCompliance> dependencies) { this.dependencies = dependencies; } public PolicyReport getPolicyReport() { return policyReport; } public void setPolicyReport(PolicyReport policyReport) { this.policyReport = policyReport; } public List<String> getErrors() { return errors; } public void setErrors(List<String> errors) { this.errors = errors; } public boolean isCompliant() { return compliant; } public void setCompliant(boolean compliant) { this.compliant = compliant; } } public class DependencyCompliance { private Dependency dependency; private List<LicenseCompliance> licenseCompliances; private List<Vulnerability> criticalVulnerabilities; private boolean compliant; public DependencyCompliance(Dependency dependency) { this.dependency = dependency; this.licenseCompliances = new ArrayList<>(); this.criticalVulnerabilities = new ArrayList<>(); this.compliant = true; } public void addLicenseCompliance(LicenseCompliance compliance) { licenseCompliances.add(compliance); if (!compliance.isCompliant()) { compliant = false; } } public void addCriticalVulnerability(Vulnerability vulnerability) { criticalVulnerabilities.add(vulnerability); compliant = false; } public boolean isCompliant() { return compliant && criticalVulnerabilities.isEmpty(); } // Getters and setters public Dependency getDependency() { return dependency; } public void setDependency(Dependency dependency) { this.dependency = dependency; } public List<LicenseCompliance> getLicenseCompliances() { return licenseCompliances; } public void setLicenseCompliances(List<LicenseCompliance> licenseCompliances) { this.licenseCompliances = licenseCompliances; } public List<Vulnerability> getCriticalVulnerabilities() { return criticalVulnerabilities; } public void setCriticalVulnerabilities(List<Vulnerability> criticalVulnerabilities) { this.criticalVulnerabilities = criticalVulnerabilities; } } public class LicenseCompliance { private License license; private ComplianceStatus status; private boolean actionRequired; private String notes; public LicenseCompliance(License license) { this.license = license; } public boolean isCompliant() { return status == ComplianceStatus.ALLOWED; } // Getters and setters public License getLicense() { return license; } public void setLicense(License license) { this.license = license; } public ComplianceStatus getStatus() { return status; } public void setStatus(ComplianceStatus status) { this.status = status; } public boolean isActionRequired() { return actionRequired; } public void setActionRequired(boolean actionRequired) { this.actionRequired = actionRequired; } public String getNotes() { return notes; } public void setNotes(String notes) { this.notes = notes; } } public enum ComplianceStatus { ALLOWED, BANNED, REVIEW_REQUIRED, UNKNOWN } public class ComplianceException extends RuntimeException { public ComplianceException(String message) { super(message); } public ComplianceException(String message, Throwable cause) { super(message, cause); } }

GitHub Actions Integration

FOSSA Analysis Workflow

# .github/workflows/fossa-analysis.yml name: FOSSA Dependency Analysis on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: - cron: '0 6 * * 1' # Weekly on Monday at 6 AM jobs: fossa-analysis: name: FOSSA Dependency Analysis runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: 'maven' - name: FOSSA Analysis uses: fossa-contrib/fossa-action@v2 with: api-key: ${{ secrets.FOSSA_API_KEY }} project: my-java-app branch: ${{ github.ref_name }} revision: ${{ github.sha }} - name: Run FOSSA Test run: | npx @fossa/check env: FOSSA_API_KEY: ${{ secrets.FOSSA_API_KEY }} - name: Upload FOSSA Reports uses: actions/upload-artifact@v4 with: name: fossa-reports path: | fossa-report.json fossa-licenses.html retention-days: 30 security-scan: name: FOSSA Security Scan runs-on: ubuntu-latest needs: fossa-analysis steps: - name: Checkout code uses: actions/checkout@v4 - name: FOSSA Security Scan run: | npx @fossa/security-scan env: FOSSA_API_KEY: ${{ secrets.FOSSA_API_KEY }} - name: Check for Critical Vulnerabilities id: security-check run: | # Parse FOSSA security report if grep -q "CRITICAL" fossa-security-report.json; then echo "has_critical_vulnerabilities=true" >> $GITHUB_OUTPUT else echo "has_critical_vulnerabilities=false" >> $GITHUB_OUTPUT fi - name: Create Security Issue if: steps.security-check.outputs.has_critical_vulnerabilities == 'true' uses: actions/github-script@v7 with: script: | github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: '🚨 Critical Security Vulnerabilities Detected', body: 'FOSSA security scan detected critical vulnerabilities. Please review the FOSSA report.', labels: ['security', 'critical'] })

Compliance Gate Workflow

# .github/workflows/compliance-gate.yml name: Compliance Gate on: pull_request: branches: [ main ] jobs: compliance-check: name: License Compliance Check runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: FOSSA Compliance Check run: | npx @fossa/check --policy env: FOSSA_API_KEY: ${{ secrets.FOSSA_API_KEY }} - name: Check Compliance Status id: compliance run: | if [ $? -eq 0 ]; then echo "compliant=true" >> $GITHUB_OUTPUT else echo "compliant=false" >> $GITHUB_OUTPUT fi - name: Comment on PR if: steps.compliance.outputs.compliant == 'false' uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: '❌ License compliance check failed. This PR introduces dependencies with non-compliant licenses. Please review the FOSSA report.' })

Spring Boot Configuration

FOSSA Auto-Configuration

/** * Spring Boot Auto-Configuration for FOSSA. */ @Configuration @EnableConfigurationProperties(FossaProperties.class) @ConditionalOnProperty(name = "fossa.enabled", havingValue = "true") public class FossaAutoConfiguration { @Bean @ConditionalOnMissingBean public FossaApiClient fossaApiClient(RestTemplateBuilder restTemplateBuilder, FossaProperties properties) { return new FossaApiClient(restTemplateBuilder, properties); } @Bean public LicenseComplianceService licenseComplianceService(FossaApiClient fossaClient, ProjectRepository projectRepository, NotificationService notificationService) { return new LicenseComplianceService(fossaClient, projectRepository, notificationService); } @Bean public FossaHealthIndicator fossaHealthIndicator(FossaApiClient fossaClient) { return new FossaHealthIndicator(fossaClient); } } /** * FOSSA configuration properties. */ @ConfigurationProperties(prefix = "fossa") public class FossaProperties { private boolean enabled = true; private String apiKey; private String apiUrl = "https://app.fossa.io"; private String defaultProject; private Compliance compliance = new Compliance(); private Scan scan = new Scan(); public static class Compliance { private boolean failOnViolation = true; private List<String> allowedLicenses = Arrays.asList( "Apache-2.0", "MIT", "BSD-3-Clause", "BSD-2-Clause", "ISC", "EPL-2.0", "CDDL-1.1" ); private List<String> bannedLicenses = Arrays.asList( "AGPL-3.0", "GPL-3.0", "LGPL-3.0" ); // Getters and setters public boolean isFailOnViolation() { return failOnViolation; } public void setFailOnViolation(boolean failOnViolation) { this.failOnViolation = failOnViolation; } public List<String> getAllowedLicenses() { return allowedLicenses; } public void setAllowedLicenses(List<String> allowedLicenses) { this.allowedLicenses = allowedLicenses; } public List<String> getBannedLicenses() { return bannedLicenses; } public void setBannedLicenses(List<String> bannedLicenses) { this.bannedLicenses = bannedLicenses; } } public static class Scan { private boolean includeAllDependencies = true; private boolean includeSubmodules = true; private String outputDirectory = "target/fossa"; private List<String> reportFormats = Arrays.asList("html", "json"); // Getters and setters public boolean isIncludeAllDependencies() { return includeAllDependencies; } public void setIncludeAllDependencies(boolean includeAllDependencies) { this.includeAllDependencies = includeAllDependencies; } public boolean isIncludeSubmodules() { return includeSubmodules; } public void setIncludeSubmodules(boolean includeSubmodules) { this.includeSubmodules = includeSubmodules; } public String getOutputDirectory() { return outputDirectory; } public void setOutputDirectory(String outputDirectory) { this.outputDirectory = outputDirectory; } public List<String> getReportFormats() { return reportFormats; } public void setReportFormats(List<String> reportFormats) { this.reportFormats = reportFormats; } } // Getters and setters public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getApiKey() { return apiKey; } public void setApiKey(String apiKey) { this.apiKey = apiKey; } public String getApiUrl() { return apiUrl; } public void setApiUrl(String apiUrl) { this.apiUrl = apiUrl; } public String getDefaultProject() { return defaultProject; } public void setDefaultProject(String defaultProject) { this.defaultProject = defaultProject; } public Compliance getCompliance() { return compliance; } public void setCompliance(Compliance compliance) { this.compliance = compliance; } public Scan getScan() { return scan; } public void setScan(Scan scan) { this.scan = scan; } } /** * Health indicator for FOSSA integration. */ @Component public class FossaHealthIndicator implements HealthIndicator { private final FossaApiClient fossaClient; public FossaHealthIndicator(FossaApiClient fossaClient) { this.fossaClient = fossaClient; } @Override public Health health() { try { // Try to get a simple response from FOSSA fossaClient.getProjectDependencies("test"); return Health.up() .withDetail("service", "fossa") .withDetail("status", "connected") .build(); } catch (Exception e) { return Health.down() .withDetail("service", "fossa") .withDetail("status", "disconnected") .withDetail("error", e.getMessage()) .build(); } } }

Best Practices Summary

/** * FOSSA INTEGRATION BEST PRACTICES FOR JAVA * * 1. Configure .fossa.yml with appropriate license policies * 2. Integrate FOSSA analysis in CI/CD pipeline * 3. Use automated compliance checks in pull requests * 4. Set up regular security vulnerability scanning * 5. Generate attribution documentation automatically * 6. Monitor for policy violations proactively * 7. Use FOSSA API for programmatic integration * 8. Configure appropriate notifications for critical issues * 9. Maintain suppression rules for false positives * 10. Regular review and update of license policies */ /** * Utility class with FOSSA best practices. */ public final class FossaBestPractices { private FossaBestPractices() { // Utility class } /** * Validates FOSSA configuration. */ public static List<String> validateConfiguration(File fossaConfig) { List<String> issues = new ArrayList<>(); try { // Basic validation if (!fossaConfig.exists()) { issues.add("Missing .fossa.yml configuration file"); return issues; } // TODO: Add specific validation rules // - Check for required fields // - Validate license policies // - Check vulnerability thresholds } catch (Exception e) { issues.add("Failed to validate FOSSA configuration: " + e.getMessage()); } return issues; } /** * Generates a compliance report template. */ public static String generateComplianceReportTemplate() { return """ # Open Source Compliance Report Generated: ${timestamp} Project: ${project.name} ## Summary - Total Dependencies: ${dependency.count} - Compliant Dependencies: ${compliant.count} - Non-Compliant Dependencies: ${nonCompliant.count} - Critical Vulnerabilities: ${critical.vulnerabilities} ## Non-Compliant Dependencies ${nonCompliant.dependencies} ## Action Items ${action.items} """; } /** * Creates FOSSA integration checklist. */ public static List<String> getIntegrationChecklist() { return Arrays.asList( "Configure .fossa.yml with license policies", "Set up FOSSA API key in CI environment", "Integrate FOSSA analysis in build process", "Configure compliance gates in PR workflow", "Set up security vulnerability alerts", "Create attribution documentation process", "Configure notifications for policy violations", "Establish regular compliance review schedule", "Train team on open source compliance", "Monitor FOSSA reports and dashboards" ); } }

Conclusion

FOSSA integration for Java provides comprehensive open source management:

  1. Dependency Analysis - Complete visibility into all dependencies
  2. License Compliance - Automated checking against company policies
  3. Security Scanning - Vulnerability detection and alerting
  4. Attribution Generation - Automatic documentation for compliance
  5. Policy Enforcement - Gates to prevent non-compliant dependencies
  6. API Integration - Programmatic access for custom workflows
  7. CI/CD Integration - Seamless integration with build pipelines
  8. Reporting - Comprehensive reports for audits and reviews

By implementing FOSSA with proper configuration, automation, and integration, Java teams can effectively manage open source dependencies while ensuring compliance and security across their projects.

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