Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77a34f96cc | ||
|
|
f095b5a541 | ||
|
|
f54a1f3b74 | ||
|
|
84921e5e4a | ||
|
|
c5af7ff272 | ||
|
|
31279d265a | ||
|
|
2532504548 | ||
|
|
cc6d251652 | ||
|
|
516e8497ac | ||
|
|
43c5083e6c | ||
|
|
fa62a0febc | ||
|
|
e897e8ebdd | ||
|
|
216fafaed5 | ||
|
|
0144419c8e | ||
|
|
7b16bd0b54 | ||
|
|
4525a8c091 | ||
|
|
72273c9a36 | ||
|
|
562a2f3c0a | ||
|
|
c82c183029 | ||
|
|
26be1f407e | ||
|
|
d6e28cdfae | ||
|
|
da3d8af3e3 |
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "Dependency Review Action",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/typescript-node:18",
|
||||||
|
"postCreateCommand": "npm install",
|
||||||
|
"remoteUser": "node",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/ruby:1": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,10 +23,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set Node.js 16.x
|
- name: Set Node.js 18.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version: 18.x
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
cache: npm
|
cache: npm
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci --ignore-scripts
|
run: npm ci --ignore-scripts
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
cache: npm
|
cache: npm
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci --ignore-scripts
|
run: npm ci --ignore-scripts
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ _Note_: We don't have any useful tests yet, contributions are welcome!
|
|||||||
|
|
||||||
## Local Development
|
## Local Development
|
||||||
|
|
||||||
|
It is recommended to have atleast [Node 18](https://nodejs.org/en/) installed.
|
||||||
We have a script to scan a given PR for vulnerabilities, this will
|
We have a script to scan a given PR for vulnerabilities, this will
|
||||||
help you test your local changes. Make sure to [grab a Personal Access Token (PAT)](https://github.com/settings/tokens) before proceeding (you'll need `repo` permissions for private repos):
|
help you test your local changes. Make sure to [grab a Personal Access Token (PAT)](https://github.com/settings/tokens) before proceeding (you'll need `repo` permissions for private repos):
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ or by inlining these options in your workflow file.
|
|||||||
|
|
||||||
### config-file
|
### config-file
|
||||||
|
|
||||||
A string representing the path to an external configuraton file. By
|
A string representing the path to an external configuration file. By
|
||||||
default external configuration files are not used.
|
default external configuration files are not used.
|
||||||
|
|
||||||
**Possible values**: A string representing the absolute path to the
|
**Possible values**: A string representing the absolute path to the
|
||||||
@@ -108,7 +108,7 @@ fail-on-scopes:
|
|||||||
|
|
||||||
Only allow the licenses that comply with the expressions in this list. See "[Licenses](https://github.com/actions/dependency-review-action#licenses)".
|
Only allow the licenses that comply with the expressions in this list. See "[Licenses](https://github.com/actions/dependency-review-action#licenses)".
|
||||||
|
|
||||||
**Possible values**: Any valid [spdx license expression](https://spdx.dev/spdx-specification-21-web-version/#h.jxpfx0ykyb60).
|
**Possible values**: A list of of [SPDX-compliant license identifiers](https://spdx.org/licenses/).
|
||||||
|
|
||||||
**Inline example**: `allow-licenses: BSD-3-Clause, LGPL-2.1 OR MIT OR BSD-3-Clause`
|
**Inline example**: `allow-licenses: BSD-3-Clause, LGPL-2.1 OR MIT OR BSD-3-Clause`
|
||||||
|
|
||||||
@@ -117,7 +117,9 @@ Only allow the licenses that comply with the expressions in this list. See "[Lic
|
|||||||
```yaml
|
```yaml
|
||||||
allow-licenses:
|
allow-licenses:
|
||||||
- BSD-3-Clause
|
- BSD-3-Clause
|
||||||
- LGPL-2.1 OR MIT OR BSD-3-Clause
|
- LGPL-2.1
|
||||||
|
- MIT
|
||||||
|
- BSD-3-Clause
|
||||||
```
|
```
|
||||||
|
|
||||||
### deny-licenses
|
### deny-licenses
|
||||||
@@ -125,7 +127,7 @@ allow-licenses:
|
|||||||
Add a custom list of licenses you want to block. See
|
Add a custom list of licenses you want to block. See
|
||||||
"[Licenses](https://github.com/actions/dependency-review-action#licenses)".
|
"[Licenses](https://github.com/actions/dependency-review-action#licenses)".
|
||||||
|
|
||||||
**Possible values**: Any valid [sodx license expression](https://spdx.dev/spdx-specification-21-web-version/#h.jxpfx0ykyb60).
|
**Possible values**: Any valid set of [SPDX licenses](https://spdx.org/licenses/).
|
||||||
|
|
||||||
**Inline example**: `deny-licenses: LGPL-2.0, GPL-2.0+ WITH Bison-exception-2.2`
|
**Inline example**: `deny-licenses: LGPL-2.0, GPL-2.0+ WITH Bison-exception-2.2`
|
||||||
|
|
||||||
@@ -153,6 +155,20 @@ allow-ghsas:
|
|||||||
- GHSA-efgh-1234-5679
|
- GHSA-efgh-1234-5679
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### license-check/vulnerability-check
|
||||||
|
|
||||||
|
Disable the license checks or vulnerability checks performed by this Action.
|
||||||
|
You can't disable both checks.
|
||||||
|
|
||||||
|
**Possible values**: `true` or `false`
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
license-check: true
|
||||||
|
vulnerability-check: false
|
||||||
|
```
|
||||||
|
|
||||||
### base-ref/head-ref
|
### base-ref/head-ref
|
||||||
|
|
||||||
Provide custom git references for the git base/head when performing
|
Provide custom git references for the git base/head when performing
|
||||||
@@ -257,7 +273,7 @@ forbid a subset of licenses. These options are not supported on Enterprise Serve
|
|||||||
|
|
||||||
You can use the [Licenses
|
You can use the [Licenses
|
||||||
API](https://docs.github.com/en/rest/licenses) to see the full list of
|
API](https://docs.github.com/en/rest/licenses) to see the full list of
|
||||||
supported licenses. Use [spdx license expressions](https://spdx.dev/spdx-specification-21-web-version/#h.jxpfx0ykyb60)
|
supported licenses. Use [SPDX licenses](https://spdx.org/licenses/)
|
||||||
to filter the licenses. A couple of examples:
|
to filter the licenses. A couple of examples:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ function clearInputs() {
|
|||||||
'ALLOW-LICENSES',
|
'ALLOW-LICENSES',
|
||||||
'DENY-LICENSES',
|
'DENY-LICENSES',
|
||||||
'ALLOW-GHSAS',
|
'ALLOW-GHSAS',
|
||||||
|
'LICENSE-CHECK',
|
||||||
|
'VULNERABILITY-CHECK',
|
||||||
'CONFIG-FILE',
|
'CONFIG-FILE',
|
||||||
'BASE-REF',
|
'BASE-REF',
|
||||||
'HEAD-REF'
|
'HEAD-REF'
|
||||||
@@ -181,6 +183,46 @@ test('it successfully parses GHSA allowlist', async () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('it defaults to checking licenses', async () => {
|
||||||
|
const options = readConfig()
|
||||||
|
expect(options.license_check).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('it parses the license-check input', async () => {
|
||||||
|
setInput('license-check', 'false')
|
||||||
|
let options = readConfig()
|
||||||
|
expect(options.license_check).toEqual(false)
|
||||||
|
|
||||||
|
clearInputs()
|
||||||
|
setInput('license-check', 'true')
|
||||||
|
options = readConfig()
|
||||||
|
expect(options.license_check).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('it defaults to checking vulnerabilities', async () => {
|
||||||
|
const options = readConfig()
|
||||||
|
expect(options.vulnerability_check).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('it parses the vulnerability-check input', async () => {
|
||||||
|
setInput('vulnerability-check', 'false')
|
||||||
|
let options = readConfig()
|
||||||
|
expect(options.vulnerability_check).toEqual(false)
|
||||||
|
|
||||||
|
clearInputs()
|
||||||
|
setInput('vulnerability-check', 'true')
|
||||||
|
options = readConfig()
|
||||||
|
expect(options.vulnerability_check).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('it is not possible to disable both checks', async () => {
|
||||||
|
setInput('license-check', 'false')
|
||||||
|
setInput('vulnerability-check', 'false')
|
||||||
|
expect(() => {
|
||||||
|
readConfig()
|
||||||
|
}).toThrow("Can't disable both license-check and vulnerability-check")
|
||||||
|
})
|
||||||
|
|
||||||
describe('licenses that are not valid SPDX licenses', () => {
|
describe('licenses that are not valid SPDX licenses', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
jest.spyOn(Utils, 'isSPDXValid').mockReturnValue(false)
|
jest.spyOn(Utils, 'isSPDXValid').mockReturnValue(false)
|
||||||
|
|||||||
+69
-39
@@ -368,12 +368,16 @@ function run() {
|
|||||||
allow: config.allow_licenses,
|
allow: config.allow_licenses,
|
||||||
deny: config.deny_licenses
|
deny: config.deny_licenses
|
||||||
});
|
});
|
||||||
summary.addSummaryToSummary(addedChanges, invalidLicenseChanges);
|
summary.addSummaryToSummary(config.vulnerability_check ? addedChanges : null, config.license_check ? invalidLicenseChanges : null);
|
||||||
|
if (config.vulnerability_check) {
|
||||||
summary.addChangeVulnerabilitiesToSummary(addedChanges, minSeverity);
|
summary.addChangeVulnerabilitiesToSummary(addedChanges, minSeverity);
|
||||||
summary.addLicensesToSummary(invalidLicenseChanges, config);
|
|
||||||
summary.addScannedDependencies(changes);
|
|
||||||
printVulnerabilitiesBlock(addedChanges, minSeverity);
|
printVulnerabilitiesBlock(addedChanges, minSeverity);
|
||||||
|
}
|
||||||
|
if (config.license_check) {
|
||||||
|
summary.addLicensesToSummary(invalidLicenseChanges, config);
|
||||||
printLicensesBlock(invalidLicenseChanges);
|
printLicensesBlock(invalidLicenseChanges);
|
||||||
|
}
|
||||||
|
summary.addScannedDependencies(changes);
|
||||||
printScannedDependencies(changes);
|
printScannedDependencies(changes);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
@@ -423,12 +427,12 @@ function printChangeVulnerabilities(change) {
|
|||||||
function printLicensesBlock(invalidLicenseChanges) {
|
function printLicensesBlock(invalidLicenseChanges) {
|
||||||
core.group('Licenses', () => __awaiter(this, void 0, void 0, function* () {
|
core.group('Licenses', () => __awaiter(this, void 0, void 0, function* () {
|
||||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
if (invalidLicenseChanges.forbidden.length > 0) {
|
||||||
core.info('\nThe following dependencies have incompatible licenses:\n');
|
core.info('\nThe following dependencies have incompatible licenses:');
|
||||||
printLicensesError(invalidLicenseChanges.forbidden);
|
printLicensesError(invalidLicenseChanges.forbidden);
|
||||||
core.setFailed('Dependency review detected incompatible licenses.');
|
core.setFailed('Dependency review detected incompatible licenses.');
|
||||||
}
|
}
|
||||||
if (invalidLicenseChanges.unresolved.length > 0) {
|
if (invalidLicenseChanges.unresolved.length > 0) {
|
||||||
core.warning('\nThe validity of the licenses of the dependecies below could not be determine. Ensure that they are valid spdx licenses:\n');
|
core.warning('\nThe validity of the licenses of the dependencies below could not be determined. Ensure that they are valid SPDX licenses:');
|
||||||
printLicensesError(invalidLicenseChanges.unresolved);
|
printLicensesError(invalidLicenseChanges.unresolved);
|
||||||
core.setFailed('Dependency review could not detect the validity of all licenses.');
|
core.setFailed('Dependency review could not detect the validity of all licenses.');
|
||||||
}
|
}
|
||||||
@@ -444,7 +448,7 @@ function printNullLicenses(changes) {
|
|||||||
if (changes.length === 0) {
|
if (changes.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
core.info('\nWe could not detect a license for the following dependencies:\n');
|
core.info('\nWe could not detect a license for the following dependencies:');
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
core.info(`${ansi_styles_1.default.bold.open}${change.manifest} » ${change.name}@${change.version}${ansi_styles_1.default.bold.close}`);
|
core.info(`${ansi_styles_1.default.bold.open}${change.manifest} » ${change.name}@${change.version}${ansi_styles_1.default.bold.close}`);
|
||||||
}
|
}
|
||||||
@@ -556,6 +560,8 @@ exports.ConfigurationOptionsSchema = z
|
|||||||
allow_licenses: z.array(z.string()).default([]),
|
allow_licenses: z.array(z.string()).default([]),
|
||||||
deny_licenses: z.array(z.string()).default([]),
|
deny_licenses: z.array(z.string()).default([]),
|
||||||
allow_ghsas: z.array(z.string()).default([]),
|
allow_ghsas: z.array(z.string()).default([]),
|
||||||
|
license_check: z.boolean().default(true),
|
||||||
|
vulnerability_check: z.boolean().default(true),
|
||||||
config_file: z.string().optional().default('false'),
|
config_file: z.string().optional().default('false'),
|
||||||
base_ref: z.string(),
|
base_ref: z.string(),
|
||||||
head_ref: z.string()
|
head_ref: z.string()
|
||||||
@@ -600,11 +606,21 @@ exports.addScannedDependencies = exports.addLicensesToSummary = exports.addChang
|
|||||||
const core = __importStar(__nccwpck_require__(2186));
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
const utils_1 = __nccwpck_require__(918);
|
const utils_1 = __nccwpck_require__(918);
|
||||||
function addSummaryToSummary(addedPackages, invalidLicenseChanges) {
|
function addSummaryToSummary(addedPackages, invalidLicenseChanges) {
|
||||||
core.summary.addHeading('Dependency Review').addRaw(`We found:
|
core.summary
|
||||||
- ${addedPackages.length} vulnerable package(s),
|
.addHeading('Dependency Review')
|
||||||
- ${invalidLicenseChanges.unresolved.length} package(s) with unprocessable licenses,
|
.addRaw('We found:')
|
||||||
- ${invalidLicenseChanges.forbidden.length} package(s) with incompatible licenses, and
|
.addList([
|
||||||
- ${invalidLicenseChanges.unlicensed.length} package(s) with unknown licenses.`);
|
...(addedPackages
|
||||||
|
? [`${addedPackages.length} vulnerable package(s)`]
|
||||||
|
: []),
|
||||||
|
...(invalidLicenseChanges
|
||||||
|
? [
|
||||||
|
`${invalidLicenseChanges.unresolved.length} package(s) with invalid SPDX license definitions`,
|
||||||
|
`${invalidLicenseChanges.forbidden.length} package(s) with incompatible licenses`,
|
||||||
|
`${invalidLicenseChanges.unlicensed.length} package(s) with unknown licenses.`
|
||||||
|
]
|
||||||
|
: [])
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
exports.addSummaryToSummary = addSummaryToSummary;
|
exports.addSummaryToSummary = addSummaryToSummary;
|
||||||
function addChangeVulnerabilitiesToSummary(addedPackages, severity) {
|
function addChangeVulnerabilitiesToSummary(addedPackages, severity) {
|
||||||
@@ -663,60 +679,55 @@ function addLicensesToSummary(invalidLicenseChanges, config) {
|
|||||||
if (config.deny_licenses && config.deny_licenses.length > 0) {
|
if (config.deny_licenses && config.deny_licenses.length > 0) {
|
||||||
core.summary.addQuote(`<strong>Denied Licenses</strong>: ${config.deny_licenses.join(', ')}`);
|
core.summary.addQuote(`<strong>Denied Licenses</strong>: ${config.deny_licenses.join(', ')}`);
|
||||||
}
|
}
|
||||||
if (invalidLicenseChanges.forbidden.length === 0 &&
|
if (Object.values(invalidLicenseChanges).every(item => item.length === 0)) {
|
||||||
invalidLicenseChanges.unlicensed.length === 0) {
|
|
||||||
core.summary.addQuote('No license violations detected.');
|
core.summary.addQuote('No license violations detected.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
core.debug(`found ${invalidLicenseChanges.unlicensed.length} unknown licenses`);
|
||||||
|
core.debug(`${invalidLicenseChanges.unresolved.length} licenses could not be validated`);
|
||||||
|
printLicenseViolation('Incompatible Licenses', invalidLicenseChanges.forbidden);
|
||||||
|
printLicenseViolation('Unknown Licenses', invalidLicenseChanges.unlicensed);
|
||||||
|
printLicenseViolation('Invalid SPDX License Definitions', invalidLicenseChanges.unresolved);
|
||||||
|
}
|
||||||
|
exports.addLicensesToSummary = addLicensesToSummary;
|
||||||
|
function printLicenseViolation(heading, changes) {
|
||||||
|
core.summary.addHeading(heading, 5).addSeparator();
|
||||||
|
if (changes.length > 0) {
|
||||||
const rows = [];
|
const rows = [];
|
||||||
const manifests = (0, utils_1.getManifestsSet)(invalidLicenseChanges.forbidden);
|
const manifests = (0, utils_1.getManifestsSet)(changes);
|
||||||
core.summary.addHeading('Incompatible Licenses', 3).addSeparator();
|
|
||||||
for (const manifest of manifests) {
|
for (const manifest of manifests) {
|
||||||
core.summary.addHeading(`<em>${manifest}</em>`, 4);
|
core.summary.addHeading(`<em>${manifest}</em>`, 4);
|
||||||
for (const change of invalidLicenseChanges.forbidden.filter(pkg => pkg.manifest === manifest)) {
|
for (const change of changes.filter(pkg => pkg.manifest === manifest)) {
|
||||||
rows.push([
|
rows.push([
|
||||||
(0, utils_1.renderUrl)(change.source_repository_url, change.name),
|
(0, utils_1.renderUrl)(change.source_repository_url, change.name),
|
||||||
change.version,
|
change.version,
|
||||||
change.license || ''
|
formatLicense(change.license)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
core.summary.addTable([['Package', 'Version', 'License'], ...rows]);
|
core.summary.addTable([['Package', 'Version', 'License'], ...rows]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.summary.addQuote('No license violations detected.');
|
core.summary.addQuote(`No ${heading.toLowerCase()} detected.`);
|
||||||
}
|
|
||||||
core.debug(`found ${invalidLicenseChanges.unlicensed.length} unknown licenses`);
|
|
||||||
if (invalidLicenseChanges.unlicensed.length > 0) {
|
|
||||||
const rows = [];
|
|
||||||
const manifests = (0, utils_1.getManifestsSet)(invalidLicenseChanges.unlicensed);
|
|
||||||
core.debug(`found ${manifests.entries.length} manifests for unknown licenses`);
|
|
||||||
core.summary.addHeading('Unknown Licenses', 3).addSeparator();
|
|
||||||
for (const manifest of manifests) {
|
|
||||||
core.summary.addHeading(`<em>${manifest}</em>`, 4);
|
|
||||||
for (const change of invalidLicenseChanges.unlicensed.filter(pkg => pkg.manifest === manifest)) {
|
|
||||||
rows.push([
|
|
||||||
(0, utils_1.renderUrl)(change.source_repository_url, change.name),
|
|
||||||
change.version
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
core.summary.addTable([['Package', 'Version'], ...rows]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.addLicensesToSummary = addLicensesToSummary;
|
function formatLicense(license) {
|
||||||
|
if (license === null || license === 'NOASSERTION') {
|
||||||
|
return 'Null';
|
||||||
|
}
|
||||||
|
return license;
|
||||||
|
}
|
||||||
function addScannedDependencies(changes) {
|
function addScannedDependencies(changes) {
|
||||||
const dependencies = (0, utils_1.groupDependenciesByManifest)(changes);
|
const dependencies = (0, utils_1.groupDependenciesByManifest)(changes);
|
||||||
const manifests = dependencies.keys();
|
const manifests = dependencies.keys();
|
||||||
const summary = core.summary
|
const summary = core.summary
|
||||||
.addHeading('Scanned Dependencies')
|
.addHeading('Scanned Dependencies')
|
||||||
.addRaw(`We scanned ${dependencies.size} manifest files:`);
|
.addHeading(`We scanned ${dependencies.size} manifest files:`, 5);
|
||||||
for (const manifest of manifests) {
|
for (const manifest of manifests) {
|
||||||
const deps = dependencies.get(manifest);
|
const deps = dependencies.get(manifest);
|
||||||
if (deps) {
|
if (deps) {
|
||||||
const dependencyNames = deps.map(dependency => `<li>${dependency.name}@${dependency.version}</li>`);
|
const dependencyNames = deps.map(dependency => `<li>${dependency.name}@${dependency.version}</li>`);
|
||||||
summary.addRaw(`<h3>${manifest}</h3><ul>${dependencyNames.join('')}</ul>`);
|
summary.addDetails(manifest, `<ul>${dependencyNames.join('')}</ul>`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27398,6 +27409,10 @@ const core = __importStar(__nccwpck_require__(2186));
|
|||||||
const z = __importStar(__nccwpck_require__(3301));
|
const z = __importStar(__nccwpck_require__(3301));
|
||||||
const schemas_1 = __nccwpck_require__(1129);
|
const schemas_1 = __nccwpck_require__(1129);
|
||||||
const utils_1 = __nccwpck_require__(1314);
|
const utils_1 = __nccwpck_require__(1314);
|
||||||
|
function getOptionalBoolean(name) {
|
||||||
|
const value = core.getInput(name);
|
||||||
|
return value.length > 0 ? core.getBooleanInput(name) : undefined;
|
||||||
|
}
|
||||||
function getOptionalInput(name) {
|
function getOptionalInput(name) {
|
||||||
const value = core.getInput(name);
|
const value = core.getInput(name);
|
||||||
return value.length > 0 ? value : undefined;
|
return value.length > 0 ? value : undefined;
|
||||||
@@ -27449,6 +27464,17 @@ function readInlineConfig() {
|
|||||||
validateLicenses('allow-licenses', allow_licenses);
|
validateLicenses('allow-licenses', allow_licenses);
|
||||||
validateLicenses('deny-licenses', deny_licenses);
|
validateLicenses('deny-licenses', deny_licenses);
|
||||||
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'));
|
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'));
|
||||||
|
const license_check = z
|
||||||
|
.boolean()
|
||||||
|
.default(true)
|
||||||
|
.parse(getOptionalBoolean('license-check'));
|
||||||
|
const vulnerability_check = z
|
||||||
|
.boolean()
|
||||||
|
.default(true)
|
||||||
|
.parse(getOptionalBoolean('vulnerability-check'));
|
||||||
|
if (license_check === false && vulnerability_check === false) {
|
||||||
|
throw new Error("Can't disable both license-check and vulnerability-check");
|
||||||
|
}
|
||||||
const base_ref = getOptionalInput('base-ref');
|
const base_ref = getOptionalInput('base-ref');
|
||||||
const head_ref = getOptionalInput('head-ref');
|
const head_ref = getOptionalInput('head-ref');
|
||||||
return {
|
return {
|
||||||
@@ -27457,6 +27483,8 @@ function readInlineConfig() {
|
|||||||
allow_licenses,
|
allow_licenses,
|
||||||
deny_licenses,
|
deny_licenses,
|
||||||
allow_ghsas,
|
allow_ghsas,
|
||||||
|
license_check,
|
||||||
|
vulnerability_check,
|
||||||
base_ref,
|
base_ref,
|
||||||
head_ref
|
head_ref
|
||||||
};
|
};
|
||||||
@@ -27633,6 +27661,8 @@ exports.ConfigurationOptionsSchema = z
|
|||||||
allow_licenses: z.array(z.string()).default([]),
|
allow_licenses: z.array(z.string()).default([]),
|
||||||
deny_licenses: z.array(z.string()).default([]),
|
deny_licenses: z.array(z.string()).default([]),
|
||||||
allow_ghsas: z.array(z.string()).default([]),
|
allow_ghsas: z.array(z.string()).default([]),
|
||||||
|
license_check: z.boolean().default(true),
|
||||||
|
vulnerability_check: z.boolean().default(true),
|
||||||
config_file: z.string().optional().default('false'),
|
config_file: z.string().optional().default('false'),
|
||||||
base_ref: z.string(),
|
base_ref: z.string(),
|
||||||
head_ref: z.string()
|
head_ref: z.string()
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
Generated
+7
-7
@@ -24,9 +24,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/node": "^16.18.0",
|
|
||||||
"@types/spdx-expression-parse": "^3.0.2",
|
"@types/spdx-expression-parse": "^3.0.2",
|
||||||
"@types/spdx-satisfies": "^0.1.0",
|
"@types/spdx-satisfies": "^0.1.0",
|
||||||
|
"@types/node": "^16.18.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||||
"@typescript-eslint/parser": "^5.41.0",
|
"@typescript-eslint/parser": "^5.41.0",
|
||||||
"@vercel/ncc": "^0.34.0",
|
"@vercel/ncc": "^0.34.0",
|
||||||
@@ -1802,9 +1802,9 @@
|
|||||||
"integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
|
"integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "16.18.0",
|
"version": "16.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.2.tgz",
|
||||||
"integrity": "sha512-LqYqYzYvnbCaQfLAwRt0zboqnsViwhZm+vjaMSqcfN36vulAg7Pt0T83q4WZO2YOBw3XdyHi8cQ88H22zmULOA=="
|
"integrity": "sha512-KIGQJyya+opDCFvDSZMNNS899ov5jlNdtN7PypgHWeb8e+5vWISdwTRo/ClsNVlmDihzOGqFyNBDamUs7TQQCA=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/prettier": {
|
"node_modules/@types/prettier": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
@@ -9592,9 +9592,9 @@
|
|||||||
"integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
|
"integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "16.18.0",
|
"version": "16.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.2.tgz",
|
||||||
"integrity": "sha512-LqYqYzYvnbCaQfLAwRt0zboqnsViwhZm+vjaMSqcfN36vulAg7Pt0T83q4WZO2YOBw3XdyHi8cQ88H22zmULOA=="
|
"integrity": "sha512-KIGQJyya+opDCFvDSZMNNS899ov5jlNdtN7PypgHWeb8e+5vWISdwTRo/ClsNVlmDihzOGqFyNBDamUs7TQQCA=="
|
||||||
},
|
},
|
||||||
"@types/prettier": {
|
"@types/prettier": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/node": "^16.18.0",
|
"@types/node": "^16.18.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||||
"@typescript-eslint/parser": "^5.41.0",
|
"@typescript-eslint/parser": "^5.41.0",
|
||||||
"@types/spdx-expression-parse": "^3.0.2",
|
"@types/spdx-expression-parse": "^3.0.2",
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ import {isSPDXValid} from './utils'
|
|||||||
|
|
||||||
type licenseKey = 'allow-licenses' | 'deny-licenses'
|
type licenseKey = 'allow-licenses' | 'deny-licenses'
|
||||||
|
|
||||||
|
function getOptionalBoolean(name: string): boolean | undefined {
|
||||||
|
const value = core.getInput(name)
|
||||||
|
return value.length > 0 ? core.getBooleanInput(name) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
function getOptionalInput(name: string): string | undefined {
|
function getOptionalInput(name: string): string | undefined {
|
||||||
const value = core.getInput(name)
|
const value = core.getInput(name)
|
||||||
return value.length > 0 ? value : undefined
|
return value.length > 0 ? value : undefined
|
||||||
@@ -77,6 +82,18 @@ export function readInlineConfig(): ConfigurationOptions {
|
|||||||
|
|
||||||
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'))
|
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'))
|
||||||
|
|
||||||
|
const license_check = z
|
||||||
|
.boolean()
|
||||||
|
.default(true)
|
||||||
|
.parse(getOptionalBoolean('license-check'))
|
||||||
|
const vulnerability_check = z
|
||||||
|
.boolean()
|
||||||
|
.default(true)
|
||||||
|
.parse(getOptionalBoolean('vulnerability-check'))
|
||||||
|
if (license_check === false && vulnerability_check === false) {
|
||||||
|
throw new Error("Can't disable both license-check and vulnerability-check")
|
||||||
|
}
|
||||||
|
|
||||||
const base_ref = getOptionalInput('base-ref')
|
const base_ref = getOptionalInput('base-ref')
|
||||||
const head_ref = getOptionalInput('head-ref')
|
const head_ref = getOptionalInput('head-ref')
|
||||||
|
|
||||||
@@ -86,6 +103,8 @@ export function readInlineConfig(): ConfigurationOptions {
|
|||||||
allow_licenses,
|
allow_licenses,
|
||||||
deny_licenses,
|
deny_licenses,
|
||||||
allow_ghsas,
|
allow_ghsas,
|
||||||
|
license_check,
|
||||||
|
vulnerability_check,
|
||||||
base_ref,
|
base_ref,
|
||||||
head_ref
|
head_ref
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import spdxSatisfies from 'spdx-satisfies'
|
import spdxSatisfies from 'spdx-satisfies'
|
||||||
import {Octokit} from 'octokit'
|
import {Octokit} from 'octokit'
|
||||||
|
|
||||||
import {Change, Changes} from './schemas'
|
import {Change, Changes} from './schemas'
|
||||||
import {isSPDXValid} from './utils'
|
import {isSPDXValid} from './utils'
|
||||||
|
|
||||||
|
|||||||
+15
-7
@@ -53,13 +53,21 @@ async function run(): Promise<void> {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
summary.addSummaryToSummary(addedChanges, invalidLicenseChanges)
|
summary.addSummaryToSummary(
|
||||||
summary.addChangeVulnerabilitiesToSummary(addedChanges, minSeverity)
|
config.vulnerability_check ? addedChanges : null,
|
||||||
summary.addLicensesToSummary(invalidLicenseChanges, config)
|
config.license_check ? invalidLicenseChanges : null
|
||||||
summary.addScannedDependencies(changes)
|
)
|
||||||
|
|
||||||
|
if (config.vulnerability_check) {
|
||||||
|
summary.addChangeVulnerabilitiesToSummary(addedChanges, minSeverity)
|
||||||
printVulnerabilitiesBlock(addedChanges, minSeverity)
|
printVulnerabilitiesBlock(addedChanges, minSeverity)
|
||||||
|
}
|
||||||
|
if (config.license_check) {
|
||||||
|
summary.addLicensesToSummary(invalidLicenseChanges, config)
|
||||||
printLicensesBlock(invalidLicenseChanges)
|
printLicensesBlock(invalidLicenseChanges)
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.addScannedDependencies(changes)
|
||||||
printScannedDependencies(changes)
|
printScannedDependencies(changes)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof RequestError && error.status === 404) {
|
if (error instanceof RequestError && error.status === 404) {
|
||||||
@@ -123,13 +131,13 @@ function printLicensesBlock(
|
|||||||
): void {
|
): void {
|
||||||
core.group('Licenses', async () => {
|
core.group('Licenses', async () => {
|
||||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
if (invalidLicenseChanges.forbidden.length > 0) {
|
||||||
core.info('\nThe following dependencies have incompatible licenses:\n')
|
core.info('\nThe following dependencies have incompatible licenses:')
|
||||||
printLicensesError(invalidLicenseChanges.forbidden)
|
printLicensesError(invalidLicenseChanges.forbidden)
|
||||||
core.setFailed('Dependency review detected incompatible licenses.')
|
core.setFailed('Dependency review detected incompatible licenses.')
|
||||||
}
|
}
|
||||||
if (invalidLicenseChanges.unresolved.length > 0) {
|
if (invalidLicenseChanges.unresolved.length > 0) {
|
||||||
core.warning(
|
core.warning(
|
||||||
'\nThe validity of the licenses of the dependecies below could not be determine. Ensure that they are valid spdx licenses:\n'
|
'\nThe validity of the licenses of the dependencies below could not be determined. Ensure that they are valid SPDX licenses:'
|
||||||
)
|
)
|
||||||
printLicensesError(invalidLicenseChanges.unresolved)
|
printLicensesError(invalidLicenseChanges.unresolved)
|
||||||
core.setFailed(
|
core.setFailed(
|
||||||
@@ -153,7 +161,7 @@ function printNullLicenses(changes: Changes): void {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info('\nWe could not detect a license for the following dependencies:\n')
|
core.info('\nWe could not detect a license for the following dependencies:')
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
core.info(
|
core.info(
|
||||||
`${styles.bold.open}${change.manifest} » ${change.name}@${change.version}${styles.bold.close}`
|
`${styles.bold.open}${change.manifest} » ${change.name}@${change.version}${styles.bold.close}`
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export const ConfigurationOptionsSchema = z
|
|||||||
allow_licenses: z.array(z.string()).default([]),
|
allow_licenses: z.array(z.string()).default([]),
|
||||||
deny_licenses: z.array(z.string()).default([]),
|
deny_licenses: z.array(z.string()).default([]),
|
||||||
allow_ghsas: z.array(z.string()).default([]),
|
allow_ghsas: z.array(z.string()).default([]),
|
||||||
|
license_check: z.boolean().default(true),
|
||||||
|
vulnerability_check: z.boolean().default(true),
|
||||||
config_file: z.string().optional().default('false'),
|
config_file: z.string().optional().default('false'),
|
||||||
base_ref: z.string(),
|
base_ref: z.string(),
|
||||||
head_ref: z.string()
|
head_ref: z.string()
|
||||||
|
|||||||
+50
-50
@@ -4,16 +4,24 @@ import {SummaryTableRow} from '@actions/core/lib/summary'
|
|||||||
import {groupDependenciesByManifest, getManifestsSet, renderUrl} from './utils'
|
import {groupDependenciesByManifest, getManifestsSet, renderUrl} from './utils'
|
||||||
|
|
||||||
export function addSummaryToSummary(
|
export function addSummaryToSummary(
|
||||||
addedPackages: Changes,
|
addedPackages: Changes | null,
|
||||||
invalidLicenseChanges: Record<string, Changes>
|
invalidLicenseChanges: Record<string, Changes> | null
|
||||||
): void {
|
): void {
|
||||||
core.summary.addHeading('Dependency Review').addRaw(
|
core.summary
|
||||||
`We found:
|
.addHeading('Dependency Review')
|
||||||
- ${addedPackages.length} vulnerable package(s),
|
.addRaw('We found:')
|
||||||
- ${invalidLicenseChanges.unresolved.length} package(s) with unprocessable licenses,
|
.addList([
|
||||||
- ${invalidLicenseChanges.forbidden.length} package(s) with incompatible licenses, and
|
...(addedPackages
|
||||||
- ${invalidLicenseChanges.unlicensed.length} package(s) with unknown licenses.`
|
? [`${addedPackages.length} vulnerable package(s)`]
|
||||||
)
|
: []),
|
||||||
|
...(invalidLicenseChanges
|
||||||
|
? [
|
||||||
|
`${invalidLicenseChanges.unresolved.length} package(s) with invalid SPDX license definitions`,
|
||||||
|
`${invalidLicenseChanges.forbidden.length} package(s) with incompatible licenses`,
|
||||||
|
`${invalidLicenseChanges.unlicensed.length} package(s) with unknown licenses.`
|
||||||
|
]
|
||||||
|
: [])
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addChangeVulnerabilitiesToSummary(
|
export function addChangeVulnerabilitiesToSummary(
|
||||||
@@ -93,76 +101,68 @@ export function addLicensesToSummary(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (Object.values(invalidLicenseChanges).every(item => item.length === 0)) {
|
||||||
invalidLicenseChanges.forbidden.length === 0 &&
|
|
||||||
invalidLicenseChanges.unlicensed.length === 0
|
|
||||||
) {
|
|
||||||
core.summary.addQuote('No license violations detected.')
|
core.summary.addQuote('No license violations detected.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
|
||||||
const rows: SummaryTableRow[] = []
|
|
||||||
const manifests = getManifestsSet(invalidLicenseChanges.forbidden)
|
|
||||||
|
|
||||||
core.summary.addHeading('Incompatible Licenses', 3).addSeparator()
|
|
||||||
|
|
||||||
for (const manifest of manifests) {
|
|
||||||
core.summary.addHeading(`<em>${manifest}</em>`, 4)
|
|
||||||
|
|
||||||
for (const change of invalidLicenseChanges.forbidden.filter(
|
|
||||||
pkg => pkg.manifest === manifest
|
|
||||||
)) {
|
|
||||||
rows.push([
|
|
||||||
renderUrl(change.source_repository_url, change.name),
|
|
||||||
change.version,
|
|
||||||
change.license || ''
|
|
||||||
])
|
|
||||||
}
|
|
||||||
core.summary.addTable([['Package', 'Version', 'License'], ...rows])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
core.summary.addQuote('No license violations detected.')
|
|
||||||
}
|
|
||||||
|
|
||||||
core.debug(
|
core.debug(
|
||||||
`found ${invalidLicenseChanges.unlicensed.length} unknown licenses`
|
`found ${invalidLicenseChanges.unlicensed.length} unknown licenses`
|
||||||
)
|
)
|
||||||
|
|
||||||
if (invalidLicenseChanges.unlicensed.length > 0) {
|
|
||||||
const rows: SummaryTableRow[] = []
|
|
||||||
const manifests = getManifestsSet(invalidLicenseChanges.unlicensed)
|
|
||||||
|
|
||||||
core.debug(
|
core.debug(
|
||||||
`found ${manifests.entries.length} manifests for unknown licenses`
|
`${invalidLicenseChanges.unresolved.length} licenses could not be validated`
|
||||||
)
|
)
|
||||||
|
|
||||||
core.summary.addHeading('Unknown Licenses', 3).addSeparator()
|
printLicenseViolation(
|
||||||
|
'Incompatible Licenses',
|
||||||
|
invalidLicenseChanges.forbidden
|
||||||
|
)
|
||||||
|
printLicenseViolation('Unknown Licenses', invalidLicenseChanges.unlicensed)
|
||||||
|
printLicenseViolation(
|
||||||
|
'Invalid SPDX License Definitions',
|
||||||
|
invalidLicenseChanges.unresolved
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function printLicenseViolation(heading: string, changes: Changes): void {
|
||||||
|
core.summary.addHeading(heading, 5).addSeparator()
|
||||||
|
|
||||||
|
if (changes.length > 0) {
|
||||||
|
const rows: SummaryTableRow[] = []
|
||||||
|
const manifests = getManifestsSet(changes)
|
||||||
|
|
||||||
for (const manifest of manifests) {
|
for (const manifest of manifests) {
|
||||||
core.summary.addHeading(`<em>${manifest}</em>`, 4)
|
core.summary.addHeading(`<em>${manifest}</em>`, 4)
|
||||||
|
|
||||||
for (const change of invalidLicenseChanges.unlicensed.filter(
|
for (const change of changes.filter(pkg => pkg.manifest === manifest)) {
|
||||||
pkg => pkg.manifest === manifest
|
|
||||||
)) {
|
|
||||||
rows.push([
|
rows.push([
|
||||||
renderUrl(change.source_repository_url, change.name),
|
renderUrl(change.source_repository_url, change.name),
|
||||||
change.version
|
change.version,
|
||||||
|
formatLicense(change.license)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
core.summary.addTable([['Package', 'Version'], ...rows])
|
core.summary.addTable([['Package', 'Version', 'License'], ...rows])
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
core.summary.addQuote(`No ${heading.toLowerCase()} detected.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatLicense(license: string | null): string {
|
||||||
|
if (license === null || license === 'NOASSERTION') {
|
||||||
|
return 'Null'
|
||||||
|
}
|
||||||
|
return license
|
||||||
|
}
|
||||||
|
|
||||||
export function addScannedDependencies(changes: Changes): void {
|
export function addScannedDependencies(changes: Changes): void {
|
||||||
const dependencies = groupDependenciesByManifest(changes)
|
const dependencies = groupDependenciesByManifest(changes)
|
||||||
const manifests = dependencies.keys()
|
const manifests = dependencies.keys()
|
||||||
|
|
||||||
const summary = core.summary
|
const summary = core.summary
|
||||||
.addHeading('Scanned Dependencies')
|
.addHeading('Scanned Dependencies')
|
||||||
.addRaw(`We scanned ${dependencies.size} manifest files:`)
|
.addHeading(`We scanned ${dependencies.size} manifest files:`, 5)
|
||||||
|
|
||||||
for (const manifest of manifests) {
|
for (const manifest of manifests) {
|
||||||
const deps = dependencies.get(manifest)
|
const deps = dependencies.get(manifest)
|
||||||
@@ -170,7 +170,7 @@ export function addScannedDependencies(changes: Changes): void {
|
|||||||
const dependencyNames = deps.map(
|
const dependencyNames = deps.map(
|
||||||
dependency => `<li>${dependency.name}@${dependency.version}</li>`
|
dependency => `<li>${dependency.name}@${dependency.version}</li>`
|
||||||
)
|
)
|
||||||
summary.addRaw(`<h3>${manifest}</h3><ul>${dependencyNames.join('')}</ul>`)
|
summary.addDetails(manifest, `<ul>${dependencyNames.join('')}</ul>`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user