Merge pull request #432 from tgrall/issue-431-fail-on-severity-none
Add none as option for fail-on-severity
This commit is contained in:
35
README.md
35
README.md
@@ -66,27 +66,30 @@ 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 |
|
||||
| `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`. | `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 |
|
||||
| `retry-on-snapshot-warnings`\* | Enable or disable retrying the action every 10 seconds while waiting for dependency submission actions to complete. | `true`, `false` | `false` |
|
||||
| `retry-on-snapshot-warnings-timeout`\* | Maximum amount of time (in seconds) to retry the action while waiting for dependency submission actions to complete. | Any positive integer | 120 |
|
||||
| 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`. | `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 |
|
||||
| `retry-on-snapshot-warnings`\* | Enable or disable retrying the action every 10 seconds while waiting for dependency submission actions to complete. | `true`, `false` | `false` |
|
||||
| `retry-on-snapshot-warnings-timeout`\* | Maximum amount of time (in seconds) to retry the action while waiting for dependency submission actions to complete. | Any positive integer | 120 |
|
||||
| `warn-only`+ | When set to `true`, the action will log all vulnerabilities as warnings regardless of the severity, and the action will complete with a `success` status. This overrides the `fail-on-severity` option. | `true`, `false` | `false` |
|
||||
|
||||
\*not supported for use with GitHub Enterprise Server
|
||||
|
||||
†will be supported with GitHub Enterprise Server 3.8
|
||||
|
||||
+when `warn-only` is set to `true`, all vulnerabilities, independently of the severity, will be reported as warnings and the action will not fail.
|
||||
|
||||
### Inline Configuration
|
||||
|
||||
You can pass options to the Dependency Review GitHub Action using your workflow file.
|
||||
|
||||
@@ -26,6 +26,11 @@ test('it reads custom configs', async () => {
|
||||
expect(config.allow_licenses).toEqual(['BSD', 'GPL 2'])
|
||||
})
|
||||
|
||||
test('it defaults to false for warn-only', async () => {
|
||||
const config = await readConfig()
|
||||
expect(config.warn_only).toEqual(false)
|
||||
})
|
||||
|
||||
test('it defaults to empty allow/deny lists ', async () => {
|
||||
const config = await readConfig()
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ const defaultConfig: ConfigurationOptions = {
|
||||
deny_groups: [],
|
||||
comment_summary_in_pr: true,
|
||||
retry_on_snapshot_warnings: false,
|
||||
retry_on_snapshot_warnings_timeout: 120
|
||||
retry_on_snapshot_warnings_timeout: 120,
|
||||
warn_only: false
|
||||
}
|
||||
|
||||
const changesWithEmptyManifests: Changes = [
|
||||
|
||||
@@ -18,7 +18,8 @@ export function clearInputs(): void {
|
||||
'CONFIG-FILE',
|
||||
'BASE-REF',
|
||||
'HEAD-REF',
|
||||
'COMMENT-SUMMARY-IN-PR'
|
||||
'COMMENT-SUMMARY-IN-PR',
|
||||
'WARN-ONLY'
|
||||
]
|
||||
|
||||
// eslint-disable-next-line github/array-foreach
|
||||
|
||||
@@ -61,6 +61,11 @@ inputs:
|
||||
description: Number of seconds to wait before stopping snapshot retries.
|
||||
required: false
|
||||
default: 120
|
||||
warn-only:
|
||||
description: When set to `true` this action will always complete with success, overriding the `fail-on-severity` parameter.
|
||||
required: false
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: 'node20'
|
||||
main: 'dist/index.js'
|
||||
|
||||
48
dist/index.js
generated
vendored
48
dist/index.js
generated
vendored
@@ -609,7 +609,13 @@ function run() {
|
||||
}
|
||||
const scopedChanges = (0, filter_1.filterChangesByScopes)(config.fail_on_scopes, changes);
|
||||
const filteredChanges = (0, filter_1.filterAllowedAdvisories)(config.allow_ghsas, scopedChanges);
|
||||
const minSeverity = config.fail_on_severity;
|
||||
const failOnSeverityParams = config.fail_on_severity;
|
||||
const warnOnly = config.warn_only;
|
||||
let minSeverity = 'low';
|
||||
// If failOnSeverityParams is not set or warnOnly is true, the minSeverity is low, to allow all vulnerabilities to be reported as warnings
|
||||
if (failOnSeverityParams && !warnOnly) {
|
||||
minSeverity = failOnSeverityParams;
|
||||
}
|
||||
const vulnerableChanges = (0, filter_1.filterChangesBySeverity)(minSeverity, filteredChanges);
|
||||
const invalidLicenseChanges = yield (0, licenses_1.getInvalidLicenseChanges)(filteredChanges, {
|
||||
allow: config.allow_licenses,
|
||||
@@ -625,11 +631,11 @@ function run() {
|
||||
}
|
||||
if (config.vulnerability_check) {
|
||||
summary.addChangeVulnerabilitiesToSummary(vulnerableChanges, minSeverity);
|
||||
printVulnerabilitiesBlock(vulnerableChanges, minSeverity);
|
||||
printVulnerabilitiesBlock(vulnerableChanges, minSeverity, warnOnly);
|
||||
}
|
||||
if (config.license_check) {
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config);
|
||||
printLicensesBlock(invalidLicenseChanges);
|
||||
printLicensesBlock(invalidLicenseChanges, warnOnly);
|
||||
}
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
summary.addDeniedToSummary(deniedChanges);
|
||||
@@ -664,17 +670,23 @@ function run() {
|
||||
}
|
||||
});
|
||||
}
|
||||
function printVulnerabilitiesBlock(addedChanges, minSeverity) {
|
||||
let failed = false;
|
||||
function printVulnerabilitiesBlock(addedChanges, minSeverity, warnOnly) {
|
||||
let vulFound = false;
|
||||
core.group('Vulnerabilities', () => __awaiter(this, void 0, void 0, function* () {
|
||||
if (addedChanges.length > 0) {
|
||||
for (const change of addedChanges) {
|
||||
printChangeVulnerabilities(change);
|
||||
}
|
||||
failed = true;
|
||||
vulFound = true;
|
||||
}
|
||||
if (failed) {
|
||||
core.setFailed('Dependency review detected vulnerable packages.');
|
||||
if (vulFound) {
|
||||
const msg = 'Dependency review detected vulnerable packages.';
|
||||
if (warnOnly) {
|
||||
core.warning(msg);
|
||||
}
|
||||
else {
|
||||
core.setFailed(msg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
core.info(`Dependency review did not detect any vulnerable packages with severity level "${minSeverity}" or higher.`);
|
||||
@@ -687,12 +699,18 @@ function printChangeVulnerabilities(change) {
|
||||
core.info(` ↪ ${vuln.advisory_url}`);
|
||||
}
|
||||
}
|
||||
function printLicensesBlock(invalidLicenseChanges) {
|
||||
function printLicensesBlock(invalidLicenseChanges, warnOnly) {
|
||||
core.group('Licenses', () => __awaiter(this, void 0, void 0, function* () {
|
||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
||||
core.info('\nThe following dependencies have incompatible licenses:');
|
||||
printLicensesError(invalidLicenseChanges.forbidden);
|
||||
core.setFailed('Dependency review detected incompatible licenses.');
|
||||
const msg = 'Dependency review detected incompatible licenses.';
|
||||
if (warnOnly) {
|
||||
core.warning(msg);
|
||||
}
|
||||
else {
|
||||
core.setFailed(msg);
|
||||
}
|
||||
}
|
||||
if (invalidLicenseChanges.unresolved.length > 0) {
|
||||
core.warning('\nThe validity of the licenses of the dependencies below could not be determined. Ensure that they are valid SPDX licenses:');
|
||||
@@ -849,7 +867,8 @@ exports.ConfigurationOptionsSchema = z
|
||||
z.preprocess(val => (val === 'true' ? true : val === 'false' ? false : val), z.boolean()),
|
||||
z.enum(['always', 'never', 'on-failure'])
|
||||
])
|
||||
.default('never')
|
||||
.default('never'),
|
||||
warn_only: z.boolean().default(false)
|
||||
})
|
||||
.transform(config => {
|
||||
if (config.comment_summary_in_pr === true) {
|
||||
@@ -49005,6 +49024,7 @@ function readInlineConfig() {
|
||||
const comment_summary_in_pr = getOptionalInput('comment-summary-in-pr');
|
||||
const retry_on_snapshot_warnings = getOptionalBoolean('retry-on-snapshot-warnings');
|
||||
const retry_on_snapshot_warnings_timeout = getOptionalNumber('retry-on-snapshot-warnings-timeout');
|
||||
const warn_only = getOptionalBoolean('warn-only');
|
||||
validatePURL(allow_dependencies_licenses);
|
||||
validateLicenses('allow-licenses', allow_licenses);
|
||||
validateLicenses('deny-licenses', deny_licenses);
|
||||
@@ -49023,7 +49043,8 @@ function readInlineConfig() {
|
||||
head_ref,
|
||||
comment_summary_in_pr,
|
||||
retry_on_snapshot_warnings,
|
||||
retry_on_snapshot_warnings_timeout
|
||||
retry_on_snapshot_warnings_timeout,
|
||||
warn_only
|
||||
};
|
||||
return Object.fromEntries(Object.entries(keys).filter(([_, value]) => value !== undefined));
|
||||
}
|
||||
@@ -49326,7 +49347,8 @@ exports.ConfigurationOptionsSchema = z
|
||||
z.preprocess(val => (val === 'true' ? true : val === 'false' ? false : val), z.boolean()),
|
||||
z.enum(['always', 'never', 'on-failure'])
|
||||
])
|
||||
.default('never')
|
||||
.default('never'),
|
||||
warn_only: z.boolean().default(false)
|
||||
})
|
||||
.transform(config => {
|
||||
if (config.comment_summary_in_pr === true) {
|
||||
|
||||
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
@@ -32,7 +32,8 @@ const defaultConfig: ConfigurationOptions = {
|
||||
],
|
||||
comment_summary_in_pr: true,
|
||||
retry_on_snapshot_warnings: false,
|
||||
retry_on_snapshot_warnings_timeout: 120
|
||||
retry_on_snapshot_warnings_timeout: 120,
|
||||
warn_only: false
|
||||
}
|
||||
|
||||
const tmpDir = path.resolve(__dirname, '../tmp')
|
||||
|
||||
@@ -47,6 +47,7 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
const retry_on_snapshot_warnings_timeout = getOptionalNumber(
|
||||
'retry-on-snapshot-warnings-timeout'
|
||||
)
|
||||
const warn_only = getOptionalBoolean('warn-only')
|
||||
|
||||
validatePURL(allow_dependencies_licenses)
|
||||
validateLicenses('allow-licenses', allow_licenses)
|
||||
@@ -67,7 +68,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
head_ref,
|
||||
comment_summary_in_pr,
|
||||
retry_on_snapshot_warnings,
|
||||
retry_on_snapshot_warnings_timeout
|
||||
retry_on_snapshot_warnings_timeout,
|
||||
warn_only
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
|
||||
39
src/main.ts
39
src/main.ts
@@ -87,7 +87,14 @@ async function run(): Promise<void> {
|
||||
scopedChanges
|
||||
)
|
||||
|
||||
const minSeverity = config.fail_on_severity
|
||||
const failOnSeverityParams = config.fail_on_severity
|
||||
const warnOnly = config.warn_only
|
||||
let minSeverity: Severity = 'low'
|
||||
// If failOnSeverityParams is not set or warnOnly is true, the minSeverity is low, to allow all vulnerabilities to be reported as warnings
|
||||
if (failOnSeverityParams && !warnOnly) {
|
||||
minSeverity = failOnSeverityParams
|
||||
}
|
||||
|
||||
const vulnerableChanges = filterChangesBySeverity(
|
||||
minSeverity,
|
||||
filteredChanges
|
||||
@@ -124,11 +131,11 @@ async function run(): Promise<void> {
|
||||
|
||||
if (config.vulnerability_check) {
|
||||
summary.addChangeVulnerabilitiesToSummary(vulnerableChanges, minSeverity)
|
||||
printVulnerabilitiesBlock(vulnerableChanges, minSeverity)
|
||||
printVulnerabilitiesBlock(vulnerableChanges, minSeverity, warnOnly)
|
||||
}
|
||||
if (config.license_check) {
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config)
|
||||
printLicensesBlock(invalidLicenseChanges)
|
||||
printLicensesBlock(invalidLicenseChanges, warnOnly)
|
||||
}
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
summary.addDeniedToSummary(deniedChanges)
|
||||
@@ -167,19 +174,25 @@ async function run(): Promise<void> {
|
||||
|
||||
function printVulnerabilitiesBlock(
|
||||
addedChanges: Changes,
|
||||
minSeverity: Severity
|
||||
minSeverity: Severity,
|
||||
warnOnly: boolean
|
||||
): void {
|
||||
let failed = false
|
||||
let vulFound = false
|
||||
core.group('Vulnerabilities', async () => {
|
||||
if (addedChanges.length > 0) {
|
||||
for (const change of addedChanges) {
|
||||
printChangeVulnerabilities(change)
|
||||
}
|
||||
failed = true
|
||||
vulFound = true
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
core.setFailed('Dependency review detected vulnerable packages.')
|
||||
if (vulFound) {
|
||||
const msg = 'Dependency review detected vulnerable packages.'
|
||||
if (warnOnly) {
|
||||
core.warning(msg)
|
||||
} else {
|
||||
core.setFailed(msg)
|
||||
}
|
||||
} else {
|
||||
core.info(
|
||||
`Dependency review did not detect any vulnerable packages with severity level "${minSeverity}" or higher.`
|
||||
@@ -202,13 +215,19 @@ function printChangeVulnerabilities(change: Change): void {
|
||||
}
|
||||
|
||||
function printLicensesBlock(
|
||||
invalidLicenseChanges: Record<string, Changes>
|
||||
invalidLicenseChanges: Record<string, Changes>,
|
||||
warnOnly: boolean
|
||||
): void {
|
||||
core.group('Licenses', async () => {
|
||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
||||
core.info('\nThe following dependencies have incompatible licenses:')
|
||||
printLicensesError(invalidLicenseChanges.forbidden)
|
||||
core.setFailed('Dependency review detected incompatible licenses.')
|
||||
const msg = 'Dependency review detected incompatible licenses.'
|
||||
if (warnOnly) {
|
||||
core.warning(msg)
|
||||
} else {
|
||||
core.setFailed(msg)
|
||||
}
|
||||
}
|
||||
if (invalidLicenseChanges.unresolved.length > 0) {
|
||||
core.warning(
|
||||
|
||||
@@ -59,7 +59,8 @@ export const ConfigurationOptionsSchema = z
|
||||
),
|
||||
z.enum(['always', 'never', 'on-failure'])
|
||||
])
|
||||
.default('never')
|
||||
.default('never'),
|
||||
warn_only: z.boolean().default(false)
|
||||
})
|
||||
.transform(config => {
|
||||
if (config.comment_summary_in_pr === true) {
|
||||
|
||||
Reference in New Issue
Block a user