Skip to content

Commit

Permalink
Merge pull request #243 from actions/sarahkemi/filter-dev-deps
Browse files Browse the repository at this point in the history
Filter blocking dependency changes by scopes
  • Loading branch information
sarahkemi committed Sep 20, 2022
2 parents fd95962 + de48c61 commit 4300ce8
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 32 deletions.
37 changes: 25 additions & 12 deletions README.md
Expand Up @@ -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
Expand All @@ -50,7 +50,6 @@ with the label of any of your runners (the default label
is `self-hosted`):

```yaml

# ...

jobs:
Expand Down Expand Up @@ -86,11 +85,14 @@ jobs:
# Possible values: "critical", "high", "moderate", "low"
# fail-on-severity: critical
#
# Possible values in comma separated list: "unknown", "runtime", or "development"
# fail-on-scopes: runtime, development
#
# 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.
# You can only include one of these two options: `allow-licenses` and `deny-licenses`. These options are not supported on Enterprise Server.
#
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
# allow-licenses: GPL-3.0, BSD-3-Clause, MIT
Expand Down Expand Up @@ -120,12 +122,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
Expand All @@ -150,14 +163,14 @@ to filter. A couple of examples:

**Important**

* 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.
* 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 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
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**.

## Blocking pull requests

Expand Down
20 changes: 20 additions & 0 deletions __tests__/config.test.ts
Expand Up @@ -13,6 +13,7 @@ function setInput(input: string, value: string) {
function clearInputs() {
const allowedOptions = [
'FAIL-ON-SEVERITY',
'FAIL-ON-SCOPES',
'ALLOW-LICENSES',
'DENY-LICENSES',
'BASE-REF',
Expand Down Expand Up @@ -82,3 +83,22 @@ test('it raises an error when no refs are provided and the event is not a pull r
})
).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()
})
17 changes: 16 additions & 1 deletion __tests__/filter.test.ts
@@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand Down Expand Up @@ -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])
})
2 changes: 2 additions & 0 deletions __tests__/licenses.test.ts
Expand Up @@ -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',
Expand All @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions action.yml
Expand Up @@ -10,6 +10,10 @@ 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
Expand Down
48 changes: 39 additions & 9 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

19 changes: 16 additions & 3 deletions src/config.ts
@@ -1,17 +1,29 @@
import * as core from '@actions/core'
import * as z from 'zod'
import {ConfigurationOptions, SEVERITIES} from './schemas'
import {ConfigurationOptions, SEVERITIES, 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 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')

Expand All @@ -24,8 +36,9 @@ 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
}
Expand Down
15 changes: 14 additions & 1 deletion src/filter.ts
@@ -1,4 +1,4 @@
import {Changes, Severity, SEVERITIES} from './schemas'
import {Changes, Severity, SEVERITIES, Scope} from './schemas'

export function filterChangesBySeverity(
severity: Severity,
Expand Down Expand Up @@ -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
}

0 comments on commit 4300ce8

Please sign in to comment.