License Management
Understanding license obligations and managing compliance risk
Software licenses carry legal obligations organizations must honor. GPL requires source code disclosure for derivative works. AGPL extends this to network services. Some licenses prohibit commercial use. Others restrict fields of use. Violating license terms creates legal liability, forces expensive remediation, and damages reputation. Yet many organizations have limited visibility into what licenses govern their software dependencies—particularly transitive dependencies buried several levels deep in dependency trees.
License violations rarely result from intentional infringement. They happen because organizations don't know what licenses apply to their deployed software. Developer adds npm dependency for convenient JSON parsing. That dependency includes transitive dependency licensed under GPL. Product ships with GPL code despite organizational policy prohibiting strong copyleft licenses. Violation discovered months later during audit or customer due diligence. Expensive remediation ensues—remove component, replace with compatible alternative, regression test, emergency deployment.
SBOMs provide license visibility enabling proactive compliance management. Know what licenses govern your software before shipping, not after legal problems emerge.
The License Visibility Problem
Traditional development practices provide insufficient license awareness at the points where it matters—during component selection and before product release.
Development-time blindness: Developer searches for library solving specific problem. Finds suitable package, checks functionality and performance, integrates into application. Never examines license. Build succeeds, tests pass, feature ships. License obligations are unknown and unmanaged.
Transitive dependency surprise: Application directly depends on 20 libraries, all with permissive licenses (MIT, Apache). But those 20 libraries depend on 180 transitive dependencies. Three layers deep, one obscure dependency uses AGPL. Organization discovers this during customer enterprise procurement when prospect's legal team reviews license obligations. Deal at risk due to unapproved license discovered too late.
Policy enforcement gaps: Organization has policy: "No GPL/AGPL licenses in products sold to customers." But policy exists in document developers don't read. No automated enforcement. Developers unknowingly introduce GPL-licensed components. Policy violations accumulate undetected until triggering event (audit, acquisition due diligence, customer complaint) exposes problems.
Audit timing: License audits happen quarterly or annually after the fact. By discovery time, violating components are deeply integrated into applications deployed to thousands of customers. Remediation requires emergency work, customer notifications, potential service disruptions. Retroactive compliance is painful and expensive.
SBOM-Enabled License Management
SBOMs document component licenses alongside component identities, enabling systematic license visibility and proactive compliance management.
License Inventory and Analysis
Automated license extraction:
# Extract license information from SBOM
def analyze_sbom_licenses(sbom):
"""Analyze license distribution in SBOM"""
license_summary = {
'components_total': len(sbom['components']),
'components_with_license': 0,
'components_without_license': 0,
'license_categories': {
'permissive': [],
'weak_copyleft': [],
'strong_copyleft': [],
'proprietary': [],
'unknown': []
},
'policy_violations': []
}
for component in sbom['components']:
licenses = component.get('licenses', [])
if not licenses:
license_summary['components_without_license'] += 1
license_summary['license_categories']['unknown'].append({
'component': component['name'],
'version': component['version']
})
continue
license_summary['components_with_license'] += 1
for license_obj in licenses:
license_id = license_obj.get('license', {}).get('id', 'UNKNOWN')
category = categorize_license(license_id)
license_summary['license_categories'][category].append({
'component': component['name'],
'version': component['version'],
'license': license_id
})
# Check policy compliance
if violates_policy(license_id, component):
license_summary['policy_violations'].append({
'component': component['name'],
'license': license_id,
'policy': get_violated_policy(license_id),
'severity': 'HIGH'
})
return license_summary
def categorize_license(spdx_id):
"""Categorize licenses by type"""
permissive = ['MIT', 'Apache-2.0', 'BSD-2-Clause', 'BSD-3-Clause', 'ISC']
weak_copyleft = ['LGPL-2.1', 'LGPL-3.0', 'MPL-2.0', 'EPL-1.0']
strong_copyleft = ['GPL-2.0', 'GPL-3.0', 'AGPL-3.0']
if spdx_id in permissive:
return 'permissive'
elif spdx_id in weak_copyleft:
return 'weak_copyleft'
elif spdx_id in strong_copyleft:
return 'strong_copyleft'
elif 'proprietary' in spdx_id.lower():
return 'proprietary'
else:
return 'unknown'Automated analysis transforms license compliance from manual audit to systematic monitoring. Run during builds, pre-deployment gates, or continuous monitoring. Violations detected before customer impact.
Policy-Based Compliance Gates
Organizations define license policies reflecting business model, legal risk tolerance, and customer requirements. SBOM data enables automated policy enforcement.
Example policies:
Products sold to customers:
- Prohibited: GPL-2.0, GPL-3.0, AGPL-3.0 (strong copyleft incompatible with proprietary sales)
- Requires legal review: LGPL-2.1, LGPL-3.0 (weak copyleft requires dynamic linking verification)
- Allowed: MIT, Apache-2.0, BSD-* (permissive licenses compatible with commercial use)
Internal tools:
- Prohibited: None (internal use removes redistribution concerns)
- Requires documentation: All copyleft (source code availability obligations)
- Allowed: All permissive and copyleft licenses
SaaS applications:
- Prohibited: AGPL-3.0 (network copyleft triggers source disclosure for SaaS)
- Requires legal review: GPL-3.0 (interpretation unclear for SaaS)
- Allowed: MIT, Apache-2.0, LGPL-* (no SaaS-specific obligations)
Build-time policy enforcement:
# CI/CD pipeline with license gate
name: Build and License Check
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build application
run: npm run build
- name: Generate SBOM
run: cyclonedx-npm --output-file sbom.json
- name: License policy check
run: |
python scripts/check-license-policy.py sbom.json \
--policy policies/commercial-product.yaml \
--fail-on-violation
# Build fails if license policy violations detected
# Prevents policy violations from reaching productionPolicy gates prevent violations rather than discovering them retroactively. Developer receives immediate feedback: "PR blocked: Component X uses GPL-3.0 which violates commercial product policy." Fix before merge, not after customer discovery.
License Obligation Tracking
Different licenses impose different obligations. SBOM license data enables tracking what obligations apply to deployed software.
License obligations matrix:
| License | Attribution Required | Source Disclosure | Patent Grant | Network Copyleft | Commercial Use |
|---|---|---|---|---|---|
| MIT | Yes | No | No | No | Yes |
| Apache-2.0 | Yes | No | Yes | No | Yes |
| GPL-3.0 | Yes | Yes (distribution) | Yes | No | Yes |
| AGPL-3.0 | Yes | Yes (network use) | Yes | Yes | Yes |
| LGPL-3.0 | Yes | Partial (modifications) | Yes | No | Yes |
Compliance checklist generation:
def generate_compliance_checklist(sbom):
"""Generate license compliance actions from SBOM"""
obligations = {
'attribution_required': [],
'source_disclosure_required': [],
'patent_license_notice': [],
'modification_tracking': []
}
for component in sbom['components']:
for license in component.get('licenses', []):
license_id = license.get('license', {}).get('id')
if requires_attribution(license_id):
obligations['attribution_required'].append({
'component': component['name'],
'license': license_id,
'action': 'Include in product attribution file'
})
if requires_source_disclosure(license_id):
obligations['source_disclosure_required'].append({
'component': component['name'],
'license': license_id,
'action': 'Provide source code or written offer',
'urgency': 'HIGH'
})
if provides_patent_grant(license_id):
obligations['patent_license_notice'].append({
'component': component['name'],
'license': license_id,
'action': 'Include patent grant notice in documentation'
})
return obligationsChecklist ensures compliance obligations are tracked and fulfilled before product release.
Practical Implementation
Phase 1: License Data Collection
Ensure SBOM generation includes comprehensive license information. Many SBOM tools can automatically detect licenses from package metadata, LICENSE files, or SPDX identifiers in source code.
Validation checks:
- What percentage of components have license information? Target: 95%+
- Are licenses expressed as SPDX identifiers (standardized) or free text (requires manual interpretation)?
- Are dual/multi-licensed components correctly represented (e.g., "MIT OR Apache-2.0")?
- Are custom/proprietary licenses properly documented?
Improving license detection: If automated tools miss licenses, enhance through:
- Manual license file scanning in repositories
- Package manager metadata enrichment
- Vendor inquiries for proprietary components
- Legal team review for ambiguous cases
Phase 2: Policy Definition
Define clear, enforceable license policies aligned with business context.
Policy development process:
- Legal team defines risk tolerance and license categories (prohibited, requires review, allowed)
- Engineering leadership maps policies to product categories (commercial, internal, SaaS)
- Document policies in machine-readable format (YAML, JSON) enabling automated enforcement
- Communicate policies to development teams with explanations of "why"
- Establish approval workflows for exceptions
Example policy file:
# commercial-product-policy.yaml
policy_name: Commercial Product License Policy
applies_to:
- product_type: commercial
- distribution: customer-deployed
prohibited_licenses:
- GPL-2.0-only
- GPL-2.0-or-later
- GPL-3.0-only
- GPL-3.0-or-later
- AGPL-3.0-only
- AGPL-3.0-or-later
- SSPL-1.0 # Server Side Public License
reasoning: "Strong copyleft incompatible with proprietary distribution model"
requires_legal_review:
- LGPL-2.1
- LGPL-3.0
- MPL-2.0
reasoning: "Weak copyleft requires verification of dynamic linking and modification disclosure"
allowed_licenses:
- MIT
- Apache-2.0
- BSD-2-Clause
- BSD-3-Clause
- ISC
- 0BSD
reasoning: "Permissive licenses compatible with commercial use"
exception_process: "File JIRA ticket in LEGAL project with business justification"Phase 3: Automated Enforcement
Integrate license policy checking into development workflows at multiple checkpoints.
Pre-commit hooks: Check licenses before code commits. Earliest possible enforcement. Prevents introducing violations into repository.
Pull request gates: CI/CD runs license checks on PRs. Reviews cannot approve if violations exist. Enforces policy before merge to main branch.
Build gates: Production builds fail if license policy violations detected. Prevents deployment of non-compliant software.
Periodic audits: Weekly or monthly automated audits of deployed software. Ensures ongoing compliance even for deployed systems. Detects violations that slipped through earlier gates.
Phase 4: Compliance Artifact Generation
Some licenses require specific compliance artifacts (attribution files, source code offers, license notices).
Automated attribution file generation:
def generate_attribution_file(sbom, output_path):
"""Generate NOTICE file from SBOM licenses"""
with open(output_path, 'w') as notice_file:
notice_file.write("Third-Party Software Attributions\n")
notice_file.write("=" * 50 + "\n\n")
for component in sorted(sbom['components'], key=lambda c: c['name']):
notice_file.write(f"{component['name']} {component['version']}\n")
licenses = component.get('licenses', [])
if licenses:
license_ids = [l.get('license', {}).get('id', 'Unknown')
for l in licenses]
notice_file.write(f"License: {', '.join(license_ids)}\n")
if 'supplier' in component:
notice_file.write(f"Supplier: {component['supplier']['name']}\n")
notice_file.write("\n")
print(f"Attribution file generated: {output_path}")Automated generation ensures compliance artifacts are current and complete without manual maintenance burden.
Advanced License Scenarios
Dual and Multi-Licensed Components
Some components offer choice of licenses: "Licensed under MIT OR Apache-2.0" or "GPL-3.0-or-later with Classpath exception."
SBOM representation:
CycloneDX and SPDX support license expressions: (MIT OR Apache-2.0), GPL-3.0-only WITH Classpath-exception-2.0
Policy handling:
- OR expressions: Component complies if ANY license in expression satisfies policy. "MIT OR GPL-3.0" satisfies "permissive only" policy by selecting MIT.
- AND expressions: Component must satisfy ALL licenses. "MIT AND BSD-3-Clause" requires compliance with both.
- WITH expressions: License with exception. Evaluate whether exception addresses policy concern.
Selection tracking: Document which license from multi-license offering you're relying on. If choosing MIT from "MIT OR GPL-3.0", record that selection so future audits don't flag GPL concern.
License Changes and Deprecation
Projects sometimes change licenses between versions. Redis changed from BSD to SSPL (more restrictive). Elasticsearch changed from Apache to dual SSPL/Elastic. Component you use might change licenses in future versions.
SBOM lifecycle tracking: SBOMs document licenses at specific component versions. Redis 6.2 (BSD) is different license than Redis 7.2 (SSPL). Version-specific license tracking prevents surprises during upgrades.
Upgrade evaluation: Before upgrading dependencies, check if licenses changed. "We're upgrading Redis 6.2 to 7.2. License changed from BSD to SSPL. Does SSPL comply with our policies? If not, evaluate alternatives."
License Compatibility
Some licenses are incompatible with each other. Cannot combine GPL-2.0 code with GPL-3.0 code in same binary without satisfying both (which is legally complex). Apache-2.0 and GPL-2.0 have patent clause conflicts.
Compatibility analysis: Advanced license management tools can analyze SBOM for compatibility issues: "Application links GPL-3.0 library with Apache-2.0 code. This combination is acceptable. Application also links GPL-2.0 library, creating GPL-2.0/GPL-3.0 combination requiring legal review."
Remediation: When incompatibilities detected, options include:
- Remove one incompatible component
- Use component isolation (microservices, dynamic linking) to avoid combining incompatible licenses
- Seek legal counsel on whether specific combination is actually problematic
- Request license exceptions from copyright holders
Unknown and Unlicensed Components
Some components lack explicit licenses. GitHub repositories with code but no LICENSE file. Internal components where license was never defined. Proprietary components with custom terms.
Risk handling:
- Unknown licenses: Treat as high-risk until clarified. Contact upstream maintainers requesting license clarification. If no response, consider alternative components.
- Unlicensed code: Technically all rights reserved to copyright holder. Cannot legally use without explicit permission. Extremely high risk.
- Custom licenses: Require legal review. Cannot be automatically categorized or policy-checked.
SBOM representation: SPDX defines "NOASSERTION" for unknown licenses and "NONE" for explicitly unlicensed code. Flag these in compliance reports for manual review.
Integration with Vendor Management
License compliance extends beyond internally-developed software to vendor-provided components.
Vendor SBOM license review: When vendors provide SBOMs, analyze for license compliance before procurement. Vendor claims "enterprise-ready" but SBOM shows GPL dependencies creating copyleft obligations incompatible with your use case. Discovered before contract signature enables negotiation or alternative selection.
Contractual license warranties: Include in vendor contracts: "Vendor warrants no prohibited licenses (GPL, AGPL) in provided software. SBOM will document all component licenses. License policy violations constitute breach enabling termination and damages."
Ongoing monitoring: Vendor updates their software, potentially changing component licenses. Monitor updated vendor SBOMs for new license violations: "Vendor update introduced AGPL dependency. This violates our contract terms and internal policies. Contact vendor for remediation."
Measuring License Compliance
Track metrics demonstrating license management effectiveness:
License coverage: Percentage of components with documented licenses in SBOMs. Target: 98%+. Gaps indicate incomplete SBOM generation or components requiring manual investigation.
Policy violation rate: Number of policy violations per build or release. Target: Zero in production, minimized in development. Trend should decrease as enforcement matures and developers learn policies.
Unknown license reduction: Count of components with "NOASSERTION" or "NONE" licenses. Target: Zero for critical systems. Demonstrates investment in license clarification.
Audit findings: When periodic legal audits occur, count of compliance issues discovered. Lower findings over time indicates improving proactive management.
Time to remediation: When violations discovered, days until remediation (component replacement, license clarification, exception approval). Target: Under 30 days. Faster remediation reduces exposure window.
Common License Management Pitfalls
Pitfall: Assuming license compatibility without verification "MIT is permissive, so MIT-licensed component is always safe." Misses corner cases like MIT-licensed component linking to GPL library, creating GPL obligations.
Prevention: Analyze full dependency tree, not just direct dependencies. Consider transitive license implications.
Pitfall: Treating all copyleft as equally risky LGPL, GPL, and AGPL are lumped together as "copyleft." But LGPL dynamic linking exception makes it far less restrictive than GPL for many use cases.
Prevention: Understand license nuances. LGPL may be acceptable where GPL is not.
Pitfall: No exception process Strict "no GPL" policy with no exceptions. Developer finds perfect library solving critical problem, but it's GPL. No pathway to get exception approved. Developer either violates policy (secret GPL) or wastes time rebuilding library functionality.
Prevention: Define exception approval process. Most policies need flexibility for rare justified cases.
Pitfall: Ignoring license notice preservation "MIT license allows anything." Misses MIT's requirement to preserve copyright and license notices. Ship product without attribution, violate even permissive license.
Prevention: Generate compliance artifacts (NOTICE files) preserving attributions as required.
Next Steps
- Build license visibility through Producer Workflows - Generate SBOMs
- Enforce policies via Producer Workflows - Validate and Sign
- Review vendor licenses through Consumer Workflows - Validate Quality
- Track obligations using Reference - Checklists