Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 20f8e76960 | |||
| 47f663b6ee | |||
| dfcdb87cb3 | |||
| 79f5aede88 | |||
| aef949f026 |
@@ -5,8 +5,14 @@ raise an error if any vulnerabilities or invalid licenses are being introduced.
|
||||
|
||||
The action is available for all public repositories, as well as private repositories that have GitHub Advanced Security licensed.
|
||||
|
||||
You can see the results on the job logs
|
||||
|
||||
<img width="854" alt="Screen Shot 2022-03-31 at 1 10 51 PM" src="https://user-images.githubusercontent.com/2161/161042286-b22d7dd3-13cb-458d-8744-ce70ed9bf562.png">
|
||||
|
||||
or on the job summary
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/7847935/182871416-50332bbb-b279-4621-a136-ca72a4314301.png">
|
||||
|
||||
## Installation
|
||||
|
||||
**Please keep in mind that you need a [GitHub Advanced Security](https://docs.github.com/en/enterprise-cloud@latest/get-started/learning-about-github/about-github-advanced-security) license if you're running this action on private repositories.**
|
||||
@@ -144,7 +150,6 @@ to filter. A couple of examples:
|
||||
|
||||
**Important**
|
||||
|
||||
<<<<<<< HEAD
|
||||
- The action will only accept one of the two parameters; an error will
|
||||
be raised if you provide both.
|
||||
- By default both parameters are empty (no license checking is
|
||||
@@ -152,16 +157,6 @@ to filter. A couple of examples:
|
||||
- We don't have license information for all of your dependents. If we
|
||||
can't detect the license for a dependency **we will inform you, but the
|
||||
action won't fail**.
|
||||
=======
|
||||
* Checking for licenses is not supported on GHES.
|
||||
* The action will only accept one of the two parameters; an error will
|
||||
be raised if you provide both.
|
||||
* By default both parameters are empty (no license checking is
|
||||
performed).
|
||||
* We don't have license information for all of your dependents. If we
|
||||
can't detect the license for a dependency **we will inform you, but the
|
||||
action won't fail**.
|
||||
>>>>>>> main
|
||||
|
||||
## Blocking pull requests
|
||||
|
||||
|
||||
+166
-9
@@ -201,6 +201,7 @@ const request_error_1 = __nccwpck_require__(537);
|
||||
const config_1 = __nccwpck_require__(6373);
|
||||
const filter_1 = __nccwpck_require__(8752);
|
||||
const licenses_1 = __nccwpck_require__(3247);
|
||||
const summary = __importStar(__nccwpck_require__(8608));
|
||||
const git_refs_1 = __nccwpck_require__(1086);
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
@@ -219,21 +220,24 @@ function run() {
|
||||
allow: config.allow_licenses,
|
||||
deny: config.deny_licenses
|
||||
};
|
||||
const filteredChanges = (0, filter_1.filterChangesBySeverity)(minSeverity, changes);
|
||||
for (const change of filteredChanges) {
|
||||
if (change.change_type === 'added' &&
|
||||
change.vulnerabilities !== undefined &&
|
||||
change.vulnerabilities.length > 0) {
|
||||
printChangeVulnerabilities(change);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
const addedChanges = (0, filter_1.filterChangesBySeverity)(minSeverity, changes).filter(change => change.change_type === 'added' &&
|
||||
change.vulnerabilities !== undefined &&
|
||||
change.vulnerabilities.length > 0);
|
||||
const [licenseErrors, unknownLicenses] = (0, licenses_1.getDeniedLicenseChanges)(changes, licenses);
|
||||
summary.addSummaryToSummary(addedChanges, licenseErrors, unknownLicenses);
|
||||
if (addedChanges.length > 0) {
|
||||
for (const change of addedChanges) {
|
||||
printChangeVulnerabilities(change);
|
||||
}
|
||||
failed = true;
|
||||
}
|
||||
summary.addChangeVulnerabilitiesToSummary(addedChanges, minSeverity || '');
|
||||
if (licenseErrors.length > 0) {
|
||||
printLicensesError(licenseErrors);
|
||||
core.setFailed('Dependency review detected incompatible licenses.');
|
||||
}
|
||||
printNullLicenses(unknownLicenses);
|
||||
summary.addLicensesToSummary(licenseErrors, unknownLicenses, config);
|
||||
if (failed) {
|
||||
core.setFailed('Dependency review detected vulnerable packages.');
|
||||
}
|
||||
@@ -257,6 +261,9 @@ function run() {
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
yield core.summary.write();
|
||||
}
|
||||
});
|
||||
}
|
||||
function printChangeVulnerabilities(change) {
|
||||
@@ -366,6 +373,156 @@ exports.ConfigurationOptionsSchema = z
|
||||
exports.ChangesSchema = z.array(exports.ChangeSchema);
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 8608:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.addLicensesToSummary = exports.addChangeVulnerabilitiesToSummary = exports.addSummaryToSummary = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
function addSummaryToSummary(addedPackages, licenseErrors, unknownLicenses) {
|
||||
core.summary
|
||||
.addHeading('Dependency Review')
|
||||
.addRaw(`We found ${addedPackages.length} vulnerable package(s), ${licenseErrors.length} package(s) with incompatible licenses, and ${unknownLicenses.length} package(s) with unknown licenses.`);
|
||||
}
|
||||
exports.addSummaryToSummary = addSummaryToSummary;
|
||||
function addChangeVulnerabilitiesToSummary(addedPackages, severity) {
|
||||
const rows = [];
|
||||
const manifests = getManifests(addedPackages);
|
||||
core.summary
|
||||
.addHeading('Vulnerabilities')
|
||||
.addQuote(`Vulnerabilites were filtered by mininum severity <strong>${severity}</strong>.`);
|
||||
if (addedPackages.length === 0) {
|
||||
core.summary.addQuote('No vulnerabilities found in added packages.');
|
||||
return;
|
||||
}
|
||||
for (const manifest of manifests) {
|
||||
for (const change of addedPackages.filter(pkg => pkg.manifest === manifest)) {
|
||||
let previous_package = '';
|
||||
let previous_version = '';
|
||||
for (const vuln of change.vulnerabilities) {
|
||||
const sameAsPrevious = previous_package === change.name &&
|
||||
previous_version === change.version;
|
||||
if (!sameAsPrevious) {
|
||||
rows.push([
|
||||
renderUrl(change.source_repository_url, change.name),
|
||||
change.version,
|
||||
renderUrl(vuln.advisory_url, vuln.advisory_summary),
|
||||
vuln.severity
|
||||
]);
|
||||
}
|
||||
else {
|
||||
rows.push([
|
||||
{ data: '', colspan: '2' },
|
||||
renderUrl(vuln.advisory_url, vuln.advisory_summary),
|
||||
vuln.severity
|
||||
]);
|
||||
}
|
||||
previous_package = change.name;
|
||||
previous_version = change.version;
|
||||
}
|
||||
}
|
||||
core.summary.addHeading(`<em>${manifest}</em>`, 3).addTable([
|
||||
[
|
||||
{ data: 'Name', header: true },
|
||||
{ data: 'Version', header: true },
|
||||
{ data: 'Vulnerability', header: true },
|
||||
{ data: 'Severity', header: true }
|
||||
],
|
||||
...rows
|
||||
]);
|
||||
}
|
||||
}
|
||||
exports.addChangeVulnerabilitiesToSummary = addChangeVulnerabilitiesToSummary;
|
||||
function addLicensesToSummary(licenseErrors, unknownLicenses, config) {
|
||||
core.summary.addHeading('Licenses');
|
||||
if (config.allow_licenses && config.allow_licenses.length > 0) {
|
||||
core.summary.addQuote(`<strong>Allowed Licenses</strong>: ${config.allow_licenses.join(', ')}`);
|
||||
}
|
||||
if (config.deny_licenses && config.deny_licenses.length > 0) {
|
||||
core.summary.addQuote(`<strong>Denied Licenses</strong>: ${config.deny_licenses.join(', ')}`);
|
||||
}
|
||||
if (licenseErrors.length === 0 && unknownLicenses.length === 0) {
|
||||
core.summary.addQuote('No license violations detected.');
|
||||
return;
|
||||
}
|
||||
if (licenseErrors.length > 0) {
|
||||
const rows = [];
|
||||
const manifests = getManifests(licenseErrors);
|
||||
core.summary.addHeading('Incompatible Licenses', 3).addSeparator();
|
||||
for (const manifest of manifests) {
|
||||
core.summary.addHeading(`<em>${manifest}</em>`, 4);
|
||||
for (const change of licenseErrors.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(`found ${unknownLicenses.length} unknown licenses`);
|
||||
if (unknownLicenses.length > 0) {
|
||||
const rows = [];
|
||||
const manifests = getManifests(unknownLicenses);
|
||||
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 unknownLicenses.filter(pkg => pkg.manifest === manifest)) {
|
||||
rows.push([
|
||||
renderUrl(change.source_repository_url, change.name),
|
||||
change.version
|
||||
]);
|
||||
}
|
||||
core.summary.addTable([['Package', 'Version'], ...rows]);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.addLicensesToSummary = addLicensesToSummary;
|
||||
function getManifests(changes) {
|
||||
return new Set(changes.flatMap(c => c.manifest));
|
||||
}
|
||||
function renderUrl(url, text) {
|
||||
if (url) {
|
||||
return `<a href="${url}">${text}</a>`;
|
||||
}
|
||||
else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 7351:
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+20
-10
@@ -7,6 +7,7 @@ import {Change, Severity} from './schemas'
|
||||
import {readConfig} from '../src/config'
|
||||
import {filterChangesBySeverity} from '../src/filter'
|
||||
import {getDeniedLicenseChanges} from './licenses'
|
||||
import * as summary from './summary'
|
||||
import {getRefs} from './git-refs'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
@@ -29,27 +30,32 @@ async function run(): Promise<void> {
|
||||
deny: config.deny_licenses
|
||||
}
|
||||
|
||||
const filteredChanges = filterChangesBySeverity(
|
||||
const addedChanges = filterChangesBySeverity(
|
||||
minSeverity as Severity,
|
||||
changes
|
||||
)
|
||||
|
||||
for (const change of filteredChanges) {
|
||||
if (
|
||||
).filter(
|
||||
change =>
|
||||
change.change_type === 'added' &&
|
||||
change.vulnerabilities !== undefined &&
|
||||
change.vulnerabilities.length > 0
|
||||
) {
|
||||
printChangeVulnerabilities(change)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const [licenseErrors, unknownLicenses] = getDeniedLicenseChanges(
|
||||
changes,
|
||||
licenses
|
||||
)
|
||||
|
||||
summary.addSummaryToSummary(addedChanges, licenseErrors, unknownLicenses)
|
||||
|
||||
if (addedChanges.length > 0) {
|
||||
for (const change of addedChanges) {
|
||||
printChangeVulnerabilities(change)
|
||||
}
|
||||
failed = true
|
||||
}
|
||||
|
||||
summary.addChangeVulnerabilitiesToSummary(addedChanges, minSeverity || '')
|
||||
|
||||
if (licenseErrors.length > 0) {
|
||||
printLicensesError(licenseErrors)
|
||||
core.setFailed('Dependency review detected incompatible licenses.')
|
||||
@@ -57,6 +63,8 @@ async function run(): Promise<void> {
|
||||
|
||||
printNullLicenses(unknownLicenses)
|
||||
|
||||
summary.addLicensesToSummary(licenseErrors, unknownLicenses, config)
|
||||
|
||||
if (failed) {
|
||||
core.setFailed('Dependency review detected vulnerable packages.')
|
||||
} else {
|
||||
@@ -80,6 +88,8 @@ async function run(): Promise<void> {
|
||||
core.setFailed('Unexpected fatal error')
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await core.summary.write()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
import * as core from '@actions/core'
|
||||
import {ConfigurationOptions, Change, Changes} from './schemas'
|
||||
import {SummaryTableRow} from '@actions/core/lib/summary'
|
||||
|
||||
export function addSummaryToSummary(
|
||||
addedPackages: Changes,
|
||||
licenseErrors: Change[],
|
||||
unknownLicenses: Change[]
|
||||
): void {
|
||||
core.summary
|
||||
.addHeading('Dependency Review')
|
||||
.addRaw(
|
||||
`We found ${addedPackages.length} vulnerable package(s), ${licenseErrors.length} package(s) with incompatible licenses, and ${unknownLicenses.length} package(s) with unknown licenses.`
|
||||
)
|
||||
}
|
||||
|
||||
export function addChangeVulnerabilitiesToSummary(
|
||||
addedPackages: Changes,
|
||||
severity: string
|
||||
): void {
|
||||
const rows: SummaryTableRow[] = []
|
||||
|
||||
const manifests = getManifests(addedPackages)
|
||||
|
||||
core.summary
|
||||
.addHeading('Vulnerabilities')
|
||||
.addQuote(
|
||||
`Vulnerabilites were filtered by mininum severity <strong>${severity}</strong>.`
|
||||
)
|
||||
|
||||
if (addedPackages.length === 0) {
|
||||
core.summary.addQuote('No vulnerabilities found in added packages.')
|
||||
return
|
||||
}
|
||||
|
||||
for (const manifest of manifests) {
|
||||
for (const change of addedPackages.filter(
|
||||
pkg => pkg.manifest === manifest
|
||||
)) {
|
||||
let previous_package = ''
|
||||
let previous_version = ''
|
||||
for (const vuln of change.vulnerabilities) {
|
||||
const sameAsPrevious =
|
||||
previous_package === change.name &&
|
||||
previous_version === change.version
|
||||
|
||||
if (!sameAsPrevious) {
|
||||
rows.push([
|
||||
renderUrl(change.source_repository_url, change.name),
|
||||
change.version,
|
||||
renderUrl(vuln.advisory_url, vuln.advisory_summary),
|
||||
vuln.severity
|
||||
])
|
||||
} else {
|
||||
rows.push([
|
||||
{data: '', colspan: '2'},
|
||||
renderUrl(vuln.advisory_url, vuln.advisory_summary),
|
||||
vuln.severity
|
||||
])
|
||||
}
|
||||
previous_package = change.name
|
||||
previous_version = change.version
|
||||
}
|
||||
}
|
||||
core.summary.addHeading(`<em>${manifest}</em>`, 3).addTable([
|
||||
[
|
||||
{data: 'Name', header: true},
|
||||
{data: 'Version', header: true},
|
||||
{data: 'Vulnerability', header: true},
|
||||
{data: 'Severity', header: true}
|
||||
],
|
||||
...rows
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export function addLicensesToSummary(
|
||||
licenseErrors: Change[],
|
||||
unknownLicenses: Change[],
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.summary.addHeading('Licenses')
|
||||
|
||||
if (config.allow_licenses && config.allow_licenses.length > 0) {
|
||||
core.summary.addQuote(
|
||||
`<strong>Allowed Licenses</strong>: ${config.allow_licenses.join(', ')}`
|
||||
)
|
||||
}
|
||||
if (config.deny_licenses && config.deny_licenses.length > 0) {
|
||||
core.summary.addQuote(
|
||||
`<strong>Denied Licenses</strong>: ${config.deny_licenses.join(', ')}`
|
||||
)
|
||||
}
|
||||
|
||||
if (licenseErrors.length === 0 && unknownLicenses.length === 0) {
|
||||
core.summary.addQuote('No license violations detected.')
|
||||
return
|
||||
}
|
||||
|
||||
if (licenseErrors.length > 0) {
|
||||
const rows: SummaryTableRow[] = []
|
||||
const manifests = getManifests(licenseErrors)
|
||||
|
||||
core.summary.addHeading('Incompatible Licenses', 3).addSeparator()
|
||||
|
||||
for (const manifest of manifests) {
|
||||
core.summary.addHeading(`<em>${manifest}</em>`, 4)
|
||||
|
||||
for (const change of licenseErrors.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(`found ${unknownLicenses.length} unknown licenses`)
|
||||
|
||||
if (unknownLicenses.length > 0) {
|
||||
const rows: SummaryTableRow[] = []
|
||||
const manifests = getManifests(unknownLicenses)
|
||||
|
||||
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 unknownLicenses.filter(
|
||||
pkg => pkg.manifest === manifest
|
||||
)) {
|
||||
rows.push([
|
||||
renderUrl(change.source_repository_url, change.name),
|
||||
change.version
|
||||
])
|
||||
}
|
||||
|
||||
core.summary.addTable([['Package', 'Version'], ...rows])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getManifests(changes: Changes): Set<string> {
|
||||
return new Set(changes.flatMap(c => c.manifest))
|
||||
}
|
||||
|
||||
function renderUrl(url: string | null, text: string): string {
|
||||
if (url) {
|
||||
return `<a href="${url}">${text}</a>`
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user