Merge pull request #200 from actions/willdasilva-fork

Support user-provided base/head refs & non-PR workflows
This commit is contained in:
Federico Builes
2022-08-18 15:30:04 +02:00
committed by GitHub
9 changed files with 178 additions and 32 deletions
+23 -2
View File
@@ -1,12 +1,12 @@
# dependency-review-action
This action scans your pull requests for dependency changes and will raise an error if any new dependencies have existing vulnerabilities. The action is supported by an [API endpoint](https://docs.github.com/en/rest/reference/dependency-graph#dependency-review) that diffs the dependencies between any two revisions.
This action scans your pull requests for dependency changes, and will
raise an error if any vulnerabilities or invalid licenses are being introduced. The action is supported by an [API endpoint](https://docs.github.com/en/rest/reference/dependency-graph#dependency-review) that diffs the dependencies between any two revisions.
The action is available for all public repositories, as well as private repositories that have GitHub Advanced Security licensed.
<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">
## 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.**
@@ -58,6 +58,7 @@ jobs:
```
## Configuration
You can pass additional options to the Dependency Review
Action using your workflow file. Here's an example workflow with
all the possible configurations:
@@ -79,6 +80,10 @@ jobs:
# Possible values: "critical", "high", "moderate", "low"
# fail-on-severity: critical
#
# Possible values: Any available git ref
# base-ref: ${{ github.event.pull_request.base.ref }}
# head-ref: ${{ github.event.pull_request.head.ref }}
#
# You can only include one of these two options: `allow-licenses` and `deny-licenses`. These options are not supported on GHES.
#
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
@@ -88,6 +93,11 @@ jobs:
# deny-licenses: LGPL-2.0, BSD-2-Clause
```
When the workflow with this action is caused by a `pull_request` or `pull_request_target` event,
the `base-ref` and `head-ref` values have the defaults as shown above. If the workflow is caused by
any other event, the `base-ref` and `head-ref` options must be
explicitly set in the configuration file.
### Vulnerability Severity
By default the action will fail on any pull request that contains a
@@ -134,6 +144,15 @@ 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
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**.
=======
* 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.
@@ -142,6 +161,7 @@ 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
@@ -159,4 +179,5 @@ We are grateful for any contributions made to this project.
Please read [CONTRIBUTING.MD](https://github.com/actions/dependency-review-action/blob/main/CONTRIBUTING.md) to get started.
## License
This project is released under the [MIT License](https://github.com/actions/dependency-review-action/blob/main/LICENSE).
+34 -3
View File
@@ -1,5 +1,6 @@
import {expect, test, beforeEach} from '@jest/globals'
import {readConfig} from '../src/config'
import {getRefs} from '../src/git-refs'
// GitHub Action inputs come in the form of environment variables
// with an INPUT prefix (e.g. INPUT_FAIL-ON-SEVERITY)
@@ -10,9 +11,17 @@ function setInput(input: string, value: string) {
// We want a clean ENV before each test. We use `delete`
// since we want `undefined` values and not empty strings.
function clearInputs() {
delete process.env['INPUT_FAIL-ON-SEVERITY']
delete process.env['INPUT_ALLOW-LICENSES']
delete process.env['INPUT_DENY-LICENSES']
const allowedOptions = [
'FAIL-ON-SEVERITY',
'ALLOW-LICENSES',
'DENY-LICENSES',
'BASE-REF',
'HEAD-REF'
]
allowedOptions.forEach(option => {
delete process.env[`INPUT_${option.toUpperCase()}`]
})
}
beforeEach(() => {
@@ -51,3 +60,25 @@ test('it raises an error when given an unknown severity', async () => {
setInput('fail-on-severity', 'zombies')
expect(() => readConfig()).toThrow()
})
test('it uses the given refs when the event is not a pull request', async () => {
setInput('base-ref', 'a-custom-base-ref')
setInput('head-ref', 'a-custom-head-ref')
const refs = getRefs(readConfig(), {
payload: {},
eventName: 'workflow_dispatch'
})
expect(refs.base).toEqual('a-custom-base-ref')
expect(refs.head).toEqual('a-custom-head-ref')
})
test('it raises an error when no refs are provided and the event is not a pull request', async () => {
const options = readConfig()
expect(() =>
getRefs(options, {
payload: {},
eventName: 'workflow_dispatch'
})
).toThrow()
})
+6
View File
@@ -10,6 +10,12 @@ inputs:
description: Don't block PRs below this severity. Possible values are `low`, `moderate`, `high`, `critical`.
required: false
default: 'low'
base-ref:
description: The base git ref to be used for this check. Has a default value when the workflow event is `pull_request` or `pull_request_target`. Must be provided otherwise.
required: false
head-ref:
description: The head git ref to be used for this check. Has a default value when the workflow event is `pull_request` or `pull_request_target`. Must be provided otherwise.
required: false
allow-licenses:
description: Comma-separated list of allowed licenses (e.g. "MIT, GPL 3.0, BSD 2 Clause")
required: false
Generated Vendored
+57 -11
View File
@@ -59,6 +59,47 @@ function compare({ owner, repo, baseRef, headRef }) {
exports.compare = compare;
/***/ }),
/***/ 1086:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getRefs = void 0;
const schemas_1 = __nccwpck_require__(8774);
function getRefs(config, context) {
let base_ref = config.base_ref;
let head_ref = config.head_ref;
// If possible, source default base & head refs from the GitHub event.
// The base/head ref from the config take priority, if provided.
if (context.eventName === 'pull_request' ||
context.eventName === 'pull_request_target') {
const pull_request = schemas_1.PullRequestSchema.parse(context.payload.pull_request);
base_ref = base_ref || pull_request.base.sha;
head_ref = head_ref || pull_request.head.sha;
}
if (!base_ref && !head_ref) {
throw new Error('Both a base ref and head ref must be provided, either via the `base_ref`/`head_ref` ' +
'config options, or by running a `pull_request`/`pull_request_target` workflow.');
}
else if (!base_ref) {
throw new Error('A base ref must be provided, either via the `base_ref` config option, ' +
'or by running a `pull_request`/`pull_request_target` workflow.');
}
else if (!head_ref) {
throw new Error('A head ref must be provided, either via the `head_ref` config option, ' +
'or by running a `pull_request`/`pull_request_target` workflow.');
}
return {
base: base_ref,
head: head_ref
};
}
exports.getRefs = getRefs;
/***/ }),
/***/ 3247:
@@ -157,24 +198,21 @@ const dependencyGraph = __importStar(__nccwpck_require__(4966));
const github = __importStar(__nccwpck_require__(5438));
const ansi_styles_1 = __importDefault(__nccwpck_require__(6844));
const request_error_1 = __nccwpck_require__(537);
const schemas_1 = __nccwpck_require__(8774);
const config_1 = __nccwpck_require__(6373);
const filter_1 = __nccwpck_require__(8752);
const licenses_1 = __nccwpck_require__(3247);
const git_refs_1 = __nccwpck_require__(1086);
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
if (github.context.eventName !== 'pull_request') {
throw new Error(`This run was triggered by the "${github.context.eventName}" event, which is unsupported. Please ensure you are using the "pull_request" event for this workflow.`);
}
const pull_request = schemas_1.PullRequestSchema.parse(github.context.payload.pull_request);
const config = (0, config_1.readConfig)();
const refs = (0, git_refs_1.getRefs)(config, github.context);
const changes = yield dependencyGraph.compare({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
baseRef: pull_request.base.sha,
headRef: pull_request.head.sha
baseRef: refs.base,
headRef: refs.head
});
const config = (0, config_1.readConfig)();
const minSeverity = config.fail_on_severity;
let failed = false;
const licenses = {
@@ -319,7 +357,9 @@ exports.ConfigurationOptionsSchema = z
.object({
fail_on_severity: z.enum(exports.SEVERITIES).default('low'),
allow_licenses: z.array(z.string()).default([]),
deny_licenses: z.array(z.string()).default([])
deny_licenses: z.array(z.string()).default([]),
base_ref: z.string(),
head_ref: z.string()
})
.partial()
.refine(obj => !(obj.allow_licenses && obj.deny_licenses), 'Your workflow file has both an allow_licenses list and deny_licenses list, but you can only set one or the other.');
@@ -14696,10 +14736,14 @@ function readConfig() {
if (allow_licenses !== undefined && deny_licenses !== undefined) {
throw new Error("Can't specify both allow_licenses and deny_licenses");
}
const base_ref = getOptionalInput('base-ref');
const head_ref = getOptionalInput('head-ref');
return {
fail_on_severity,
allow_licenses: allow_licenses === null || allow_licenses === void 0 ? void 0 : allow_licenses.split(',').map(x => x.trim()),
deny_licenses: deny_licenses === null || deny_licenses === void 0 ? void 0 : deny_licenses.split(',').map(x => x.trim())
deny_licenses: deny_licenses === null || deny_licenses === void 0 ? void 0 : deny_licenses.split(',').map(x => x.trim()),
base_ref,
head_ref
};
}
exports.readConfig = readConfig;
@@ -14801,7 +14845,9 @@ exports.ConfigurationOptionsSchema = z
.object({
fail_on_severity: z.enum(exports.SEVERITIES).default('low'),
allow_licenses: z.array(z.string()).default([]),
deny_licenses: z.array(z.string()).default([])
deny_licenses: z.array(z.string()).default([]),
base_ref: z.string(),
head_ref: z.string()
})
.partial()
.refine(obj => !(obj.allow_licenses && obj.deny_licenses), 'Your workflow file has both an allow_licenses list and deny_licenses list, but you can only set one or the other.');
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+6 -1
View File
@@ -19,9 +19,14 @@ export function readConfig(): ConfigurationOptions {
throw new Error("Can't specify both allow_licenses and deny_licenses")
}
const base_ref = getOptionalInput('base-ref')
const head_ref = getOptionalInput('head-ref')
return {
fail_on_severity,
allow_licenses: allow_licenses?.split(',').map(x => x.trim()),
deny_licenses: deny_licenses?.split(',').map(x => x.trim())
deny_licenses: deny_licenses?.split(',').map(x => x.trim()),
base_ref,
head_ref
}
}
+42
View File
@@ -0,0 +1,42 @@
import {PullRequestSchema, ConfigurationOptions} from './schemas'
export function getRefs(
config: ConfigurationOptions,
context: {payload: {pull_request?: unknown}; eventName: string}
): {base: string; head: string} {
let base_ref = config.base_ref
let head_ref = config.head_ref
// If possible, source default base & head refs from the GitHub event.
// The base/head ref from the config take priority, if provided.
if (
context.eventName === 'pull_request' ||
context.eventName === 'pull_request_target'
) {
const pull_request = PullRequestSchema.parse(context.payload.pull_request)
base_ref = base_ref || pull_request.base.sha
head_ref = head_ref || pull_request.head.sha
}
if (!base_ref && !head_ref) {
throw new Error(
'Both a base ref and head ref must be provided, either via the `base_ref`/`head_ref` ' +
'config options, or by running a `pull_request`/`pull_request_target` workflow.'
)
} else if (!base_ref) {
throw new Error(
'A base ref must be provided, either via the `base_ref` config option, ' +
'or by running a `pull_request`/`pull_request_target` workflow.'
)
} else if (!head_ref) {
throw new Error(
'A head ref must be provided, either via the `head_ref` config option, ' +
'or by running a `pull_request`/`pull_request_target` workflow.'
)
}
return {
base: base_ref,
head: head_ref
}
}
+6 -13
View File
@@ -3,31 +3,24 @@ 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, PullRequestSchema, Severity} from './schemas'
import {Change, Severity} from './schemas'
import {readConfig} from '../src/config'
import {filterChangesBySeverity} from '../src/filter'
import {getDeniedLicenseChanges} from './licenses'
import {getRefs} from './git-refs'
async function run(): Promise<void> {
try {
if (github.context.eventName !== 'pull_request') {
throw new Error(
`This run was triggered by the "${github.context.eventName}" event, which is unsupported. Please ensure you are using the "pull_request" event for this workflow.`
)
}
const pull_request = PullRequestSchema.parse(
github.context.payload.pull_request
)
const config = readConfig()
const refs = getRefs(config, github.context)
const changes = await dependencyGraph.compare({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
baseRef: pull_request.base.sha,
headRef: pull_request.head.sha
baseRef: refs.base,
headRef: refs.head
})
const config = readConfig()
const minSeverity = config.fail_on_severity
let failed = false
+3 -1
View File
@@ -34,7 +34,9 @@ export const ConfigurationOptionsSchema = z
.object({
fail_on_severity: z.enum(SEVERITIES).default('low'),
allow_licenses: z.array(z.string()).default([]),
deny_licenses: z.array(z.string()).default([])
deny_licenses: z.array(z.string()).default([]),
base_ref: z.string(),
head_ref: z.string()
})
.partial()
.refine(