add tests and docs
This commit is contained in:
24
README.md
24
README.md
@@ -66,18 +66,20 @@ jobs:
|
||||
|
||||
Configure this action by either inlining these options in your workflow file, or by using an external configuration file. All configuration options are optional.
|
||||
|
||||
| Option | Usage | Possible values | Default value |
|
||||
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------- |
|
||||
| `fail-on-severity` | Defines the threshold for the level of severity. The action will fail on any pull requests that introduce vulnerabilities of the specified severity level or higher. | `low`, `moderate`, `high`, `critical` | `low` |
|
||||
| `allow-licenses`\* | Contains a list of allowed licenses. The action will fail on pull requests that introduce dependencies with licenses that do not match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
|
||||
| `deny-licenses`\* | Contains a list of prohibited licenses. The action will fail on pull requests that introduce dependencies with licenses that match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
|
||||
| Option | Usage | Possible values | Default value |
|
||||
|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|---------------|
|
||||
| `fail-on-severity` | Defines the threshold for the level of severity. The action will fail on any pull requests that introduce vulnerabilities of the specified severity level or higher. | `low`, `moderate`, `high`, `critical` | `low` |
|
||||
| `allow-licenses`\* | Contains a list of allowed licenses. The action will fail on pull requests that introduce dependencies with licenses that do not match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
|
||||
| `deny-licenses`\* | Contains a list of prohibited licenses. The action will fail on pull requests that introduce dependencies with licenses that match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
|
||||
| `fail-on-scopes`† | Contains a list of strings of the build environments you want to support. The action will fail on pull requests that introduce vulnerabilities in the scopes that match the list. | `runtime`, `development`, `unknown` | `runtime` |
|
||||
| `allow-ghsas` | Contains a list of GitHub Advisory Database IDs that can be skipped during detection. | Any GHSAs from the [GitHub Advisory Database](https://github.com/advisories) | none |
|
||||
| `license-check` | Enable or disable the license check performed by the action. | `true`, `false` | `true` |
|
||||
| `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` |
|
||||
| `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`. | `true`, `false` | `false` |
|
||||
| `allow-ghsas` | Contains a list of GitHub Advisory Database IDs that can be skipped during detection. | Any GHSAs from the [GitHub Advisory Database](https://github.com/advisories) | none |
|
||||
| `license-check` | Enable or disable the license check performed by the action. | `true`, `false` | `true` |
|
||||
| `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` |
|
||||
| `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`. | `true`, `false` | `false` |
|
||||
| `deny-packages` | Contains a list of denied package's name. | Any packages complete names | empty |
|
||||
| `deny-groups` | Contains a list of denied groups package's name. | Any packages's group names | empty |
|
||||
|
||||
\*not supported for use with GitHub Enterprise Server
|
||||
|
||||
|
||||
@@ -75,6 +75,27 @@ const pipChange: Change = {
|
||||
]
|
||||
}
|
||||
|
||||
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:org.apache.logging.log4j:log4j-core@1.1.1',
|
||||
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 = {
|
||||
@@ -106,13 +127,36 @@ beforeEach(async () => {
|
||||
return jest.fn((license: string, _: string): boolean => license === 'BSD')
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
;({getDeniedChanges} = require('../src/denylist'))
|
||||
;({getDeniedChanges} = require('../src/deny'))
|
||||
})
|
||||
|
||||
test('it adds license outside the allow list to forbidden changes', async () => {
|
||||
test('it adds packages in the deny packages list', async () => {
|
||||
const changes: Changes = [npmChange, rubyChange]
|
||||
const deniedChanges = await getDeniedChanges(changes, ['actionsomething'])
|
||||
const deniedChanges = await getDeniedChanges(changes, ['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,
|
||||
[],
|
||||
['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,
|
||||
['actionsomething'],
|
||||
['org.apache.logging.log4j']
|
||||
)
|
||||
|
||||
expect(deniedChanges.length).toEqual(0)
|
||||
})
|
||||
@@ -24,7 +24,8 @@ const defaultConfig: ConfigurationOptions = {
|
||||
allow_ghsas: [],
|
||||
allow_licenses: [],
|
||||
deny_licenses: [],
|
||||
deny_list: [],
|
||||
deny_packages: [],
|
||||
deny_groups: [],
|
||||
comment_summary_in_pr: true
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,11 @@ inputs:
|
||||
comment-summary-in-pr:
|
||||
description: A boolean to determine if the report should be posted as a comment in the PR itself. Setting this to true requires you to give the workflow the write permissions for pull-requests
|
||||
required: false
|
||||
deny-list:
|
||||
description: A comma-separated list of dependencies to deny (e.g. "pkg:npm/express, pkg:pip/pycrypto")
|
||||
deny-packages:
|
||||
description: A comma-separated list of packages to deny (e.g. "pkg:npm/express, pkg:pip/pycrypto")
|
||||
required: false
|
||||
deny-groups:
|
||||
description: A comma-separated list of groups of packages to deny (e.g. "pkg:npm/express, pkg:pip/pycrypto")
|
||||
required: false
|
||||
runs:
|
||||
using: 'node16'
|
||||
|
||||
81
dist/index.js
generated
vendored
81
dist/index.js
generated
vendored
@@ -137,11 +137,34 @@ function findCommentByMarker(commentBodyIncludes) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 3491:
|
||||
/***/ (function(__unused_webpack_module, exports) {
|
||||
/***/ 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) {
|
||||
@@ -153,17 +176,36 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.getDeniedChanges = void 0;
|
||||
function getDeniedChanges(changes, deniedList) {
|
||||
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();
|
||||
change.package_url = change.package_url.toLowerCase();
|
||||
for (const denied of deniedList) {
|
||||
if (change.name.includes(denied)) {
|
||||
changesDenied.push(change);
|
||||
if (deniedPackages) {
|
||||
for (const denied of deniedPackages) {
|
||||
if (change.name === denied.toLowerCase()) {
|
||||
changesDenied.push(change);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deniedGroups) {
|
||||
for (const denied of deniedGroups) {
|
||||
if (change.name.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;
|
||||
});
|
||||
@@ -521,7 +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 denylist_1 = __nccwpck_require__(3491);
|
||||
const deny_1 = __nccwpck_require__(2134);
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
@@ -550,10 +592,7 @@ function run() {
|
||||
deny: config.deny_licenses,
|
||||
licenseExclusions: config.allow_dependencies_licenses
|
||||
});
|
||||
const deniedChanges = yield (0, denylist_1.getDeniedChanges)(filteredChanges, config.deny_list);
|
||||
core.debug(`config: ${JSON.stringify(config)}`);
|
||||
core.debug(`filteredChanges: ${JSON.stringify(filteredChanges)}`);
|
||||
core.debug(`deniedChanges: ${JSON.stringify(deniedChanges)}`);
|
||||
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);
|
||||
@@ -566,7 +605,7 @@ function run() {
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config);
|
||||
printLicensesBlock(invalidLicenseChanges);
|
||||
}
|
||||
if (config.deny_list) {
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
summary.addDeniedToSummary(deniedChanges);
|
||||
printDeniedDependencies(deniedChanges, config);
|
||||
}
|
||||
@@ -687,7 +726,7 @@ function printScannedDependencies(changes) {
|
||||
}
|
||||
function printDeniedDependencies(changes, config) {
|
||||
core.group('Denied', () => __awaiter(this, void 0, void 0, function* () {
|
||||
for (const denied of config.deny_list) {
|
||||
for (const denied of config.deny_packages) {
|
||||
core.info(`Config: ${denied}`);
|
||||
}
|
||||
for (const change of changes) {
|
||||
@@ -768,7 +807,8 @@ 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_list: 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(),
|
||||
@@ -47902,7 +47942,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_list = parseList(getOptionalInput('deny-list'));
|
||||
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');
|
||||
@@ -47917,7 +47958,8 @@ function readInlineConfig() {
|
||||
fail_on_scopes,
|
||||
allow_licenses,
|
||||
deny_licenses,
|
||||
deny_list,
|
||||
deny_packages,
|
||||
deny_groups,
|
||||
allow_dependencies_licenses,
|
||||
allow_ghsas,
|
||||
license_check,
|
||||
@@ -47988,7 +48030,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
|
||||
@@ -48202,7 +48246,8 @@ 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_list: 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(),
|
||||
|
||||
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
@@ -22,7 +22,8 @@ const defaultConfig: ConfigurationOptions = {
|
||||
allow_ghsas: [],
|
||||
allow_licenses: ['MIT'],
|
||||
deny_licenses: [],
|
||||
deny_list: [],
|
||||
deny_packages: [],
|
||||
deny_groups: [],
|
||||
allow_dependencies_licenses: [
|
||||
'pkg:npm/express@4.17.1',
|
||||
'pkg:pip/requests',
|
||||
|
||||
@@ -33,7 +33,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
const allow_dependencies_licenses = parseList(
|
||||
getOptionalInput('allow-dependencies-licenses')
|
||||
)
|
||||
const deny_list = parseList(getOptionalInput('deny-list'))
|
||||
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')
|
||||
@@ -50,7 +51,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
fail_on_scopes,
|
||||
allow_licenses,
|
||||
deny_licenses,
|
||||
deny_list,
|
||||
deny_packages,
|
||||
deny_groups,
|
||||
allow_dependencies_licenses,
|
||||
allow_ghsas,
|
||||
license_check,
|
||||
@@ -139,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)) {
|
||||
|
||||
@@ -3,7 +3,8 @@ import * as core from '@actions/core'
|
||||
|
||||
export async function getDeniedChanges(
|
||||
changes: Change[],
|
||||
deniedList: string[]
|
||||
deniedPackages: string[],
|
||||
deniedGroups: string[]
|
||||
): Promise<Change[]> {
|
||||
const changesDenied: Change[] = []
|
||||
|
||||
@@ -12,10 +13,21 @@ export async function getDeniedChanges(
|
||||
change.name = change.name.toLowerCase()
|
||||
change.package_url = change.package_url.toLowerCase()
|
||||
|
||||
for (const denied of deniedList) {
|
||||
if (change.name.includes(denied)) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
if (deniedPackages) {
|
||||
for (const denied of deniedPackages) {
|
||||
if (change.name === denied.toLowerCase()) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deniedGroups) {
|
||||
for (const denied of deniedGroups) {
|
||||
if (change.name.startsWith(denied.toLowerCase())) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/main.ts
13
src/main.ts
@@ -16,7 +16,7 @@ import {getRefs} from './git-refs'
|
||||
|
||||
import {groupDependenciesByManifest} from './utils'
|
||||
import {commentPr} from './comment-pr'
|
||||
import {getDeniedChanges} from './denylist'
|
||||
import {getDeniedChanges} from './deny'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
@@ -66,13 +66,10 @@ async function run(): Promise<void> {
|
||||
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
filteredChanges,
|
||||
config.deny_list
|
||||
config.deny_packages,
|
||||
config.deny_groups
|
||||
)
|
||||
|
||||
core.debug(`config: ${JSON.stringify(config)}`)
|
||||
core.debug(`filteredChanges: ${JSON.stringify(filteredChanges)}`)
|
||||
core.debug(`deniedChanges: ${JSON.stringify(deniedChanges)}`)
|
||||
|
||||
summary.addSummaryToSummary(
|
||||
vulnerableChanges,
|
||||
invalidLicenseChanges,
|
||||
@@ -92,7 +89,7 @@ async function run(): Promise<void> {
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config)
|
||||
printLicensesBlock(invalidLicenseChanges)
|
||||
}
|
||||
if (config.deny_list) {
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
summary.addDeniedToSummary(deniedChanges)
|
||||
printDeniedDependencies(deniedChanges, config)
|
||||
}
|
||||
@@ -259,7 +256,7 @@ function printDeniedDependencies(
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.group('Denied', async () => {
|
||||
for (const denied of config.deny_list) {
|
||||
for (const denied of config.deny_packages) {
|
||||
core.info(`Config: ${denied}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,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_list: 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(),
|
||||
|
||||
Reference in New Issue
Block a user