Skip to content

Commit 7dcfabf

Browse files
authored
Merge pull request #753 from actions/juxtin/debug-purl
Parse purls cautiously in getDeniedChanges
2 parents 0659a74 + 5f0808f commit 7dcfabf

18 files changed

+677
-427
lines changed

__tests__/config.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,28 @@ test('it raises an error if an empty allow list is specified', async () => {
5454
)
5555
})
5656

57+
test('it raises an error when an invalid package-url is used for deny-packages', async () => {
58+
setInput('deny-packages', 'not-a-purl')
59+
60+
await expect(readConfig()).rejects.toThrow(`Error parsing package-url`)
61+
})
62+
63+
test('it raises an error when a nameless package-url is used for deny-packages', async () => {
64+
setInput('deny-packages', 'pkg:npm/@namespace/')
65+
66+
await expect(readConfig()).rejects.toThrow(
67+
`Error parsing package-url: name is required`
68+
)
69+
})
70+
71+
test('it raises an error when an argument to deny-groups is missing a namespace', async () => {
72+
setInput('deny-groups', 'pkg:npm/my-fun-org')
73+
74+
await expect(readConfig()).rejects.toThrow(
75+
`package-url must have a namespace`
76+
)
77+
})
78+
5779
test('it raises an error when given an unknown severity', async () => {
5880
setInput('fail-on-severity', 'zombies')
5981

__tests__/deny.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,25 @@ test('denies packages that match the deny group list exactly', async () => {
106106
expect(deniedChanges[0]).toBe(changes[1])
107107
})
108108

109+
test(`denies packages using the namespace from the name when there's no package_url`, async () => {
110+
const changes: Changes = [
111+
createTestChange({
112+
package_url: 'pkg:npm/org.test.pass/[email protected]',
113+
ecosystem: 'npm'
114+
}),
115+
createTestChange({
116+
name: 'org.test:deny-this',
117+
package_url: '',
118+
ecosystem: 'maven'
119+
})
120+
]
121+
const deniedGroups = createTestPURLs(['pkg:maven/org.test/'])
122+
const deniedChanges = await getDeniedChanges(changes, [], deniedGroups)
123+
124+
expect(deniedChanges.length).toEqual(1)
125+
expect(deniedChanges[0]).toBe(changes[1])
126+
})
127+
109128
test('allows packages not defined in the deny packages and groups list', async () => {
110129
const changes: Changes = [npmChange, pipChange]
111130
const deniedPackages = createTestPURLs([

__tests__/fixtures/create-test-change.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import {PackageURL} from 'packageurl-js'
21
import {Change} from '../../src/schemas'
32
import {createTestVulnerability} from './create-test-vulnerability'
4-
import {parsePURL} from '../../src/utils'
3+
import {PackageURL, parsePURL} from '../../src/purl'
54

65
const defaultNpmChange: Change = {
76
change_type: 'added',

__tests__/purl.test.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import {expect, test} from '@jest/globals'
2+
import {parsePURL} from '../src/purl'
3+
4+
test('parsePURL returns an error if the purl does not start with "pkg:"', () => {
5+
const purl = 'not-a-purl'
6+
const result = parsePURL(purl)
7+
expect(result.error).toEqual('package-url must start with "pkg:"')
8+
})
9+
10+
test('parsePURL returns an error if the purl does not contain a type', () => {
11+
const purl = 'pkg:/'
12+
const result = parsePURL(purl)
13+
expect(result.error).toEqual('package-url must contain a type')
14+
})
15+
16+
test('parsePURL returns an error if the purl does not contain a namespace or name', () => {
17+
const purl = 'pkg:ecosystem/'
18+
const result = parsePURL(purl)
19+
expect(result.type).toEqual('ecosystem')
20+
expect(result.error).toEqual('package-url must contain a namespace or name')
21+
})
22+
23+
test('parsePURL returns a PURL with the correct values in the happy case', () => {
24+
const purl = 'pkg:ecosystem/namespace/name@version'
25+
const result = parsePURL(purl)
26+
expect(result.type).toEqual('ecosystem')
27+
expect(result.namespace).toEqual('namespace')
28+
expect(result.name).toEqual('name')
29+
expect(result.version).toEqual('version')
30+
expect(result.original).toEqual(purl)
31+
expect(result.error).toBeNull()
32+
})
33+
34+
test('parsePURL table test', () => {
35+
const examples = [
36+
{
37+
purl: 'pkg:npm/@n4m3SPACE/Name@^1.2.3',
38+
expected: {
39+
type: 'npm',
40+
namespace: '@n4m3SPACE',
41+
name: 'Name',
42+
version: '^1.2.3',
43+
original: 'pkg:npm/@n4m3SPACE/Name@^1.2.3',
44+
error: null
45+
}
46+
},
47+
{
48+
purl: 'pkg:npm/%40ns%20foo/n%40me@1.%2f2.3',
49+
expected: {
50+
type: 'npm',
51+
namespace: '@ns foo',
52+
name: 'n@me',
53+
version: '1./2.3',
54+
original: 'pkg:npm/%40ns%20foo/n%40me@1.%2f2.3',
55+
error: null
56+
}
57+
},
58+
{
59+
purl: 'pkg:ecosystem/name@version',
60+
expected: {
61+
type: 'ecosystem',
62+
namespace: null,
63+
name: 'name',
64+
version: 'version',
65+
original: 'pkg:ecosystem/name@version',
66+
error: null
67+
}
68+
},
69+
{
70+
purl: 'pkg:npm/namespace/',
71+
expected: {
72+
type: 'npm',
73+
namespace: 'namespace',
74+
name: null,
75+
version: null,
76+
original: 'pkg:npm/namespace/',
77+
error: null
78+
}
79+
},
80+
{
81+
purl: 'pkg:ecosystem/name',
82+
expected: {
83+
type: 'ecosystem',
84+
namespace: null,
85+
name: 'name',
86+
version: null,
87+
original: 'pkg:ecosystem/name',
88+
error: null
89+
}
90+
},
91+
{
92+
purl: 'pkg:/?',
93+
expected: {
94+
type: '',
95+
namespace: null,
96+
name: null,
97+
version: null,
98+
original: 'pkg:/?',
99+
error: 'package-url must contain a type'
100+
}
101+
},
102+
{
103+
purl: 'pkg:ecosystem/#',
104+
expected: {
105+
type: 'ecosystem',
106+
namespace: null,
107+
name: null,
108+
version: null,
109+
original: 'pkg:ecosystem/#',
110+
error: 'package-url must contain a namespace or name'
111+
}
112+
},
113+
{
114+
purl: 'pkg:ecosystem/name@version#subpath?attributes=123',
115+
expected: {
116+
type: 'ecosystem',
117+
namespace: null,
118+
name: 'name',
119+
version: 'version',
120+
original: 'pkg:ecosystem/name@version#subpath?attributes=123',
121+
error: null
122+
}
123+
},
124+
{
125+
purl: 'pkg:ecosystem/name@version#subpath',
126+
expected: {
127+
type: 'ecosystem',
128+
namespace: null,
129+
name: 'name',
130+
version: 'version',
131+
original: 'pkg:ecosystem/name@version#subpath',
132+
error: null
133+
}
134+
},
135+
{
136+
purl: 'pkg:ecosystem/namespace/name@version?attributes',
137+
expected: {
138+
type: 'ecosystem',
139+
namespace: 'namespace',
140+
name: 'name',
141+
version: 'version',
142+
original: 'pkg:ecosystem/namespace/name@version?attributes',
143+
error: null
144+
}
145+
},
146+
{
147+
purl: 'pkg:ecosystem/name#subpath?attributes',
148+
expected: {
149+
type: 'ecosystem',
150+
namespace: null,
151+
name: 'name',
152+
version: null,
153+
original: 'pkg:ecosystem/name#subpath?attributes',
154+
error: null
155+
}
156+
}
157+
]
158+
for (const example of examples) {
159+
const result = parsePURL(example.purl)
160+
expect(result).toEqual(example.expected)
161+
}
162+
})

__tests__/test-helpers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ export function clearInputs(): void {
1919
'BASE-REF',
2020
'HEAD-REF',
2121
'COMMENT-SUMMARY-IN-PR',
22-
'WARN-ONLY'
22+
'WARN-ONLY',
23+
'DENY-GROUPS',
24+
'DENY-PACKAGES'
2325
]
2426

2527
// eslint-disable-next-line github/array-foreach

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ inputs:
5959
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.
6060
required: false
6161
deny-groups:
62-
description: A comma-separated list of package URLs for group(s)/namespace(s) to deny (e.g. "pkg:npm/express, pkg:pypi/pycrypto")
62+
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 `/`.
6363
required: false
6464
retry-on-snapshot-warnings:
6565
description: Whether to retry on snapshot warnings

0 commit comments

Comments
 (0)