License Compliance in Dependencies
License Compliance in Dependencies
Open-source licenses create legal obligations that extend to all software using those dependencies. Copyleft licenses like GPL can require releasing proprietary code. Permissive licenses have fewer restrictions but still require attribution. License incompatibilities between dependencies can create legal risks.
// License compliance automation
const licenseChecker = require('license-checker');
const spdxCorrect = require('spdx-correct');
const satisfies = require('spdx-satisfies');
class LicenseComplianceManager {
constructor(config) {
this.allowedLicenses = config.allowedLicenses;
this.deniedLicenses = config.deniedLicenses;
this.licenseOverrides = config.overrides || {};
}
async checkCompliance(projectPath) {
const licenses = await this.scanLicenses(projectPath);
const results = {
compliant: true,
violations: [],
warnings: [],
summary: {}
};
for (const [package, info] of Object.entries(licenses)) {
const licenseId = this.normalizeLicense(info.licenses);
// Check against denied list
if (this.isDeniedLicense(licenseId)) {
results.compliant = false;
results.violations.push({
package: package,
license: licenseId,
reason: 'License is explicitly denied'
});
}
// Check against allowed list
else if (!this.isAllowedLicense(licenseId)) {
results.compliant = false;
results.violations.push({
package: package,
license: licenseId,
reason: 'License is not in allowed list'
});
}
// Check for license compatibility
const compatibility = this.checkCompatibility(licenseId);
if (compatibility.warnings.length > 0) {
results.warnings.push(...compatibility.warnings);
}
// Track license usage
results.summary[licenseId] = (results.summary[licenseId] || 0) + 1;
}
return results;
}
normalizeLicense(licenseString) {
// Handle SPDX expressions
if (licenseString.includes(' OR ') || licenseString.includes(' AND ')) {
return licenseString;
}
// Correct common misspellings
const corrected = spdxCorrect(licenseString);
if (corrected) {
return corrected;
}
// Check overrides
if (this.licenseOverrides[licenseString]) {
return this.licenseOverrides[licenseString];
}
return licenseString;
}
generateAttribution() {
// Generate attribution file for compliance
const attributions = [];
for (const [package, info] of Object.entries(this.licenses)) {
if (this.requiresAttribution(info.licenses)) {
attributions.push({
name: package,
version: info.version,
license: info.licenses,
copyright: info.copyright || 'Unknown',
url: info.repository || info.url
});
}
}
return this.formatAttributions(attributions);
}
}