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:
- Dependency Analysis - Complete visibility into all dependencies
- License Compliance - Automated checking against company policies
- Security Scanning - Vulnerability detection and alerting
- Attribution Generation - Automatic documentation for compliance
- Policy Enforcement - Gates to prevent non-compliant dependencies
- API Integration - Programmatic access for custom workflows
- CI/CD Integration - Seamless integration with build pipelines
- 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.