Go library for SPDX license expression parsing, normalization, and validation.
Normalizes informal license strings from the real world (like "Apache 2" or "MIT License") to valid SPDX identifiers (like "Apache-2.0" or "MIT"). Useful when working with package metadata from registries where license fields often contain non-standard values.
go get github.com/git-pkgs/spdximport "github.com/git-pkgs/spdx"
// Normalize converts informal strings to valid SPDX identifiers
id, err := spdx.Normalize("Apache 2") // "Apache-2.0"
id, err := spdx.Normalize("MIT License") // "MIT"
id, err := spdx.Normalize("GPL v3") // "GPL-3.0-or-later"
id, err := spdx.Normalize("GNU General Public License") // "GPL-3.0-or-later"
id, err := spdx.Normalize("BSD 3-Clause") // "BSD-3-Clause"
id, err := spdx.Normalize("CC BY 4.0") // "CC-BY-4.0"// Parse handles both strict SPDX IDs and informal license names
expr, err := spdx.Parse("MIT OR Apache-2.0")
fmt.Println(expr.String()) // "MIT OR Apache-2.0"
expr, err := spdx.Parse("Apache 2 OR MIT License")
fmt.Println(expr.String()) // "Apache-2.0 OR MIT"
expr, err := spdx.Parse("GPL v3 AND BSD 3-Clause")
fmt.Println(expr.String()) // "GPL-3.0-or-later AND BSD-3-Clause"
// Handles operator precedence (AND binds tighter than OR)
expr, err := spdx.Parse("MIT OR GPL-2.0-only AND Apache-2.0")
fmt.Println(expr.String()) // "MIT OR (GPL-2.0-only AND Apache-2.0)"
// ParseStrict requires valid SPDX IDs (no fuzzy normalization)
expr, err := spdx.ParseStrict("MIT OR Apache-2.0") // succeeds
expr, err := spdx.ParseStrict("Apache 2 OR MIT") // fails
// ParseSyntax validates expression grammar without requiring identifiers
// to exist in the SPDX list bundled by this module
expr, err := spdx.ParseSyntax("Future-License-1.0 OR MIT") // succeedsParseSyntax is useful when the caller validates identifiers against another
pinned data source. Known identifiers are returned in their canonical form,
while well-formed unknown license and exception identifiers are preserved.
ParseStrict continues to require identifiers from the bundled SPDX list.
Parse an expression once, then replace its identifiers while keeping its operators and precedence:
expr, err := spdx.ParseStrict(
"MIT OR GPL-2.0-only WITH Classpath-exception-2.0",
)
rewritten := spdx.RewriteIdentifiers(expr, func(id string) string {
return strings.ToLower(id)
})
fmt.Println(rewritten)
// "mit OR (gpl-2.0-only WITH classpath-exception-2.0)"The callback receives license identifiers, exception identifiers, and complete
LicenseRef or DocumentRef values in expression order. Replacement values
are not validated as SPDX identifiers.
// Check if a string is valid SPDX
spdx.Valid("MIT OR Apache-2.0") // true
spdx.Valid("FAKEYLICENSE") // false
// Check if a single identifier is valid
spdx.ValidLicense("MIT") // true
spdx.ValidLicense("Apache 2") // false (informal, not valid SPDX)
// Validate multiple licenses at once
valid, invalid := spdx.ValidateLicenses([]string{"MIT", "Apache-2.0", "FAKE"})
// valid: false, invalid: ["FAKE"]// Check if allowed licenses satisfy an expression
satisfied, err := spdx.Satisfies("MIT OR Apache-2.0", []string{"MIT"})
// true
satisfied, err := spdx.Satisfies("MIT AND Apache-2.0", []string{"MIT"})
// false (both required)licenses, err := spdx.ExtractLicenses("(MIT AND GPL-2.0-only) OR Apache-2.0")
// ["Apache-2.0", "GPL-2.0-only", "MIT"]Categories are sourced from scancode-licensedb (OSS licenses only) and updated weekly.
// Get the category for a license
cat := spdx.LicenseCategory("MIT") // spdx.CategoryPermissive
cat := spdx.LicenseCategory("GPL-3.0-only") // spdx.CategoryCopyleft
cat := spdx.LicenseCategory("MPL-2.0") // spdx.CategoryCopyleftLimited
cat := spdx.LicenseCategory("Unlicense") // spdx.CategoryPublicDomain
// Check license type
spdx.IsPermissive("MIT") // true
spdx.IsPermissive("GPL-3.0") // false
spdx.IsCopyleft("GPL-3.0-only") // true
spdx.IsCopyleft("LGPL-2.1") // true (weak copyleft)
// Get categories for an expression
cats, err := spdx.ExpressionCategories("MIT OR GPL-3.0-only")
// []Category{CategoryPermissive, CategoryCopyleft}
// Check expressions for copyleft
spdx.HasCopyleft("MIT OR Apache-2.0") // false
spdx.HasCopyleft("MIT OR GPL-3.0-only") // true
spdx.IsFullyPermissive("MIT OR Apache-2.0") // true
spdx.IsFullyPermissive("MIT OR GPL-3.0") // false
// Get detailed license info
info := spdx.GetLicenseInfo("MIT")
// info.Category: CategoryPermissive
// info.IsException: false
// info.IsDeprecated: falseAvailable categories:
CategoryPermissive- MIT, Apache-2.0, BSD-*CategoryCopyleft- GPL-, AGPL-CategoryCopyleftLimited- LGPL-, MPL-, EPL-*CategoryPublicDomain- Unlicense, CC0-1.0CategoryCommercial- Commercial licensesCategoryProprietaryFree- Free but proprietaryCategorySourceAvailable- Source-available licensesCategoryPatentLicense- Patent grantsCategoryFreeRestricted- Free with restrictionsCategoryCLA- Contributor agreementsCategoryUnstated- No license stated
The library handles many common variations found in package registries:
| Input | Output |
|---|---|
| Apache 2 | Apache-2.0 |
| Apache License 2.0 | Apache-2.0 |
| Apache License, Version 2.0 | Apache-2.0 |
| MIT License | MIT |
| M.I.T. | MIT |
| GPL v3 | GPL-3.0-or-later |
| GNU General Public License v3 | GPL-3.0-or-later |
| LGPL 2.1 | LGPL-2.1-only |
| BSD 3-Clause | BSD-3-Clause |
| 3-Clause BSD | BSD-3-Clause |
| Simplified BSD | BSD-2-Clause |
| MPL 2.0 | MPL-2.0 |
| Mozilla Public License | MPL-2.0 |
| CC BY 4.0 | CC-BY-4.0 |
| Attribution-NonCommercial | CC-BY-NC-4.0 |
| Unlicense | Unlicense |
| WTFPL | WTFPL |
Designed for processing large numbers of licenses:
BenchmarkNormalize-8 49116 24381 ns/op (~5µs per license)
BenchmarkNormalizeBatch-8 372 3271336 ns/op (~3.3µs per license at scale)
BenchmarkParse-8 236752 5263 ns/op (includes normalization)
BenchmarkValid-8 789087 1506 ns/op (strict validation)
This library combines approaches from several existing implementations:
- librariesio/spdx (Ruby) - Expression parsing and case normalization
- jslicense/spdx-correct.js (JavaScript) - Fuzzy matching transforms and test cases
- EmbarkStudios/spdx (Rust) - Performance-oriented design
- github/go-spdx (Go) - SPDX license list and Satisfies implementation
- aboutcode-org/scancode-licensedb - License categories and metadata
MIT