Comparing version 6.2.0 to 7.0.0
146
index.js
@@ -5,4 +5,5 @@ module.exports = licensee | ||
var correctLicenseMetadata = require('correct-license-metadata') | ||
var licenseSatisfies = require('spdx-satisfies') | ||
var npmLicenseCorrections = require('npm-license-corrections') | ||
var osi = require('spdx-osi') | ||
var parse = require('spdx-expression-parse') | ||
var parseJSON = require('json-parse-errback') | ||
@@ -14,3 +15,3 @@ var readPackageTree = require('read-package-tree') | ||
var spawn = require('child_process').spawn | ||
var validSPDX = require('spdx-expression-validate') | ||
var spdxWhitelisted = require('spdx-whitelisted') | ||
@@ -21,11 +22,9 @@ function licensee (configuration, path, callback) { | ||
} | ||
if (configuration.license) { | ||
configuration.rule = configuration.license | ||
configuration.licenses = compileLicenseWhitelist(configuration) | ||
if ( | ||
configuration.licenses.length === 0 && | ||
Object.keys(configuration.packages).length === 0 | ||
) { | ||
callback(new Error('No licenses or packages whitelisted.')) | ||
} else { | ||
configuration.rule = licenseRuleFromBlueOak(configuration.blueOak) | ||
} | ||
if (!validSPDX(configuration.rule)) { | ||
console.log(configuration.rule) | ||
callback(new Error('Invalid license expression')) | ||
} else { | ||
if (configuration.productionOnly) { | ||
@@ -132,28 +131,11 @@ // In order to ignore devDependencies, we need to read: | ||
isObject(configuration) && | ||
XOR( | ||
configuration.license, | ||
configuration.blueOak | ||
), | ||
XOR( | ||
( // Validate `license` property. | ||
configuration.hasOwnProperty('license') && | ||
isString(configuration.license) && | ||
configuration.license.length > 0 | ||
), | ||
( // Validate Blue Oak rating. | ||
configuration.hasOwnProperty('blueOak') && | ||
isString(configuration.blueOak) && | ||
configuration.blueOak.length > 0 && | ||
blueOakList.some(function (element) { | ||
return element.name === configuration.blueOak.toLowerCase() | ||
}) | ||
) | ||
) && | ||
configuration.hasOwnProperty('whitelist') | ||
configuration.hasOwnProperty('licenses') && | ||
isObject(configuration.licenses) && | ||
configuration.hasOwnProperty('packages') | ||
? ( | ||
// Validate `whitelist` property. | ||
isObject(configuration.whitelist) && | ||
Object.keys(configuration.whitelist) | ||
// Validate `packages` property. | ||
isObject(configuration.packages) && | ||
Object.keys(configuration.packages) | ||
.every(function (key) { | ||
return isString(configuration.whitelist[key]) | ||
return isString(configuration.packages[key]) | ||
}) | ||
@@ -164,6 +146,2 @@ ) : true | ||
function XOR (a, b) { | ||
return (a || b) && !(a && b) | ||
} | ||
function isObject (argument) { | ||
@@ -222,4 +200,15 @@ return typeof argument === 'object' | ||
function resultForPackage (configuration, tree) { | ||
var rule = configuration.rule | ||
var whitelist = configuration.whitelist || {} | ||
var licenseWhitelist = (configuration.licenses || []) | ||
.reduce(function (whitelist, element) { | ||
try { | ||
var parsed = parse(element) | ||
if (parsed.hasOwnProperty('conjunction')) { | ||
throw new Error('Cannot match against "' + JSON.stringify(element) + '".') | ||
} | ||
return whitelist.concat(parsed) | ||
} catch (e) { | ||
return whitelist | ||
} | ||
}, []) | ||
var packageWhitelist = configuration.packages || {} | ||
var result = { | ||
@@ -291,30 +280,39 @@ name: tree.package.name, | ||
// Check if whitelisted. | ||
var whitelisted = Object.keys(whitelist).some(function (name) { | ||
return ( | ||
result.name === name && | ||
satisfies(result.version, whitelist[name]) === true | ||
) | ||
}) | ||
if (whitelisted) { | ||
result.approved = false | ||
var packageWhitelisted = Object.keys(packageWhitelist) | ||
.some(function (name) { | ||
return ( | ||
result.name === name && | ||
satisfies(result.version, packageWhitelist[name]) === true | ||
) | ||
}) | ||
if (packageWhitelisted) { | ||
result.approved = true | ||
result.whitelisted = true | ||
result.package = true | ||
return result | ||
} | ||
if (!result.license || typeof result.license !== 'string') { | ||
return result | ||
} | ||
var validSPDX = true | ||
var parsed | ||
try { | ||
parsed = parse(result.license) | ||
} catch (e) { | ||
validSPDX = false | ||
} | ||
// Check against licensing rule. | ||
var matchesRule = ( | ||
rule && | ||
validSPDX(rule) && | ||
result.license && | ||
typeof result.license === 'string' && | ||
validSPDX(result.license) && | ||
licenseSatisfies(result.license, rule) | ||
var licenseWhitelisted = ( | ||
validSPDX && | ||
spdxWhitelisted(parsed, licenseWhitelist) | ||
) | ||
if (matchesRule) { | ||
if (licenseWhitelisted) { | ||
result.approved = true | ||
result.rule = true | ||
} else { | ||
result.approved = false | ||
result.license = true | ||
} | ||
return result | ||
@@ -352,3 +350,3 @@ } | ||
function licenseRuleFromBlueOak (rating) { | ||
function licensesFromBlueOak (rating) { | ||
rating = rating.toLowerCase() | ||
@@ -360,7 +358,29 @@ var ids = [] | ||
element.licenses.forEach(function (license) { | ||
if (validSPDX(license.id)) ids.push(license.id) | ||
try { | ||
parse(license.id) | ||
ids.push(license.id) | ||
} catch (e) { | ||
// pass | ||
} | ||
}) | ||
if (rating === element.name) break | ||
} | ||
return '(' + ids.join(' OR ') + ')' | ||
return ids | ||
} | ||
function compileLicenseWhitelist (configuration) { | ||
var licenses = configuration.licenses | ||
var whitelist = [] | ||
var spdx = licenses.spdx | ||
if (spdx) pushMissing(spdx, whitelist) | ||
var blueOak = licenses.blueOak | ||
if (blueOak) pushMissing(licensesFromBlueOak(blueOak), whitelist) | ||
if (licenses.osi) pushMissing(osi, whitelist) | ||
return whitelist | ||
} | ||
function pushMissing (source, sink) { | ||
source.forEach(function (element) { | ||
if (sink.indexOf(element) === -1) sink.push(element) | ||
}) | ||
} |
{ | ||
"name": "licensee", | ||
"description": "check dependency licenses against rules", | ||
"version": "6.2.0", | ||
"version": "7.0.0", | ||
"author": "Kyle E. Mitchell <kyle@kemitchell.com> (https://kemitchell.com/)", | ||
@@ -20,4 +20,6 @@ "contributors": [ | ||
"simple-concat": "^1.0.0", | ||
"spdx-expression-parse": "^3.0.0", | ||
"spdx-expression-validate": "^2.0.0", | ||
"spdx-satisfies": "^5.0.0" | ||
"spdx-osi": "^3.0.0", | ||
"spdx-whitelisted": "^1.0.0" | ||
}, | ||
@@ -24,0 +26,0 @@ "bin": "./licensee", |
@@ -15,4 +15,11 @@ Check npm package dependency license metadata against rules. | ||
{ | ||
"license": "(MIT OR BSD-2-Clause OR BSD-3-Clause OR Apache-2.0)", | ||
"whitelist": { | ||
"licenses": { | ||
"spdx": [ | ||
"MIT", | ||
"BSD-2-Clause", | ||
"BSD-3-Clause", | ||
"Apache-2.0" | ||
] | ||
}, | ||
"packages": { | ||
"optimist": "<=0.6.1" | ||
@@ -29,14 +36,16 @@ }, | ||
The `license` property is an SPDX license expression that | ||
[spdx-expression-parse][parse] can parse. Any package with [standard | ||
license metadata][metadata] that satisfies the SPDX license expression | ||
according to [spdx-satisfies][satisfies] will not cause an error. | ||
The `licenses` object adds licenses to a whitelist. | ||
Any package with [standard license metadata][metadata] | ||
that satisfies that whitelist according to | ||
[spdx-whitelisted][whitelisted] will not cause an error. | ||
[parse]: https://www.npmjs.com/package/spdx-expression-parse | ||
[satisfies]: https://www.npmjs.com/package/spdx-satisfies | ||
[whitelisted]: https://www.npmjs.com/package/spdx-whitelisted | ||
Instead of a `license` property, you can specify a minimum | ||
Blue Oak Council [license rating]---lead, bronze, silver, or | ||
gold---from which `licensee` will generate a rule: | ||
Instead of whitelisting each license by SPDX identifier, | ||
you can whitelist categories of licenses. | ||
You can specify a minimum Blue Oak Council [license | ||
rating]---lead, bronze, silver, or gold---like so: | ||
[license rating]: https://blueoakcouncil.org/license | ||
@@ -46,18 +55,45 @@ | ||
{ | ||
"blueOak": "bronze" | ||
"licenses": { | ||
"blueOak": "bronze" | ||
} | ||
} | ||
``` | ||
The `whitelist` is a map from package name to a [node-semver][semver] | ||
Semantic Versioning range. Packages whose license metadata don't match | ||
the SPDX license expression in `license` but have a name and version | ||
described in `whitelist` will not cause an error. | ||
You can also whitelist all [OSI]-approved licenses: | ||
[osi]: https://opensource.org | ||
```json | ||
{ | ||
"licenses": { | ||
"osi": true | ||
} | ||
} | ||
``` | ||
All of these can be combined: | ||
```json | ||
{ | ||
"licenses": { | ||
"spdx": ["CC-BY-4.0"], | ||
"blueOak": "gold", | ||
"osi": true | ||
} | ||
} | ||
``` | ||
The `packages` property is a map from package name to a | ||
[node-semver][semver] Semantic Versioning range. Packages whose | ||
license metadata don't match the SPDX license expression in | ||
`licenses` but have a name and version described in `packages` | ||
will not cause an error. | ||
[metadata]: https://docs.npmjs.com/files/package.json#license | ||
[semver]: https://www.npmjs.com/package/semver | ||
The `corrections` flag toggles community corrections to npm package | ||
license metadata. When enabled, `licensee` will check `license` and | ||
`whitelist` against `license` values from [npm-license-corrections] | ||
when available, and also use [correct-license-metadata] to try to | ||
The `corrections` flag toggles community corrections to npm | ||
package license metadata. When enabled, `licensee` will check | ||
against `license` values from [npm-license-corrections] when | ||
available, and also use [correct-license-metadata] to try to | ||
correct old-style `licenses` arrays and other unambiguous, but | ||
@@ -64,0 +100,0 @@ invalid, metadata. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34753
348
212
14
+ Addedspdx-expression-parse@^3.0.0
+ Addedspdx-osi@^3.0.0
+ Addedspdx-whitelisted@^1.0.0
+ Addedspdx-osi@3.0.0(transitive)
+ Addedspdx-whitelisted@1.0.0(transitive)
- Removedspdx-satisfies@^5.0.0
- Removedspdx-satisfies@5.0.1(transitive)