Skip to content

Commit

Permalink
Merge pull request #753 from actions/juxtin/debug-purl
Browse files Browse the repository at this point in the history
Parse purls cautiously in getDeniedChanges
  • Loading branch information
juxtin authored Apr 29, 2024
2 parents 0659a74 + 5f0808f commit 7dcfabf
Show file tree
Hide file tree
Showing 18 changed files with 677 additions and 427 deletions.
22 changes: 22 additions & 0 deletions __tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ test('it raises an error if an empty allow list is specified', async () => {
)
})

test('it raises an error when an invalid package-url is used for deny-packages', async () => {
setInput('deny-packages', 'not-a-purl')

await expect(readConfig()).rejects.toThrow(`Error parsing package-url`)
})

test('it raises an error when a nameless package-url is used for deny-packages', async () => {
setInput('deny-packages', 'pkg:npm/@namespace/')

await expect(readConfig()).rejects.toThrow(
`Error parsing package-url: name is required`
)
})

test('it raises an error when an argument to deny-groups is missing a namespace', async () => {
setInput('deny-groups', 'pkg:npm/my-fun-org')

await expect(readConfig()).rejects.toThrow(
`package-url must have a namespace`
)
})

test('it raises an error when given an unknown severity', async () => {
setInput('fail-on-severity', 'zombies')

Expand Down
19 changes: 19 additions & 0 deletions __tests__/deny.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,25 @@ test('denies packages that match the deny group list exactly', async () => {
expect(deniedChanges[0]).toBe(changes[1])
})

test(`denies packages using the namespace from the name when there's no package_url`, async () => {
const changes: Changes = [
createTestChange({
package_url: 'pkg:npm/org.test.pass/[email protected]',
ecosystem: 'npm'
}),
createTestChange({
name: 'org.test:deny-this',
package_url: '',
ecosystem: 'maven'
})
]
const deniedGroups = createTestPURLs(['pkg:maven/org.test/'])
const deniedChanges = await getDeniedChanges(changes, [], deniedGroups)

expect(deniedChanges.length).toEqual(1)
expect(deniedChanges[0]).toBe(changes[1])
})

test('allows packages not defined in the deny packages and groups list', async () => {
const changes: Changes = [npmChange, pipChange]
const deniedPackages = createTestPURLs([
Expand Down
3 changes: 1 addition & 2 deletions __tests__/fixtures/create-test-change.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {PackageURL} from 'packageurl-js'
import {Change} from '../../src/schemas'
import {createTestVulnerability} from './create-test-vulnerability'
import {parsePURL} from '../../src/utils'
import {PackageURL, parsePURL} from '../../src/purl'

const defaultNpmChange: Change = {
change_type: 'added',
Expand Down
162 changes: 162 additions & 0 deletions __tests__/purl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import {expect, test} from '@jest/globals'
import {parsePURL} from '../src/purl'

test('parsePURL returns an error if the purl does not start with "pkg:"', () => {
const purl = 'not-a-purl'
const result = parsePURL(purl)
expect(result.error).toEqual('package-url must start with "pkg:"')
})

test('parsePURL returns an error if the purl does not contain a type', () => {
const purl = 'pkg:/'
const result = parsePURL(purl)
expect(result.error).toEqual('package-url must contain a type')
})

test('parsePURL returns an error if the purl does not contain a namespace or name', () => {
const purl = 'pkg:ecosystem/'
const result = parsePURL(purl)
expect(result.type).toEqual('ecosystem')
expect(result.error).toEqual('package-url must contain a namespace or name')
})

test('parsePURL returns a PURL with the correct values in the happy case', () => {
const purl = 'pkg:ecosystem/namespace/name@version'
const result = parsePURL(purl)
expect(result.type).toEqual('ecosystem')
expect(result.namespace).toEqual('namespace')
expect(result.name).toEqual('name')
expect(result.version).toEqual('version')
expect(result.original).toEqual(purl)
expect(result.error).toBeNull()
})

test('parsePURL table test', () => {
const examples = [
{
purl: 'pkg:npm/@n4m3SPACE/Name@^1.2.3',
expected: {
type: 'npm',
namespace: '@n4m3SPACE',
name: 'Name',
version: '^1.2.3',
original: 'pkg:npm/@n4m3SPACE/Name@^1.2.3',
error: null
}
},
{
purl: 'pkg:npm/%40ns%20foo/n%40me@1.%2f2.3',
expected: {
type: 'npm',
namespace: '@ns foo',
name: 'n@me',
version: '1./2.3',
original: 'pkg:npm/%40ns%20foo/n%40me@1.%2f2.3',
error: null
}
},
{
purl: 'pkg:ecosystem/name@version',
expected: {
type: 'ecosystem',
namespace: null,
name: 'name',
version: 'version',
original: 'pkg:ecosystem/name@version',
error: null
}
},
{
purl: 'pkg:npm/namespace/',
expected: {
type: 'npm',
namespace: 'namespace',
name: null,
version: null,
original: 'pkg:npm/namespace/',
error: null
}
},
{
purl: 'pkg:ecosystem/name',
expected: {
type: 'ecosystem',
namespace: null,
name: 'name',
version: null,
original: 'pkg:ecosystem/name',
error: null
}
},
{
purl: 'pkg:/?',
expected: {
type: '',
namespace: null,
name: null,
version: null,
original: 'pkg:/?',
error: 'package-url must contain a type'
}
},
{
purl: 'pkg:ecosystem/#',
expected: {
type: 'ecosystem',
namespace: null,
name: null,
version: null,
original: 'pkg:ecosystem/#',
error: 'package-url must contain a namespace or name'
}
},
{
purl: 'pkg:ecosystem/name@version#subpath?attributes=123',
expected: {
type: 'ecosystem',
namespace: null,
name: 'name',
version: 'version',
original: 'pkg:ecosystem/name@version#subpath?attributes=123',
error: null
}
},
{
purl: 'pkg:ecosystem/name@version#subpath',
expected: {
type: 'ecosystem',
namespace: null,
name: 'name',
version: 'version',
original: 'pkg:ecosystem/name@version#subpath',
error: null
}
},
{
purl: 'pkg:ecosystem/namespace/name@version?attributes',
expected: {
type: 'ecosystem',
namespace: 'namespace',
name: 'name',
version: 'version',
original: 'pkg:ecosystem/namespace/name@version?attributes',
error: null
}
},
{
purl: 'pkg:ecosystem/name#subpath?attributes',
expected: {
type: 'ecosystem',
namespace: null,
name: 'name',
version: null,
original: 'pkg:ecosystem/name#subpath?attributes',
error: null
}
}
]
for (const example of examples) {
const result = parsePURL(example.purl)
expect(result).toEqual(example.expected)
}
})
4 changes: 3 additions & 1 deletion __tests__/test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export function clearInputs(): void {
'BASE-REF',
'HEAD-REF',
'COMMENT-SUMMARY-IN-PR',
'WARN-ONLY'
'WARN-ONLY',
'DENY-GROUPS',
'DENY-PACKAGES'
]

// eslint-disable-next-line github/array-foreach
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ inputs:
description: A comma-separated list of package URLs to deny (e.g. "pkg:npm/express, pkg:pypi/pycrypto"). If version specified, only deny matching packages and version; else, deny all regardless of version.
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:pypi/pycrypto")
description: A comma-separated list of package URLs for group(s)/namespace(s) to deny (e.g. "pkg:npm/express/, pkg:pypi/pycrypto/"). Please note that the group name must be followed by a `/`.
required: false
retry-on-snapshot-warnings:
description: Whether to retry on snapshot warnings
Expand Down
Loading

0 comments on commit 7dcfabf

Please sign in to comment.