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);
    }
}