Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88502badc9 | |||
| ff7c97a976 | |||
| 4d3b8e5269 | |||
| 38ee6e8360 | |||
| 54cd9a7cba | |||
| c4693c00ac | |||
| e89f113be2 | |||
| 2b96ea7f03 | |||
| 4300ce8d38 | |||
| de48c615a3 | |||
| eef7e39202 | |||
| 37dc32836b | |||
| 890361387d | |||
| 61f19e6447 | |||
| fd959624bf | |||
| 11dd186eb0 | |||
| 1ab05cf855 | |||
| 7d7d5e7c84 | |||
| 8a8fa8bd07 | |||
| 06daf8e801 | |||
| aeb9ff5438 | |||
| 1ef21ab130 | |||
| 3c95902dd6 | |||
| 4b4ec08f7b | |||
| a91c3ac205 | |||
| bf0cb7fac4 | |||
| 07a7056819 | |||
| b93fcee7ff | |||
| 8bac022bfd | |||
| fc4fb55b25 | |||
| 31c132fdca | |||
| 10bc05df70 | |||
| e641ee9a41 | |||
| 0ba71661e5 | |||
| 8ef181b2cb | |||
| 7e2a489d03 | |||
| eaeaeb3d57 | |||
| 1eaf30e6eb | |||
| 5da3462152 | |||
| 6fa5a8f9c0 | |||
| 0d23c39a5d | |||
| 6549b27685 | |||
| f4b16c52e5 | |||
| 1a7a37c468 | |||
| 38b459efad | |||
| 6410b2cdd2 | |||
| fd3a3b1051 | |||
| 6771e49f11 | |||
| c7c07e1117 | |||
| 59fdb0cce7 | |||
| 950228f7f7 | |||
| 6973819203 | |||
| eee2e3260e | |||
| 7eeddef885 | |||
| 8c58cdad09 | |||
| 380290a89b | |||
| 50c3ed0ba6 | |||
| 0455501026 | |||
| bac3f038ac | |||
| 2d81062605 | |||
| 2ae4b932b7 | |||
| c7d4075ae0 | |||
| 49a0208abf | |||
| 94941958fb | |||
| 2764e60363 | |||
| bcd1b9ab86 | |||
| d96759fedc | |||
| bfd72e7da2 | |||
| d8efcf0c1f | |||
| 3b74514266 | |||
| 6dfe5fd567 | |||
| 71a0ed0a31 | |||
| 7a364ecd6b | |||
| 435083feb7 | |||
| 781a55eaaa | |||
| 335c64c139 | |||
| af9a4fa160 | |||
| 3e04d4bc87 | |||
| be076ebeca | |||
| b74c52c335 | |||
| 2233eb2b88 | |||
| ca11176434 | |||
| c8f5c5518e | |||
| 469156603d | |||
| 6b1d7e7207 | |||
| a57a1dd454 | |||
| 0e8bd1f46f | |||
| dd931c7005 | |||
| d8d78b6ace | |||
| a1eafc653a | |||
| 35b0f5ded9 | |||
| 5a25f0b1b3 | |||
| 88dd76a7ef | |||
| b1427bfe58 | |||
| 0d079c6553 | |||
| ce3b0c8116 | |||
| d01dd09c36 | |||
| 21d1a080df | |||
| c869fcfa38 | |||
| 20229aad71 | |||
| 65d6c26087 | |||
| 8b6795d89d | |||
| 030c97ab49 | |||
| dc44a85a96 | |||
| 9cdfbb83fa | |||
| b1f8412445 | |||
| 0d02efb12c | |||
| 2a09e52261 | |||
| e86dfd8cc0 | |||
| a39d9063b3 | |||
| 9809e06c2d | |||
| 70bbe4186e | |||
| 23d1ffffb6 | |||
| d792f3e8ca | |||
| 5da7945e2b | |||
| a8e7c378a3 | |||
| 0e0d6ec5d6 |
@@ -0,0 +1 @@
|
||||
fail-on-severity: low
|
||||
+2
-1
@@ -75,6 +75,7 @@ Here are a few things you can do that will increase the likelihood of your pull
|
||||
|
||||
## Cutting a new release
|
||||
|
||||
1. Update the version number in [package.json](https://github.com/actions/dependency-review-action/blob/main/package.json).
|
||||
1. Go to [Draft a new
|
||||
release](https://github.com/actions/dependency-review-action/releases/new)
|
||||
in the Releases page.
|
||||
@@ -100,7 +101,7 @@ major version number (e.g. `v1`) in their workflows while
|
||||
automatically getting all the
|
||||
minor/patch updates.
|
||||
|
||||
To do this just force-create a new annotated tag and push it:
|
||||
To do this just checkout `main`, force-create a new annotated tag, and push it:
|
||||
```
|
||||
git tag -fa v2 -m "Updating v2 to 2.3.4"
|
||||
git push origin v2 --force
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
### GitHub Enterprise Server
|
||||
|
||||
This action is available in GHES starting with version 3.6. Make sure
|
||||
This action is available in Enterprise Server starting with version 3.6. Make sure
|
||||
[GitHub Advanced
|
||||
Security](https://docs.github.com/en/enterprise-server@3.6/admin/code-security/managing-github-advanced-security-for-your-enterprise/enabling-github-advanced-security-for-your-enterprise)
|
||||
and [GitHub
|
||||
@@ -50,7 +50,6 @@ with the label of any of your runners (the default label
|
||||
is `self-hosted`):
|
||||
|
||||
```yaml
|
||||
|
||||
# ...
|
||||
|
||||
jobs:
|
||||
@@ -65,9 +64,124 @@ 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:
|
||||
Configure this action by either using an external configuration file,
|
||||
or by inlining these options in your workflow file.
|
||||
|
||||
### Options
|
||||
|
||||
#### config-file
|
||||
|
||||
A string representing the path to an external configuraton file. By
|
||||
default external configuration files are not used.
|
||||
|
||||
**Possible values**: A string representing the absolute path to the
|
||||
configuration file.
|
||||
|
||||
**Example**: `config-file: ./.github/dependency-review-config.yml`.
|
||||
|
||||
#### fail-on-severity
|
||||
|
||||
Configure the severity level for alerting. See "[Vulnerability Severity](https://github.com/actions/dependency-review-action#vulnerability-severity)".
|
||||
|
||||
**Possible values**: `critical`, `high`, `moderate`, `low`.
|
||||
|
||||
**Example**: `fail-on-severity: moderate`.
|
||||
|
||||
#### fail-on-scopes
|
||||
|
||||
A list of strings representing the build environments you want to
|
||||
support. The default value is `development, runtime`.
|
||||
|
||||
**Possible values**: `development`, `runtime`, `unknown`
|
||||
|
||||
**Inline example**: `fail-on-scopes: development, runtime`
|
||||
|
||||
**YAML example**:
|
||||
```yaml
|
||||
# this prevents scanning development dependencies
|
||||
fail-on-scopes:
|
||||
- runtime
|
||||
```
|
||||
|
||||
#### allow-licenses
|
||||
|
||||
Only allow the licenses in this list. See "[Licenses](https://github.com/actions/dependency-review-action#licenses)".
|
||||
|
||||
**Possible values**: Any `spdx_id` value(s) from
|
||||
https://docs.github.com/en/rest/licenses.
|
||||
|
||||
**Inline example**: `allow-licenses: BSD-3-Clause, MIT`
|
||||
|
||||
**YAML example**:
|
||||
```yaml
|
||||
allow-licenses:
|
||||
- BSD-3-Clause
|
||||
- MIT
|
||||
```
|
||||
|
||||
#### deny-licenses
|
||||
|
||||
Add a custom list of licenses you want to block. See
|
||||
"[Licenses](https://github.com/actions/dependency-review-action#licenses)".
|
||||
|
||||
**Possible values**: Any `spdx_id` value(s) from
|
||||
https://docs.github.com/en/rest/licenses.
|
||||
|
||||
**Inline example**: `deny-licenses: LGPL-2.0, BSD-2-Clause`
|
||||
|
||||
**YAML example**:
|
||||
```yaml
|
||||
deny-licenses:
|
||||
- LGPL-2.0
|
||||
- BSD-2-Clause
|
||||
```
|
||||
|
||||
#### base-ref/head-ref
|
||||
|
||||
Provide custom git references for the git base/head when performing
|
||||
the comparison. If you are using pull requests, or
|
||||
`pull_request_target` events you do not need to worry about setting
|
||||
this. The values need to be specified for all other event types.
|
||||
|
||||
**Possible values**: Any valid git ref(s) in your project.
|
||||
|
||||
**Example**:
|
||||
```yaml
|
||||
base-ref: 8bb8a58d6a4028b6c2e314d5caaf273f57644896
|
||||
head-ref: 69af5638bf660cf218aad5709a4c100e42a2f37b
|
||||
```
|
||||
|
||||
### Configuration File
|
||||
|
||||
You can use an external configuration file to specify the settings for
|
||||
this Action.
|
||||
|
||||
Start by specifying that you will be using an external configuration
|
||||
file:
|
||||
|
||||
```yaml
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@v2
|
||||
with:
|
||||
config-file: "./.github/dependency-review-config.yml"
|
||||
```
|
||||
|
||||
And then create the file in the path you just specified. **All of these fields are
|
||||
optional**:
|
||||
|
||||
```yaml
|
||||
fail-on-severity: "critical"
|
||||
allow-licenses:
|
||||
- "GPL-3.0"
|
||||
- "BSD-3-Clause"
|
||||
- "MIT"
|
||||
```
|
||||
|
||||
### Inline Configuration
|
||||
|
||||
You can pass options to the Dependency Review
|
||||
Action using your workflow file. Here's an example of what the full
|
||||
file would look like:
|
||||
|
||||
```yaml
|
||||
name: 'Dependency Review'
|
||||
@@ -83,26 +197,11 @@ jobs:
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@v2
|
||||
with:
|
||||
# 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
|
||||
# allow-licenses: GPL-3.0, BSD-3-Clause, MIT
|
||||
#
|
||||
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
|
||||
# deny-licenses: LGPL-2.0, BSD-2-Clause
|
||||
```
|
||||
fail-on-severity: moderate
|
||||
|
||||
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.
|
||||
# Use comma-separated names to pass list arguments:
|
||||
deny-licenses: LGPL-2.0, BSD-2-Clause
|
||||
```
|
||||
|
||||
### Vulnerability Severity
|
||||
|
||||
@@ -120,12 +219,23 @@ This example will only fail on pull requests with `critical` and `high` vulnerab
|
||||
fail-on-severity: high
|
||||
```
|
||||
|
||||
### Dependency Scoping
|
||||
|
||||
By default the action will only fail on `runtime` dependencies that have vulnerabilities or unacceptable licenses, ignoring `development` dependencies. You can override this behavior with the `fail-on-scopes` option, which will allow you to list the specific dependency scopes you care about. The possible values are: `unknown`, `runtime`, and `development`. Note: Filtering by scope will not be supported on Enterprise Server just yet, as the REST API's introduction of `scope` will be released in an upcoming Enterprise Server version. We will treat all dependencies on Enterprise Server as having a `runtime` scope and thus will not be filtered away.
|
||||
|
||||
```yaml
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@v2
|
||||
with:
|
||||
fail-on-scopes: runtime, development
|
||||
```
|
||||
|
||||
### Licenses
|
||||
|
||||
You can set the action to fail on pull requests based on the licenses of the dependencies
|
||||
they introduce. With `allow-licenses` you can define the list of licenses
|
||||
your repository will accept. Alternatively, you can use `deny-licenses` to only
|
||||
forbid a subset of licenses. These options are not supported on GHES.
|
||||
forbid a subset of licenses. These options are not supported on Enterprise Server.
|
||||
|
||||
You can use the [Licenses
|
||||
API](https://docs.github.com/en/rest/licenses) to see the full list of
|
||||
@@ -148,8 +258,9 @@ to filter. A couple of examples:
|
||||
deny-licenses: Apache-1.1, Apache-2.0
|
||||
```
|
||||
|
||||
**Important**
|
||||
### Considerations
|
||||
|
||||
- Checking for licenses is not supported on Enterprise Server.
|
||||
- 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {expect, test, beforeEach} from '@jest/globals'
|
||||
import {readConfig} from '../src/config'
|
||||
import {readConfig, readConfigFile} from '../src/config'
|
||||
import {getRefs} from '../src/git-refs'
|
||||
|
||||
// GitHub Action inputs come in the form of environment variables
|
||||
@@ -13,8 +13,10 @@ function setInput(input: string, value: string) {
|
||||
function clearInputs() {
|
||||
const allowedOptions = [
|
||||
'FAIL-ON-SEVERITY',
|
||||
'FAIL-ON-SCOPES',
|
||||
'ALLOW-LICENSES',
|
||||
'DENY-LICENSES',
|
||||
'CONFIG-FILE',
|
||||
'BASE-REF',
|
||||
'HEAD-REF'
|
||||
]
|
||||
@@ -82,3 +84,79 @@ test('it raises an error when no refs are provided and the event is not a pull r
|
||||
})
|
||||
).toThrow()
|
||||
})
|
||||
|
||||
test('it reads an external config file', async () => {
|
||||
let options = readConfigFile('./__tests__/fixtures/config-allow-sample.yml')
|
||||
expect(options.fail_on_severity).toEqual('critical')
|
||||
expect(options.allow_licenses).toEqual(['BSD', 'GPL 2'])
|
||||
})
|
||||
|
||||
test('raises an error when the the config file was not found', async () => {
|
||||
expect(() => readConfigFile('fixtures/i-dont-exist')).toThrow()
|
||||
})
|
||||
|
||||
test('it parses options from both sources', async () => {
|
||||
setInput('config-file', './__tests__/fixtures/config-allow-sample.yml')
|
||||
|
||||
let options = readConfig()
|
||||
expect(options.fail_on_severity).toEqual('critical')
|
||||
|
||||
setInput('base-ref', 'a-custom-base-ref')
|
||||
options = readConfig()
|
||||
expect(options.base_ref).toEqual('a-custom-base-ref')
|
||||
})
|
||||
|
||||
test('in case of conflicts, the external config is the source of truth', async () => {
|
||||
setInput('config-file', './__tests__/fixtures/config-allow-sample.yml') // this will set fail-on-severity to 'critical'
|
||||
|
||||
let options = readConfig()
|
||||
expect(options.fail_on_severity).toEqual('critical')
|
||||
|
||||
// this should not overwite the previous value
|
||||
setInput('fail-on-severity', 'low')
|
||||
options = readConfig()
|
||||
expect(options.fail_on_severity).toEqual('critical')
|
||||
})
|
||||
|
||||
test('it uses the default values when loading external files', async () => {
|
||||
setInput('config-file', './__tests__/fixtures/no-licenses-config.yml')
|
||||
let options = readConfig()
|
||||
expect(options.allow_licenses).toEqual(undefined)
|
||||
expect(options.deny_licenses).toEqual(undefined)
|
||||
|
||||
setInput('config-file', './__tests__/fixtures/license-config-sample.yml')
|
||||
options = readConfig()
|
||||
expect(options.fail_on_severity).toEqual('low')
|
||||
})
|
||||
|
||||
test('it accepts an external configuration filename', async () => {
|
||||
setInput('config-file', './__tests__/fixtures/no-licenses-config.yml')
|
||||
const options = readConfig()
|
||||
expect(options.fail_on_severity).toEqual('critical')
|
||||
})
|
||||
|
||||
test('it raises an error when given an unknown severity in an external config file', async () => {
|
||||
setInput('config-file', './__tests__/fixtures/invalid-severity-config.yml')
|
||||
expect(() => readConfig()).toThrow()
|
||||
})
|
||||
|
||||
test('it defaults to runtime scope', async () => {
|
||||
const options = readConfig()
|
||||
expect(options.fail_on_scopes).toEqual(['runtime'])
|
||||
})
|
||||
|
||||
test('it parses custom scopes preference', async () => {
|
||||
setInput('fail-on-scopes', 'runtime, development')
|
||||
let options = readConfig()
|
||||
expect(options.fail_on_scopes).toEqual(['runtime', 'development'])
|
||||
|
||||
clearInputs()
|
||||
setInput('fail-on-scopes', 'development')
|
||||
options = readConfig()
|
||||
expect(options.fail_on_scopes).toEqual(['development'])
|
||||
})
|
||||
|
||||
test('it raises an error when given invalid scope', async () => {
|
||||
setInput('fail-on-scopes', 'runtime, zombies')
|
||||
expect(() => readConfig()).toThrow()
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {expect, test} from '@jest/globals'
|
||||
import {Change, Changes} from '../src/schemas'
|
||||
import {filterChangesBySeverity} from '../src/filter'
|
||||
import {filterChangesBySeverity, filterChangesByScopes} from '../src/filter'
|
||||
|
||||
let npmChange: Change = {
|
||||
manifest: 'package.json',
|
||||
@@ -11,6 +11,7 @@ let npmChange: Change = {
|
||||
package_url: 'pkg:npm/reeuhq@1.0.2',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'critical',
|
||||
@@ -30,6 +31,7 @@ let rubyChange: Change = {
|
||||
package_url: 'pkg:gem/actionsomething@3.2.0',
|
||||
license: 'BSD',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'development',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'moderate',
|
||||
@@ -57,3 +59,16 @@ test('it properly filters changes by severity', async () => {
|
||||
result = filterChangesBySeverity('critical', changes)
|
||||
expect(changes).toEqual([npmChange, rubyChange])
|
||||
})
|
||||
|
||||
test('it properly filters changes by scope', async () => {
|
||||
const changes = [npmChange, rubyChange]
|
||||
|
||||
let result = filterChangesByScopes(['runtime'], changes)
|
||||
expect(result).toEqual([npmChange])
|
||||
|
||||
result = filterChangesByScopes(['development'], changes)
|
||||
expect(result).toEqual([rubyChange])
|
||||
|
||||
result = filterChangesByScopes(['runtime', 'development'], changes)
|
||||
expect(result).toEqual([npmChange, rubyChange])
|
||||
})
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fail-on-severity: 'so many zombies'
|
||||
deny-licenses:
|
||||
- MIT
|
||||
@@ -0,0 +1 @@
|
||||
allow_licenses: ['MIT', 'GPL 2']
|
||||
@@ -11,6 +11,7 @@ let npmChange: Change = {
|
||||
package_url: 'pkg:npm/reeuhq@1.0.2',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'critical',
|
||||
@@ -30,6 +31,7 @@ let rubyChange: Change = {
|
||||
package_url: 'pkg:gem/actionsomething@3.2.0',
|
||||
license: 'BSD',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'moderate',
|
||||
|
||||
@@ -10,12 +10,19 @@ inputs:
|
||||
description: Don't block PRs below this severity. Possible values are `low`, `moderate`, `high`, `critical`.
|
||||
required: false
|
||||
default: 'low'
|
||||
fail-on-scopes:
|
||||
description: Dependency scopes to block PRs on. Comma-separated list. Possible values are 'unknown', 'runtime', and 'development' (e.g. "runtime, development")
|
||||
required: false
|
||||
default: 'runtime'
|
||||
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
|
||||
config-file:
|
||||
description: A filepath to the configuration file for the action.
|
||||
required: false
|
||||
allow-licenses:
|
||||
description: Comma-separated list of allowed licenses (e.g. "MIT, GPL 3.0, BSD 2 Clause")
|
||||
required: false
|
||||
|
||||
+8580
-167
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
+17
@@ -697,6 +697,23 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
yaml
|
||||
ISC
|
||||
Copyright Eemeli Aro <eemeli@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
|
||||
|
||||
zod
|
||||
MIT
|
||||
MIT License
|
||||
|
||||
Generated
+278
-430
File diff suppressed because it is too large
Load Diff
+12
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dependency-review-action",
|
||||
"version": "2.0.4",
|
||||
"version": "2.2.0",
|
||||
"private": true,
|
||||
"description": "A GitHub Action for Dependency Review",
|
||||
"main": "lib/main.js",
|
||||
@@ -29,26 +29,26 @@
|
||||
"@actions/github": "^5.0.3",
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
"@octokit/request-error": "^3.0.1",
|
||||
"ansi-styles": "^6.1.0",
|
||||
"got": "^12.3.1",
|
||||
"nodemon": "^2.0.19",
|
||||
"ansi-styles": "^6.1.1",
|
||||
"got": "^12.5.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"yaml": "^2.1.1",
|
||||
"zod": "^3.18.0"
|
||||
"zod": "^3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.49",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
||||
"@typescript-eslint/parser": "^5.33.1",
|
||||
"@types/node": "^16.11.59",
|
||||
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||
"@typescript-eslint/parser": "^5.38.0",
|
||||
"@vercel/ncc": "^0.34.0",
|
||||
"esbuild-register": "^3.3.3",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint": "^8.23.1",
|
||||
"eslint-plugin-github": "^4.3.7",
|
||||
"eslint-plugin-jest": "^26.8.3",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"jest": "^27.5.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"nodemon": "^2.0.19",
|
||||
"nodemon": "^2.0.20",
|
||||
"prettier": "2.7.1",
|
||||
"ts-jest": "^27.1.4",
|
||||
"typescript": "^4.7.4"
|
||||
"typescript": "^4.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
+15
-7
@@ -32,17 +32,25 @@ event_file = Tempfile.new
|
||||
event_file.write("{ \"pull_request\": #{pr.to_h.to_json}}")
|
||||
event_file.close
|
||||
|
||||
dev_cmd_env = {
|
||||
"INPUT_REPO-TOKEN" => github_token,
|
||||
"GITHUB_REPOSITORY" => repo_nwo,
|
||||
"GITHUB_EVENT_NAME" => "pull_request",
|
||||
"GITHUB_EVENT_PATH" => event_file.path
|
||||
action_inputs = {
|
||||
"repo-token" => github_token
|
||||
}
|
||||
|
||||
dev_cmd = "./node_modules/.bin/nodemon --exec \"node -r esbuild-register\" src/main.ts"
|
||||
dev_cmd_env = {
|
||||
"GITHUB_REPOSITORY" => repo_nwo,
|
||||
"GITHUB_EVENT_NAME" => "pull_request",
|
||||
"GITHUB_EVENT_PATH" => event_file.path,
|
||||
"GITHUB_STEP_SUMMARY" => "/dev/null"
|
||||
}
|
||||
|
||||
# bash does not like variable names with dashes like the ones Actions
|
||||
# uses (e.g. INPUT_REPO-TOKEN). Passing them through `env` instead of
|
||||
# manually setting them does the job.
|
||||
action_inputs_env_str = action_inputs.map { |name, value| "\"INPUT_#{name.upcase}=#{value}\"" }.join(" ")
|
||||
dev_cmd = "./node_modules/.bin/nodemon --exec \"env #{action_inputs_env_str} node -r esbuild-register\" src/main.ts"
|
||||
|
||||
Open3.popen2e(dev_cmd_env, dev_cmd) do |stdin, out|
|
||||
while line = out.gets
|
||||
puts line
|
||||
puts line.gsub(github_token, "<REDACTED>")
|
||||
end
|
||||
end
|
||||
|
||||
+64
-7
@@ -1,17 +1,52 @@
|
||||
import * as fs from 'fs'
|
||||
import path from 'path'
|
||||
import YAML from 'yaml'
|
||||
import * as core from '@actions/core'
|
||||
import * as z from 'zod'
|
||||
import {ConfigurationOptions, SEVERITIES} from './schemas'
|
||||
import {
|
||||
ConfigurationOptions,
|
||||
ConfigurationOptionsSchema,
|
||||
SeveritySchema,
|
||||
SCOPES
|
||||
} from './schemas'
|
||||
|
||||
function getOptionalInput(name: string): string | undefined {
|
||||
const value = core.getInput(name)
|
||||
return value.length > 0 ? value : undefined
|
||||
}
|
||||
|
||||
function parseList(list: string | undefined): string[] | undefined {
|
||||
if (list === undefined) {
|
||||
return list
|
||||
} else {
|
||||
return list.split(',').map(x => x.trim())
|
||||
}
|
||||
}
|
||||
|
||||
export function readConfig(): ConfigurationOptions {
|
||||
const fail_on_severity = z
|
||||
.enum(SEVERITIES)
|
||||
.default('low')
|
||||
.parse(getOptionalInput('fail-on-severity'))
|
||||
const externalConfig = getOptionalInput('config-file')
|
||||
if (externalConfig !== undefined) {
|
||||
const config = readConfigFile(externalConfig)
|
||||
// the reasoning behind reading the inline config when an external
|
||||
// config file is provided is that we still want to allow users to
|
||||
// pass inline options in the presence of an external config file.
|
||||
const inlineConfig = readInlineConfig()
|
||||
// the external config takes precedence
|
||||
return Object.assign({}, inlineConfig, config)
|
||||
} else {
|
||||
return readInlineConfig()
|
||||
}
|
||||
}
|
||||
|
||||
export function readInlineConfig(): ConfigurationOptions {
|
||||
const fail_on_severity = SeveritySchema.parse(
|
||||
getOptionalInput('fail-on-severity')
|
||||
)
|
||||
const fail_on_scopes = z
|
||||
.array(z.enum(SCOPES))
|
||||
.default(['runtime'])
|
||||
.parse(parseList(getOptionalInput('fail-on-scopes')))
|
||||
|
||||
const allow_licenses = getOptionalInput('allow-licenses')
|
||||
const deny_licenses = getOptionalInput('deny-licenses')
|
||||
|
||||
@@ -24,9 +59,31 @@ export function readConfig(): ConfigurationOptions {
|
||||
|
||||
return {
|
||||
fail_on_severity,
|
||||
allow_licenses: allow_licenses?.split(',').map(x => x.trim()),
|
||||
deny_licenses: deny_licenses?.split(',').map(x => x.trim()),
|
||||
fail_on_scopes,
|
||||
allow_licenses: parseList(allow_licenses),
|
||||
deny_licenses: parseList(deny_licenses),
|
||||
base_ref,
|
||||
head_ref
|
||||
}
|
||||
}
|
||||
|
||||
export function readConfigFile(filePath: string): ConfigurationOptions {
|
||||
let data
|
||||
|
||||
try {
|
||||
data = fs.readFileSync(path.resolve(filePath), 'utf-8')
|
||||
} catch (error: unknown) {
|
||||
throw error
|
||||
}
|
||||
data = YAML.parse(data)
|
||||
|
||||
// get rid of the ugly dashes from the actions conventions
|
||||
for (const key of Object.keys(data)) {
|
||||
if (key.includes('-')) {
|
||||
data[key.replace(/-/g, '_')] = data[key]
|
||||
delete data[key]
|
||||
}
|
||||
}
|
||||
const values = ConfigurationOptionsSchema.parse(data)
|
||||
return values
|
||||
}
|
||||
|
||||
+14
-1
@@ -1,4 +1,4 @@
|
||||
import {Changes, Severity, SEVERITIES} from './schemas'
|
||||
import {Changes, Severity, SEVERITIES, Scope} from './schemas'
|
||||
|
||||
export function filterChangesBySeverity(
|
||||
severity: Severity,
|
||||
@@ -33,3 +33,16 @@ export function filterChangesBySeverity(
|
||||
)
|
||||
return filteredChanges
|
||||
}
|
||||
|
||||
export function filterChangesByScopes(
|
||||
scopes: Scope[],
|
||||
changes: Changes
|
||||
): Changes {
|
||||
const filteredChanges = changes.filter(change => {
|
||||
// if there is no scope on the change (Enterprise Server API for now), we will assume it is a runtime scope
|
||||
const scope = change.scope || 'runtime'
|
||||
return scopes.includes(scope)
|
||||
})
|
||||
|
||||
return filteredChanges
|
||||
}
|
||||
|
||||
+8
-4
@@ -3,9 +3,9 @@ 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} from './schemas'
|
||||
import {Change, Severity, Scope} from './schemas'
|
||||
import {readConfig} from '../src/config'
|
||||
import {filterChangesBySeverity} from '../src/filter'
|
||||
import {filterChangesBySeverity, filterChangesByScopes} from '../src/filter'
|
||||
import {getDeniedLicenseChanges} from './licenses'
|
||||
import * as summary from './summary'
|
||||
import {getRefs} from './git-refs'
|
||||
@@ -30,9 +30,13 @@ async function run(): Promise<void> {
|
||||
deny: config.deny_licenses
|
||||
}
|
||||
|
||||
const scopes = config.fail_on_scopes
|
||||
|
||||
const scopedChanges = filterChangesByScopes(scopes as Scope[], changes)
|
||||
|
||||
const addedChanges = filterChangesBySeverity(
|
||||
minSeverity as Severity,
|
||||
changes
|
||||
scopedChanges
|
||||
).filter(
|
||||
change =>
|
||||
change.change_type === 'added' &&
|
||||
@@ -41,7 +45,7 @@ async function run(): Promise<void> {
|
||||
)
|
||||
|
||||
const [licenseErrors, unknownLicenses] = getDeniedLicenseChanges(
|
||||
changes,
|
||||
scopedChanges,
|
||||
licenses
|
||||
)
|
||||
|
||||
|
||||
+10
-3
@@ -1,6 +1,9 @@
|
||||
import * as z from 'zod'
|
||||
|
||||
export const SEVERITIES = ['critical', 'high', 'moderate', 'low'] as const
|
||||
export const SCOPES = ['unknown', 'runtime', 'development'] as const
|
||||
|
||||
export const SeveritySchema = z.enum(SEVERITIES).default('low')
|
||||
|
||||
export const ChangeSchema = z.object({
|
||||
change_type: z.enum(['added', 'removed']),
|
||||
@@ -11,10 +14,11 @@ export const ChangeSchema = z.object({
|
||||
package_url: z.string(),
|
||||
license: z.string().nullable(),
|
||||
source_repository_url: z.string().nullable(),
|
||||
scope: z.enum(SCOPES).optional(),
|
||||
vulnerabilities: z
|
||||
.array(
|
||||
z.object({
|
||||
severity: z.enum(['critical', 'high', 'moderate', 'low']),
|
||||
severity: SeveritySchema,
|
||||
advisory_ghsa_id: z.string(),
|
||||
advisory_summary: z.string(),
|
||||
advisory_url: z.string()
|
||||
@@ -32,9 +36,11 @@ export const PullRequestSchema = z.object({
|
||||
|
||||
export const ConfigurationOptionsSchema = z
|
||||
.object({
|
||||
fail_on_severity: z.enum(SEVERITIES).default('low'),
|
||||
fail_on_severity: SeveritySchema,
|
||||
fail_on_scopes: z.array(z.enum(SCOPES)).default(['runtime']),
|
||||
allow_licenses: z.array(z.string()).default([]),
|
||||
deny_licenses: z.array(z.string()).default([]),
|
||||
config_file: z.string().optional().default('false'),
|
||||
base_ref: z.string(),
|
||||
head_ref: z.string()
|
||||
})
|
||||
@@ -49,4 +55,5 @@ export const ChangesSchema = z.array(ChangeSchema)
|
||||
export type Change = z.infer<typeof ChangeSchema>
|
||||
export type Changes = z.infer<typeof ChangesSchema>
|
||||
export type ConfigurationOptions = z.infer<typeof ConfigurationOptionsSchema>
|
||||
export type Severity = typeof SEVERITIES[number]
|
||||
export type Severity = z.infer<typeof SeveritySchema>
|
||||
export type Scope = typeof SCOPES[number]
|
||||
|
||||
Reference in New Issue
Block a user