Merge branch 'main' into comment-on-failure
This commit is contained in:
@@ -78,6 +78,8 @@ Configure this action by either inlining these options in your workflow file, or
|
||||
| `allow-dependencies-licenses`\* | Contains a list of packages that will be excluded from license checks. | Any package(s) in [purl](https://github.com/package-url/purl-spec) format | none |
|
||||
| `base-ref`/`head-ref` | Provide custom git references for the git base/head when performing the comparison check. This is only used for event types other than `pull_request` and `pull_request_target`. | Any valid git ref(s) in your project | none |
|
||||
| `comment-summary-in-pr` | Enable or disable reporting the review summary as a comment in the pull request. If enabled, you must give the workflow or job permission `pull-requests: write`. | `always`, `on-failure`, `never` | `never` |
|
||||
| `deny-packages` | Any number of packages to block in a PR. | Package(s) in [purl](https://github.com/package-url/purl-spec) format | empty |
|
||||
| `deny-groups` | Any number of groups (namespaces) to block in a PR. | Namespace(s) in [purl](https://github.com/package-url/purl-spec) format (no package name, no version number) | empty |
|
||||
|
||||
\*not supported for use with GitHub Enterprise Server
|
||||
|
||||
|
||||
166
__tests__/deny.test.ts
Normal file
166
__tests__/deny.test.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import {expect, jest, test} from '@jest/globals'
|
||||
import {Change, Changes} from '../src/schemas'
|
||||
|
||||
let getDeniedChanges: Function
|
||||
|
||||
const npmChange: Change = {
|
||||
manifest: 'package.json',
|
||||
change_type: 'added',
|
||||
ecosystem: 'npm',
|
||||
name: 'Reeuhq',
|
||||
version: '1.0.2',
|
||||
package_url: 'pkg:npm/reeuhq@1.0.2',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'critical',
|
||||
advisory_ghsa_id: 'first-random_string',
|
||||
advisory_summary: 'very dangerous',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const rubyChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'Gemfile.lock',
|
||||
ecosystem: 'rubygems',
|
||||
name: 'actionsomething',
|
||||
version: '3.2.0',
|
||||
package_url: 'pkg:gem/actionsomething@3.2.0',
|
||||
license: 'BSD',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'moderate',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
},
|
||||
{
|
||||
severity: 'low',
|
||||
advisory_ghsa_id: 'third-random_string',
|
||||
advisory_summary: 'dont page me',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const pipChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'requirements.txt',
|
||||
ecosystem: 'pip',
|
||||
name: 'package-1',
|
||||
version: '1.1.1',
|
||||
package_url: 'pkg:pip/package-1@1.1.1',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'moderate',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
},
|
||||
{
|
||||
severity: 'low',
|
||||
advisory_ghsa_id: 'third-random_string',
|
||||
advisory_summary: 'dont page me',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const mvnChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'pom.xml',
|
||||
ecosystem: 'maven',
|
||||
name: 'org.apache.logging.log4j:log4j-core',
|
||||
version: '2.15.0',
|
||||
package_url: 'pkg:maven/org.apache.logging.log4j/log4j-core@2.14.7',
|
||||
license: 'Apache-2.0',
|
||||
source_repository_url:
|
||||
'https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core',
|
||||
scope: 'unknown',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'critical',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
jest.mock('@actions/core')
|
||||
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
licenses: {
|
||||
getForRepo: jest
|
||||
.fn()
|
||||
.mockReturnValue({data: {license: {spdx_id: 'AGPL'}}})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jest.mock('octokit', () => {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
Octokit: class {
|
||||
constructor() {
|
||||
return mockOctokit
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.resetModules()
|
||||
jest.doMock('spdx-satisfies', () => {
|
||||
// mock spdx-satisfies return value
|
||||
// true for BSD, false for all others
|
||||
return jest.fn((license: string, _: string): boolean => license === 'BSD')
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
;({getDeniedChanges} = require('../src/deny'))
|
||||
})
|
||||
|
||||
test('it adds packages in the deny packages list', async () => {
|
||||
const changes: Changes = [npmChange, rubyChange]
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
changes,
|
||||
['pkg:gem/actionsomething'],
|
||||
[]
|
||||
)
|
||||
|
||||
expect(deniedChanges[0]).toBe(rubyChange)
|
||||
expect(deniedChanges.length).toEqual(1)
|
||||
})
|
||||
|
||||
test('it adds packages in the deny group list', async () => {
|
||||
const changes: Changes = [mvnChange, rubyChange]
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
changes,
|
||||
[],
|
||||
['pkg:maven/org.apache.logging.log4j']
|
||||
)
|
||||
|
||||
expect(deniedChanges[0]).toBe(mvnChange)
|
||||
expect(deniedChanges.length).toEqual(1)
|
||||
})
|
||||
|
||||
test('it adds packages outside of the deny lists', async () => {
|
||||
const changes: Changes = [npmChange, pipChange]
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
changes,
|
||||
['pkg:gem/actionsomething'],
|
||||
['pkg:maven:org.apache.logging.log4j']
|
||||
)
|
||||
|
||||
expect(deniedChanges.length).toEqual(0)
|
||||
})
|
||||
@@ -24,6 +24,8 @@ const defaultConfig: ConfigurationOptions = {
|
||||
allow_ghsas: [],
|
||||
allow_licenses: [],
|
||||
deny_licenses: [],
|
||||
deny_packages: [],
|
||||
deny_groups: [],
|
||||
comment_summary_in_pr: 'never'
|
||||
}
|
||||
|
||||
@@ -70,6 +72,7 @@ test('prints headline as h1', () => {
|
||||
summary.addSummaryToSummary(
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
defaultConfig
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
@@ -81,6 +84,7 @@ test('only includes "No vulnerabilities or license issues found"-message if both
|
||||
summary.addSummaryToSummary(
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
defaultConfig
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
@@ -90,7 +94,12 @@ test('only includes "No vulnerabilities or license issues found"-message if both
|
||||
|
||||
test('only includes "No vulnerabilities found"-message if "license_check" is set to false and nothing was found', () => {
|
||||
const config = {...defaultConfig, license_check: false}
|
||||
summary.addSummaryToSummary(emptyChanges, emptyInvalidLicenseChanges, config)
|
||||
summary.addSummaryToSummary(
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
config
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
|
||||
expect(text).toContain('✅ No vulnerabilities found.')
|
||||
@@ -98,7 +107,12 @@ test('only includes "No vulnerabilities found"-message if "license_check" is set
|
||||
|
||||
test('only includes "No license issues found"-message if "vulnerability_check" is set to false and nothing was found', () => {
|
||||
const config = {...defaultConfig, vulnerability_check: false}
|
||||
summary.addSummaryToSummary(emptyChanges, emptyInvalidLicenseChanges, config)
|
||||
summary.addSummaryToSummary(
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
config
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
|
||||
expect(text).toContain('✅ No license issues found.')
|
||||
@@ -108,6 +122,7 @@ test('groups dependencies with empty manifest paths together', () => {
|
||||
summary.addSummaryToSummary(
|
||||
changesWithEmptyManifests,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
defaultConfig
|
||||
)
|
||||
summary.addScannedDependencies(changesWithEmptyManifests)
|
||||
@@ -124,6 +139,7 @@ test('does not include status section if nothing was found', () => {
|
||||
summary.addSummaryToSummary(
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
defaultConfig
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
@@ -142,7 +158,12 @@ test('includes count and status icons for all findings', () => {
|
||||
unlicensed: [createTestChange(), createTestChange(), createTestChange()]
|
||||
}
|
||||
|
||||
summary.addSummaryToSummary(vulnerabilities, licenseIssues, defaultConfig)
|
||||
summary.addSummaryToSummary(
|
||||
vulnerabilities,
|
||||
licenseIssues,
|
||||
emptyChanges,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
const text = core.summary.stringify()
|
||||
expect(text).toContain('❌ 2 vulnerable package(s)')
|
||||
@@ -159,6 +180,7 @@ test('uses checkmarks for license issues if only vulnerabilities were found', ()
|
||||
summary.addSummaryToSummary(
|
||||
vulnerabilities,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
@@ -178,7 +200,12 @@ test('uses checkmarks for vulnerabilities if only license issues were found', ()
|
||||
unlicensed: []
|
||||
}
|
||||
|
||||
summary.addSummaryToSummary(emptyChanges, licenseIssues, defaultConfig)
|
||||
summary.addSummaryToSummary(
|
||||
emptyChanges,
|
||||
licenseIssues,
|
||||
emptyChanges,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
const text = core.summary.stringify()
|
||||
expect(text).toContain('✅ 0 vulnerable package(s)')
|
||||
|
||||
@@ -47,6 +47,12 @@ inputs:
|
||||
comment-summary-in-pr:
|
||||
description: Determines if the summary is posted as a comment in the PR itself. Setting this to `always` or `on-failure` requires you to give the workflow the write permissions for pull-requests
|
||||
required: false
|
||||
deny-packages:
|
||||
description: A comma-separated list of package URLs to deny (e.g. "pkg:npm/express, pkg:pip/pycrypto")
|
||||
required: false
|
||||
deny-groups:
|
||||
description: A comma-separated list of package URLs for group(s)/namespace(s) to deny (e.g. "pkg:npm/express, pkg:pip/pycrypto")
|
||||
required: false
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'dist/index.js'
|
||||
|
||||
181
dist/index.js
generated
vendored
181
dist/index.js
generated
vendored
@@ -135,6 +135,84 @@ function findCommentByMarker(commentBodyIncludes) {
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 2134:
|
||||
/***/ (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;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.getDeniedChanges = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
function getDeniedChanges(changes, deniedPackages, deniedGroups) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const changesDenied = [];
|
||||
let failed = false;
|
||||
for (const change of changes) {
|
||||
change.name = change.name.toLowerCase();
|
||||
const packageUrl = change.package_url.toLowerCase().split('@')[0];
|
||||
if (deniedPackages) {
|
||||
for (const denied of deniedPackages) {
|
||||
if (packageUrl === denied.split('@')[0].toLowerCase()) {
|
||||
changesDenied.push(change);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deniedGroups) {
|
||||
for (const denied of deniedGroups) {
|
||||
if (packageUrl.startsWith(denied.toLowerCase())) {
|
||||
changesDenied.push(change);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
core.setFailed('Dependency review detected denied packages.');
|
||||
}
|
||||
else {
|
||||
core.info('Dependency review did not detect any denied packages');
|
||||
}
|
||||
return changesDenied;
|
||||
});
|
||||
}
|
||||
exports.getDeniedChanges = getDeniedChanges;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 4966:
|
||||
@@ -485,6 +563,7 @@ const summary = __importStar(__nccwpck_require__(8608));
|
||||
const git_refs_1 = __nccwpck_require__(1086);
|
||||
const utils_1 = __nccwpck_require__(918);
|
||||
const comment_pr_1 = __nccwpck_require__(5842);
|
||||
const deny_1 = __nccwpck_require__(2134);
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
@@ -513,7 +592,10 @@ function run() {
|
||||
deny: config.deny_licenses,
|
||||
licenseExclusions: config.allow_dependencies_licenses
|
||||
});
|
||||
summary.addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, config);
|
||||
core.debug(`Filtered Changes: ${JSON.stringify(filteredChanges)}`);
|
||||
core.debug(`Config Deny Packages: ${JSON.stringify(config)}`);
|
||||
const deniedChanges = yield (0, deny_1.getDeniedChanges)(filteredChanges, config.deny_packages, config.deny_groups);
|
||||
summary.addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedChanges, config);
|
||||
if (snapshot_warnings) {
|
||||
summary.addSnapshotWarnings(snapshot_warnings);
|
||||
}
|
||||
@@ -525,6 +607,10 @@ function run() {
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config);
|
||||
printLicensesBlock(invalidLicenseChanges);
|
||||
}
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
summary.addDeniedToSummary(deniedChanges);
|
||||
printDeniedDependencies(deniedChanges, config);
|
||||
}
|
||||
summary.addScannedDependencies(changes);
|
||||
printScannedDependencies(changes);
|
||||
if (config.comment_summary_in_pr === 'always' ||
|
||||
@@ -642,6 +728,17 @@ function printScannedDependencies(changes) {
|
||||
}
|
||||
}));
|
||||
}
|
||||
function printDeniedDependencies(changes, config) {
|
||||
core.group('Denied', () => __awaiter(this, void 0, void 0, function* () {
|
||||
for (const denied of config.deny_packages) {
|
||||
core.info(`Config: ${denied}`);
|
||||
}
|
||||
for (const change of changes) {
|
||||
core.info(`Change: ${change.name}@${change.version} is denied`);
|
||||
core.info(`Change: ${change.package_url} is denied`);
|
||||
}
|
||||
}));
|
||||
}
|
||||
run();
|
||||
|
||||
|
||||
@@ -676,10 +773,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.ComparisonResponseSchema = exports.ChangesSchema = exports.ConfigurationOptionsSchema = exports.PullRequestSchema = exports.ChangeSchema = exports.SeveritySchema = exports.SCOPES = exports.SEVERITIES = void 0;
|
||||
exports.ComparisonResponseSchema = exports.ChangesSchema = exports.ConfigurationOptionsSchema = exports.PullRequestSchema = exports.ChangeSchema = exports.SeveritySchema = exports.COMMENT_SUMMARY_OPTIONS = exports.SCOPES = exports.SEVERITIES = void 0;
|
||||
const z = __importStar(__nccwpck_require__(3301));
|
||||
exports.SEVERITIES = ['critical', 'high', 'moderate', 'low'];
|
||||
exports.SCOPES = ['unknown', 'runtime', 'development'];
|
||||
exports.COMMENT_SUMMARY_OPTIONS = [
|
||||
'always',
|
||||
'never',
|
||||
'on-failure'
|
||||
];
|
||||
exports.SeveritySchema = z.enum(exports.SEVERITIES).default('low');
|
||||
exports.ChangeSchema = z.object({
|
||||
change_type: z.enum(['added', 'removed']),
|
||||
@@ -714,12 +816,25 @@ exports.ConfigurationOptionsSchema = z
|
||||
deny_licenses: z.array(z.string()).optional(),
|
||||
allow_dependencies_licenses: z.array(z.string()).optional(),
|
||||
allow_ghsas: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(z.string()).default([]),
|
||||
deny_groups: z.array(z.string()).default([]),
|
||||
license_check: z.boolean().default(true),
|
||||
vulnerability_check: z.boolean().default(true),
|
||||
config_file: z.string().optional(),
|
||||
base_ref: z.string().optional(),
|
||||
head_ref: z.string().optional(),
|
||||
comment_summary_in_pr: z.enum(['always', 'never', 'on-failure']).default('never'),
|
||||
comment_summary_in_pr: z
|
||||
.union([z.boolean(), z.enum(exports.COMMENT_SUMMARY_OPTIONS)])
|
||||
.default('never')
|
||||
})
|
||||
.transform(config => {
|
||||
if (config.comment_summary_in_pr === true) {
|
||||
config.comment_summary_in_pr = 'always';
|
||||
}
|
||||
else if (config.comment_summary_in_pr === false) {
|
||||
config.comment_summary_in_pr = 'never';
|
||||
}
|
||||
return config;
|
||||
})
|
||||
.superRefine((config, context) => {
|
||||
if (config.allow_licenses && config.deny_licenses) {
|
||||
@@ -780,7 +895,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.addSnapshotWarnings = exports.addScannedDependencies = exports.addLicensesToSummary = exports.addChangeVulnerabilitiesToSummary = exports.addSummaryToSummary = void 0;
|
||||
exports.addDeniedToSummary = exports.addSnapshotWarnings = exports.addScannedDependencies = exports.addLicensesToSummary = exports.addChangeVulnerabilitiesToSummary = exports.addSummaryToSummary = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const utils_1 = __nccwpck_require__(918);
|
||||
const icons = {
|
||||
@@ -788,10 +903,11 @@ const icons = {
|
||||
cross: '❌',
|
||||
warning: '⚠️'
|
||||
};
|
||||
function addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, config) {
|
||||
function addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedChanges, config) {
|
||||
core.summary.addHeading('Dependency Review', 1);
|
||||
if (vulnerableChanges.length === 0 &&
|
||||
countLicenseIssues(invalidLicenseChanges) === 0) {
|
||||
countLicenseIssues(invalidLicenseChanges) === 0 &&
|
||||
deniedChanges.length === 0) {
|
||||
if (!config.license_check) {
|
||||
core.summary.addRaw(`${icons.check} No vulnerabilities found.`);
|
||||
}
|
||||
@@ -817,6 +933,11 @@ function addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, config) {
|
||||
`${checkOrFailIcon(invalidLicenseChanges.unresolved.length)} ${invalidLicenseChanges.unresolved.length} package(s) with invalid SPDX license definitions`,
|
||||
`${checkOrWarnIcon(invalidLicenseChanges.unlicensed.length)} ${invalidLicenseChanges.unlicensed.length} package(s) with unknown licenses.`
|
||||
]
|
||||
: []),
|
||||
...(deniedChanges.length > 0
|
||||
? [
|
||||
`${checkOrWarnIcon(deniedChanges.length)} ${deniedChanges.length} package(s) denied.`
|
||||
]
|
||||
: [])
|
||||
])
|
||||
.addRaw('See the Details below.');
|
||||
@@ -958,6 +1079,24 @@ exports.addSnapshotWarnings = addSnapshotWarnings;
|
||||
function countLicenseIssues(invalidLicenseChanges) {
|
||||
return Object.values(invalidLicenseChanges).reduce((acc, val) => acc + val.length, 0);
|
||||
}
|
||||
function addDeniedToSummary(deniedChanges) {
|
||||
if (deniedChanges.length === 0) {
|
||||
return;
|
||||
}
|
||||
core.summary.addHeading('Denied dependencies', 2);
|
||||
for (const change of deniedChanges) {
|
||||
core.summary.addHeading(`<em>Denied dependencies</em>`, 4);
|
||||
core.summary.addTable([
|
||||
['Package', 'Version', 'License'],
|
||||
[
|
||||
(0, utils_1.renderUrl)(change.source_repository_url, change.name),
|
||||
change.version,
|
||||
change.license || ''
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
exports.addDeniedToSummary = addDeniedToSummary;
|
||||
function checkOrFailIcon(count) {
|
||||
return count === 0 ? icons.check : icons.cross;
|
||||
}
|
||||
@@ -47823,6 +47962,8 @@ function readInlineConfig() {
|
||||
const allow_licenses = parseList(getOptionalInput('allow-licenses'));
|
||||
const deny_licenses = parseList(getOptionalInput('deny-licenses'));
|
||||
const allow_dependencies_licenses = parseList(getOptionalInput('allow-dependencies-licenses'));
|
||||
const deny_packages = parseList(getOptionalInput('deny-packages'));
|
||||
const deny_groups = parseList(getOptionalInput('deny-groups'));
|
||||
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'));
|
||||
const license_check = getOptionalBoolean('license-check');
|
||||
const vulnerability_check = getOptionalBoolean('vulnerability-check');
|
||||
@@ -47837,6 +47978,8 @@ function readInlineConfig() {
|
||||
fail_on_scopes,
|
||||
allow_licenses,
|
||||
deny_licenses,
|
||||
deny_packages,
|
||||
deny_groups,
|
||||
allow_dependencies_licenses,
|
||||
allow_ghsas,
|
||||
license_check,
|
||||
@@ -47907,7 +48050,9 @@ function parseConfigFile(configData) {
|
||||
'deny-licenses',
|
||||
'fail-on-scopes',
|
||||
'allow-ghsas',
|
||||
'allow-dependencies-licenses'
|
||||
'allow-dependencies-licenses',
|
||||
'deny-packages',
|
||||
'deny-groups'
|
||||
];
|
||||
for (const key of Object.keys(data)) {
|
||||
// strings can contain list values (e.g. 'MIT, Apache-2.0'). In this
|
||||
@@ -48083,10 +48228,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.ComparisonResponseSchema = exports.ChangesSchema = exports.ConfigurationOptionsSchema = exports.PullRequestSchema = exports.ChangeSchema = exports.SeveritySchema = exports.SCOPES = exports.SEVERITIES = void 0;
|
||||
exports.ComparisonResponseSchema = exports.ChangesSchema = exports.ConfigurationOptionsSchema = exports.PullRequestSchema = exports.ChangeSchema = exports.SeveritySchema = exports.COMMENT_SUMMARY_OPTIONS = exports.SCOPES = exports.SEVERITIES = void 0;
|
||||
const z = __importStar(__nccwpck_require__(3301));
|
||||
exports.SEVERITIES = ['critical', 'high', 'moderate', 'low'];
|
||||
exports.SCOPES = ['unknown', 'runtime', 'development'];
|
||||
exports.COMMENT_SUMMARY_OPTIONS = [
|
||||
'always',
|
||||
'never',
|
||||
'on-failure'
|
||||
];
|
||||
exports.SeveritySchema = z.enum(exports.SEVERITIES).default('low');
|
||||
exports.ChangeSchema = z.object({
|
||||
change_type: z.enum(['added', 'removed']),
|
||||
@@ -48121,12 +48271,25 @@ exports.ConfigurationOptionsSchema = z
|
||||
deny_licenses: z.array(z.string()).optional(),
|
||||
allow_dependencies_licenses: z.array(z.string()).optional(),
|
||||
allow_ghsas: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(z.string()).default([]),
|
||||
deny_groups: z.array(z.string()).default([]),
|
||||
license_check: z.boolean().default(true),
|
||||
vulnerability_check: z.boolean().default(true),
|
||||
config_file: z.string().optional(),
|
||||
base_ref: z.string().optional(),
|
||||
head_ref: z.string().optional(),
|
||||
comment_summary_in_pr: z.enum(['always', 'never', 'on-failure']).default('never'),
|
||||
comment_summary_in_pr: z
|
||||
.union([z.boolean(), z.enum(exports.COMMENT_SUMMARY_OPTIONS)])
|
||||
.default('never')
|
||||
})
|
||||
.transform(config => {
|
||||
if (config.comment_summary_in_pr === true) {
|
||||
config.comment_summary_in_pr = 'always';
|
||||
}
|
||||
else if (config.comment_summary_in_pr === false) {
|
||||
config.comment_summary_in_pr = 'never';
|
||||
}
|
||||
return config;
|
||||
})
|
||||
.superRefine((config, context) => {
|
||||
if (config.allow_licenses && config.deny_licenses) {
|
||||
|
||||
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -230,3 +230,31 @@ jobs:
|
||||
comment-summary-in-pr: always
|
||||
license-check: false
|
||||
```
|
||||
|
||||
## Exclude dependencies from their name or groups
|
||||
|
||||
Using the `deny-packages` option you can exclude dependencies by their PURL. You can add multiple values separated by a commas.
|
||||
Using the `deny-groups` option you can exclude dependencies by their group name/namespace. You can add multiple values separated by a comma.
|
||||
|
||||
In this example, we are excluding `pkg:maven/org.apache.logging.log4j:log4j-api` and `pkg:maven/org.apache.logging.log4j/log4j-core` from `maven` and all packages in the group `pkg:maven/com.bazaarvoice.maven`
|
||||
|
||||
```yaml
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
with:
|
||||
deny-packages: 'pkg:maven/org.apache.logging.log4j/log4j-api,pkg:maven/org.apache.logging.log4j/log4j-core'
|
||||
deny-groups: 'pkg:maven/com.bazaarvoice.jolt'
|
||||
```
|
||||
102
package-lock.json
generated
102
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "dependency-review-action",
|
||||
"version": "3.0.6",
|
||||
"version": "3.0.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "dependency-review-action",
|
||||
"version": "3.0.6",
|
||||
"version": "3.0.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
@@ -28,7 +28,7 @@
|
||||
"@types/spdx-expression-parse": "^3.0.2",
|
||||
"@types/spdx-satisfies": "^0.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.1",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"esbuild-register": "^3.4.2",
|
||||
"eslint": "^8.44.0",
|
||||
@@ -38,7 +38,7 @@
|
||||
"jest": "^27.5.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"prettier": "3.0.0",
|
||||
"prettier": "3.0.1",
|
||||
"ts-jest": "^27.1.4",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
@@ -2378,15 +2378,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.2.0.tgz",
|
||||
"integrity": "sha512-igVYOqtiK/UsvKAmmloQAruAdUHihsOCvplJpplPZ+3h4aDkC/UKZZNKgB6h93ayuYLuEymU3h8nF1xMRbh37g==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.2.1.tgz",
|
||||
"integrity": "sha512-Ld+uL1kYFU8e6btqBFpsHkwQ35rw30IWpdQxgOqOh4NfxSDH6uCkah1ks8R/RgQqI5hHPXMaLy9fbFseIe+dIg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "6.2.0",
|
||||
"@typescript-eslint/types": "6.2.0",
|
||||
"@typescript-eslint/typescript-estree": "6.2.0",
|
||||
"@typescript-eslint/visitor-keys": "6.2.0",
|
||||
"@typescript-eslint/scope-manager": "6.2.1",
|
||||
"@typescript-eslint/types": "6.2.1",
|
||||
"@typescript-eslint/typescript-estree": "6.2.1",
|
||||
"@typescript-eslint/visitor-keys": "6.2.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2405,6 +2405,80 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.1.tgz",
|
||||
"integrity": "sha512-UCqBF9WFqv64xNsIEPfBtenbfodPXsJ3nPAr55mGPkQIkiQvgoWNo+astj9ZUfJfVKiYgAZDMnM6dIpsxUMp3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.2.1",
|
||||
"@typescript-eslint/visitor-keys": "6.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.2.1.tgz",
|
||||
"integrity": "sha512-528bGcoelrpw+sETlyM91k51Arl2ajbNT9L4JwoXE2dvRe1yd8Q64E4OL7vHYw31mlnVsf+BeeLyAZUEQtqahQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.1.tgz",
|
||||
"integrity": "sha512-G+UJeQx9AKBHRQBpmvr8T/3K5bJa485eu+4tQBxFq0KoT22+jJyzo1B50JDT9QdC1DEmWQfdKsa8ybiNWYsi0Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.2.1",
|
||||
"@typescript-eslint/visitor-keys": "6.2.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.1.tgz",
|
||||
"integrity": "sha512-iTN6w3k2JEZ7cyVdZJTVJx2Lv7t6zFA8DCrJEHD2mwfc16AEvvBWVhbFh34XyG2NORCd0viIgQY1+u7kPI0WpA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.2.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.0.tgz",
|
||||
@@ -7486,9 +7560,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz",
|
||||
"integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
|
||||
"integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dependency-review-action",
|
||||
"version": "3.0.6",
|
||||
"version": "3.0.7",
|
||||
"private": true,
|
||||
"description": "A GitHub Action for Dependency Review",
|
||||
"main": "lib/main.js",
|
||||
@@ -44,7 +44,7 @@
|
||||
"@types/spdx-expression-parse": "^3.0.2",
|
||||
"@types/spdx-satisfies": "^0.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.1",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"esbuild-register": "^3.4.2",
|
||||
"eslint": "^8.44.0",
|
||||
@@ -54,7 +54,7 @@
|
||||
"jest": "^27.5.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"prettier": "3.0.0",
|
||||
"prettier": "3.0.1",
|
||||
"ts-jest": "^27.1.4",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* npx ts-node scripts/create_summary.ts
|
||||
*/
|
||||
|
||||
import {Changes, ConfigurationOptions} from '../src/schemas'
|
||||
import {Change, Changes, ConfigurationOptions} from '../src/schemas'
|
||||
import {createTestChange} from '../__tests__/fixtures/create-test-change'
|
||||
import {InvalidLicenseChanges} from '../src/licenses'
|
||||
import * as fs from 'fs'
|
||||
@@ -22,6 +22,8 @@ const defaultConfig: ConfigurationOptions = {
|
||||
allow_ghsas: [],
|
||||
allow_licenses: ['MIT'],
|
||||
deny_licenses: [],
|
||||
deny_packages: [],
|
||||
deny_groups: [],
|
||||
allow_dependencies_licenses: [
|
||||
'pkg:npm/express@4.17.1',
|
||||
'pkg:pip/requests',
|
||||
@@ -44,6 +46,7 @@ const createNonIssueSummary = async (): Promise<void> => {
|
||||
await createSummary(
|
||||
[],
|
||||
{forbidden: [], unresolved: [], unlicensed: []},
|
||||
[],
|
||||
defaultConfig,
|
||||
'non-issue-summary.md'
|
||||
)
|
||||
@@ -85,16 +88,17 @@ const createFullSummary = async (): Promise<void> => {
|
||||
]
|
||||
}
|
||||
|
||||
await createSummary(changes, licenses, defaultConfig, 'full-summary.md')
|
||||
await createSummary(changes, licenses, [], defaultConfig, 'full-summary.md')
|
||||
}
|
||||
|
||||
async function createSummary(
|
||||
vulnerabilities: Changes,
|
||||
licenseIssues: InvalidLicenseChanges,
|
||||
denied: Change[],
|
||||
config: ConfigurationOptions,
|
||||
fileName: string
|
||||
): Promise<void> {
|
||||
summary.addSummaryToSummary(vulnerabilities, licenseIssues, config)
|
||||
summary.addSummaryToSummary(vulnerabilities, licenseIssues, denied, config)
|
||||
summary.addChangeVulnerabilitiesToSummary(
|
||||
vulnerabilities,
|
||||
config.fail_on_severity
|
||||
|
||||
@@ -33,6 +33,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
const allow_dependencies_licenses = parseList(
|
||||
getOptionalInput('allow-dependencies-licenses')
|
||||
)
|
||||
const deny_packages = parseList(getOptionalInput('deny-packages'))
|
||||
const deny_groups = parseList(getOptionalInput('deny-groups'))
|
||||
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'))
|
||||
const license_check = getOptionalBoolean('license-check')
|
||||
const vulnerability_check = getOptionalBoolean('vulnerability-check')
|
||||
@@ -49,6 +51,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
fail_on_scopes,
|
||||
allow_licenses,
|
||||
deny_licenses,
|
||||
deny_packages,
|
||||
deny_groups,
|
||||
allow_dependencies_licenses,
|
||||
allow_ghsas,
|
||||
license_check,
|
||||
@@ -137,7 +141,9 @@ function parseConfigFile(configData: string): ConfigurationOptionsPartial {
|
||||
'deny-licenses',
|
||||
'fail-on-scopes',
|
||||
'allow-ghsas',
|
||||
'allow-dependencies-licenses'
|
||||
'allow-dependencies-licenses',
|
||||
'deny-packages',
|
||||
'deny-groups'
|
||||
]
|
||||
|
||||
for (const key of Object.keys(data)) {
|
||||
|
||||
42
src/deny.ts
Normal file
42
src/deny.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import {Change} from './schemas'
|
||||
import * as core from '@actions/core'
|
||||
|
||||
export async function getDeniedChanges(
|
||||
changes: Change[],
|
||||
deniedPackages: string[],
|
||||
deniedGroups: string[]
|
||||
): Promise<Change[]> {
|
||||
const changesDenied: Change[] = []
|
||||
|
||||
let failed = false
|
||||
for (const change of changes) {
|
||||
change.name = change.name.toLowerCase()
|
||||
const packageUrl = change.package_url.toLowerCase().split('@')[0]
|
||||
|
||||
if (deniedPackages) {
|
||||
for (const denied of deniedPackages) {
|
||||
if (packageUrl === denied.split('@')[0].toLowerCase()) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deniedGroups) {
|
||||
for (const denied of deniedGroups) {
|
||||
if (packageUrl.startsWith(denied.toLowerCase())) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
core.setFailed('Dependency review detected denied packages.')
|
||||
} else {
|
||||
core.info('Dependency review did not detect any denied packages')
|
||||
}
|
||||
|
||||
return changesDenied
|
||||
}
|
||||
33
src/main.ts
33
src/main.ts
@@ -3,7 +3,7 @@ import * as dependencyGraph from './dependency-graph'
|
||||
import * as github from '@actions/github'
|
||||
import styles from 'ansi-styles'
|
||||
import {RequestError} from '@octokit/request-error'
|
||||
import {Change, Severity, Changes} from './schemas'
|
||||
import {Change, Severity, Changes, ConfigurationOptions} from './schemas'
|
||||
import {readConfig} from '../src/config'
|
||||
import {
|
||||
filterChangesBySeverity,
|
||||
@@ -16,6 +16,7 @@ import {getRefs} from './git-refs'
|
||||
|
||||
import {groupDependenciesByManifest} from './utils'
|
||||
import {commentPr} from './comment-pr'
|
||||
import {getDeniedChanges} from './deny'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
@@ -63,9 +64,19 @@ async function run(): Promise<void> {
|
||||
}
|
||||
)
|
||||
|
||||
core.debug(`Filtered Changes: ${JSON.stringify(filteredChanges)}`)
|
||||
core.debug(`Config Deny Packages: ${JSON.stringify(config)}`)
|
||||
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
filteredChanges,
|
||||
config.deny_packages,
|
||||
config.deny_groups
|
||||
)
|
||||
|
||||
summary.addSummaryToSummary(
|
||||
vulnerableChanges,
|
||||
invalidLicenseChanges,
|
||||
deniedChanges,
|
||||
config
|
||||
)
|
||||
|
||||
@@ -81,6 +92,10 @@ async function run(): Promise<void> {
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config)
|
||||
printLicensesBlock(invalidLicenseChanges)
|
||||
}
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
summary.addDeniedToSummary(deniedChanges)
|
||||
printDeniedDependencies(deniedChanges, config)
|
||||
}
|
||||
|
||||
summary.addScannedDependencies(changes)
|
||||
printScannedDependencies(changes)
|
||||
@@ -243,4 +258,20 @@ function printScannedDependencies(changes: Changes): void {
|
||||
})
|
||||
}
|
||||
|
||||
function printDeniedDependencies(
|
||||
changes: Change[],
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.group('Denied', async () => {
|
||||
for (const denied of config.deny_packages) {
|
||||
core.info(`Config: ${denied}`)
|
||||
}
|
||||
|
||||
for (const change of changes) {
|
||||
core.info(`Change: ${change.name}@${change.version} is denied`)
|
||||
core.info(`Change: ${change.package_url} is denied`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
@@ -47,6 +47,8 @@ export const ConfigurationOptionsSchema = z
|
||||
deny_licenses: z.array(z.string()).optional(),
|
||||
allow_dependencies_licenses: z.array(z.string()).optional(),
|
||||
allow_ghsas: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(z.string()).default([]),
|
||||
deny_groups: z.array(z.string()).default([]),
|
||||
license_check: z.boolean().default(true),
|
||||
vulnerability_check: z.boolean().default(true),
|
||||
config_file: z.string().optional(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as core from '@actions/core'
|
||||
import {ConfigurationOptions, Changes} from './schemas'
|
||||
import {ConfigurationOptions, Changes, Change} from './schemas'
|
||||
import {SummaryTableRow} from '@actions/core/lib/summary'
|
||||
import {InvalidLicenseChanges, InvalidLicenseChangeTypes} from './licenses'
|
||||
import {groupDependenciesByManifest, getManifestsSet, renderUrl} from './utils'
|
||||
@@ -13,13 +13,15 @@ const icons = {
|
||||
export function addSummaryToSummary(
|
||||
vulnerableChanges: Changes,
|
||||
invalidLicenseChanges: InvalidLicenseChanges,
|
||||
deniedChanges: Changes,
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.summary.addHeading('Dependency Review', 1)
|
||||
|
||||
if (
|
||||
vulnerableChanges.length === 0 &&
|
||||
countLicenseIssues(invalidLicenseChanges) === 0
|
||||
countLicenseIssues(invalidLicenseChanges) === 0 &&
|
||||
deniedChanges.length === 0
|
||||
) {
|
||||
if (!config.license_check) {
|
||||
core.summary.addRaw(`${icons.check} No vulnerabilities found.`)
|
||||
@@ -56,6 +58,13 @@ export function addSummaryToSummary(
|
||||
invalidLicenseChanges.unlicensed.length
|
||||
} package(s) with unknown licenses.`
|
||||
]
|
||||
: []),
|
||||
...(deniedChanges.length > 0
|
||||
? [
|
||||
`${checkOrWarnIcon(deniedChanges.length)} ${
|
||||
deniedChanges.length
|
||||
} package(s) denied.`
|
||||
]
|
||||
: [])
|
||||
])
|
||||
.addRaw('See the Details below.')
|
||||
@@ -248,6 +257,25 @@ function countLicenseIssues(
|
||||
)
|
||||
}
|
||||
|
||||
export function addDeniedToSummary(deniedChanges: Change[]): void {
|
||||
if (deniedChanges.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
core.summary.addHeading('Denied dependencies', 2)
|
||||
for (const change of deniedChanges) {
|
||||
core.summary.addHeading(`<em>Denied dependencies</em>`, 4)
|
||||
core.summary.addTable([
|
||||
['Package', 'Version', 'License'],
|
||||
[
|
||||
renderUrl(change.source_repository_url, change.name),
|
||||
change.version,
|
||||
change.license || ''
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
function checkOrFailIcon(count: number): string {
|
||||
return count === 0 ? icons.check : icons.cross
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user