Skip to content

Commit

Permalink
Merge branch 'main' into comment-on-failure
Browse files Browse the repository at this point in the history
  • Loading branch information
sgmurphy committed Aug 9, 2023
2 parents 2e59943 + 7d90b4f commit 7ef37f3
Show file tree
Hide file tree
Showing 15 changed files with 617 additions and 38 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ Configure this action by either inlining these options in your workflow file, or
| `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 |

\*not supported for use with GitHub Enterprise Server

Expand Down
166 changes: 166 additions & 0 deletions __tests__/deny.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import {expect, jest, test} from '@jest/globals'
import {Change, Changes} from '../src/schemas'

let getDeniedChanges: Function

const npmChange: Change = {
manifest: 'package.json',
change_type: 'added',
ecosystem: 'npm',
name: 'Reeuhq',
version: '1.0.2',
package_url: 'pkg:npm/[email protected]',
license: 'MIT',
source_repository_url: 'github.com/some-repo',
scope: 'runtime',
vulnerabilities: [
{
severity: 'critical',
advisory_ghsa_id: 'first-random_string',
advisory_summary: 'very dangerous',
advisory_url: 'github.com/future-funk'
}
]
}

const rubyChange: Change = {
change_type: 'added',
manifest: 'Gemfile.lock',
ecosystem: 'rubygems',
name: 'actionsomething',
version: '3.2.0',
package_url: 'pkg:gem/[email protected]',
license: 'BSD',
source_repository_url: 'github.com/some-repo',
scope: 'runtime',
vulnerabilities: [
{
severity: 'moderate',
advisory_ghsa_id: 'second-random_string',
advisory_summary: 'not so dangerous',
advisory_url: 'github.com/future-funk'
},
{
severity: 'low',
advisory_ghsa_id: 'third-random_string',
advisory_summary: 'dont page me',
advisory_url: 'github.com/future-funk'
}
]
}

const pipChange: Change = {
change_type: 'added',
manifest: 'requirements.txt',
ecosystem: 'pip',
name: 'package-1',
version: '1.1.1',
package_url: 'pkg:pip/[email protected]',
license: 'MIT',
source_repository_url: 'github.com/some-repo',
scope: 'runtime',
vulnerabilities: [
{
severity: 'moderate',
advisory_ghsa_id: 'second-random_string',
advisory_summary: 'not so dangerous',
advisory_url: 'github.com/future-funk'
},
{
severity: 'low',
advisory_ghsa_id: 'third-random_string',
advisory_summary: 'dont page me',
advisory_url: 'github.com/future-funk'
}
]
}

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:maven/org.apache.logging.log4j/[email protected]',
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 = {
rest: {
licenses: {
getForRepo: jest
.fn()
.mockReturnValue({data: {license: {spdx_id: 'AGPL'}}})
}
}
}

jest.mock('octokit', () => {
return {
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
Octokit: class {
constructor() {
return mockOctokit
}
}
}
})

beforeEach(async () => {
jest.resetModules()
jest.doMock('spdx-satisfies', () => {
// mock spdx-satisfies return value
// true for BSD, false for all others
return jest.fn((license: string, _: string): boolean => license === 'BSD')
})
// eslint-disable-next-line @typescript-eslint/no-require-imports
;({getDeniedChanges} = require('../src/deny'))
})

test('it adds packages in the deny packages list', async () => {
const changes: Changes = [npmChange, rubyChange]
const deniedChanges = await getDeniedChanges(
changes,
['pkg:gem/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,
[],
['pkg:maven/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,
['pkg:gem/actionsomething'],
['pkg:maven:org.apache.logging.log4j']
)

expect(deniedChanges.length).toEqual(0)
})
35 changes: 31 additions & 4 deletions __tests__/summary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const defaultConfig: ConfigurationOptions = {
allow_ghsas: [],
allow_licenses: [],
deny_licenses: [],
deny_packages: [],
deny_groups: [],
comment_summary_in_pr: 'never'
}

Expand Down Expand Up @@ -70,6 +72,7 @@ test('prints headline as h1', () => {
summary.addSummaryToSummary(
emptyChanges,
emptyInvalidLicenseChanges,
emptyChanges,
defaultConfig
)
const text = core.summary.stringify()
Expand All @@ -81,6 +84,7 @@ test('only includes "No vulnerabilities or license issues found"-message if both
summary.addSummaryToSummary(
emptyChanges,
emptyInvalidLicenseChanges,
emptyChanges,
defaultConfig
)
const text = core.summary.stringify()
Expand All @@ -90,15 +94,25 @@ test('only includes "No vulnerabilities or license issues found"-message if both

test('only includes "No vulnerabilities found"-message if "license_check" is set to false and nothing was found', () => {
const config = {...defaultConfig, license_check: false}
summary.addSummaryToSummary(emptyChanges, emptyInvalidLicenseChanges, config)
summary.addSummaryToSummary(
emptyChanges,
emptyInvalidLicenseChanges,
emptyChanges,
config
)
const text = core.summary.stringify()

expect(text).toContain('✅ No vulnerabilities found.')
})

test('only includes "No license issues found"-message if "vulnerability_check" is set to false and nothing was found', () => {
const config = {...defaultConfig, vulnerability_check: false}
summary.addSummaryToSummary(emptyChanges, emptyInvalidLicenseChanges, config)
summary.addSummaryToSummary(
emptyChanges,
emptyInvalidLicenseChanges,
emptyChanges,
config
)
const text = core.summary.stringify()

expect(text).toContain('✅ No license issues found.')
Expand All @@ -108,6 +122,7 @@ test('groups dependencies with empty manifest paths together', () => {
summary.addSummaryToSummary(
changesWithEmptyManifests,
emptyInvalidLicenseChanges,
emptyChanges,
defaultConfig
)
summary.addScannedDependencies(changesWithEmptyManifests)
Expand All @@ -124,6 +139,7 @@ test('does not include status section if nothing was found', () => {
summary.addSummaryToSummary(
emptyChanges,
emptyInvalidLicenseChanges,
emptyChanges,
defaultConfig
)
const text = core.summary.stringify()
Expand All @@ -142,7 +158,12 @@ test('includes count and status icons for all findings', () => {
unlicensed: [createTestChange(), createTestChange(), createTestChange()]
}

summary.addSummaryToSummary(vulnerabilities, licenseIssues, defaultConfig)
summary.addSummaryToSummary(
vulnerabilities,
licenseIssues,
emptyChanges,
defaultConfig
)

const text = core.summary.stringify()
expect(text).toContain('❌ 2 vulnerable package(s)')
Expand All @@ -159,6 +180,7 @@ test('uses checkmarks for license issues if only vulnerabilities were found', ()
summary.addSummaryToSummary(
vulnerabilities,
emptyInvalidLicenseChanges,
emptyChanges,
defaultConfig
)

Expand All @@ -178,7 +200,12 @@ test('uses checkmarks for vulnerabilities if only license issues were found', ()
unlicensed: []
}

summary.addSummaryToSummary(emptyChanges, licenseIssues, defaultConfig)
summary.addSummaryToSummary(
emptyChanges,
licenseIssues,
emptyChanges,
defaultConfig
)

const text = core.summary.stringify()
expect(text).toContain('✅ 0 vulnerable package(s)')
Expand Down
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ inputs:
comment-summary-in-pr:
description: Determines if the summary is posted as a comment in the PR itself. Setting this to `always` or `on-failure` requires you to give the workflow the write permissions for pull-requests
required: false
deny-packages:
description: A comma-separated list of package URLs to deny (e.g. "pkg:npm/express, pkg:pip/pycrypto")
required: false
deny-groups:
description: A comma-separated list of package URLs for group(s)/namespace(s) to deny (e.g. "pkg:npm/express, pkg:pip/pycrypto")
required: false
runs:
using: 'node16'
main: 'dist/index.js'
Loading

0 comments on commit 7ef37f3

Please sign in to comment.