Skip to content

Commit 2034bab

Browse files
authored
Bypass purls (mostly) for deny checks
1 parent 7e773b1 commit 2034bab

File tree

6 files changed

+172
-41
lines changed

6 files changed

+172
-41
lines changed

dist/index.js

Lines changed: 100 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/config.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ function validatePURL(allow_dependencies_licenses: string[] | undefined): void {
233233
return
234234
}
235235
const invalid_purls = allow_dependencies_licenses.filter(
236-
purl => !PackageURL.fromString(purl)
236+
purl => !isPURLValid(purl)
237237
)
238238

239239
if (invalid_purls.length > 0) {
@@ -243,3 +243,11 @@ function validatePURL(allow_dependencies_licenses: string[] | undefined): void {
243243
}
244244
return
245245
}
246+
247+
const isPURLValid = (purl: string): boolean => {
248+
try {
249+
return PackageURL.fromString(purl) !== null
250+
} catch (error) {
251+
return false
252+
}
253+
}

src/deny.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as core from '@actions/core'
22
import {Change} from './schemas'
33
import {PackageURL} from 'packageurl-js'
4+
import {parsePURL} from './utils'
45

56
export async function getDeniedChanges(
67
changes: Change[],
@@ -11,29 +12,19 @@ export async function getDeniedChanges(
1112

1213
let hasDeniedPackage = false
1314
for (const change of changes) {
14-
let changedPackage: PackageURL
15-
try {
16-
changedPackage = PackageURL.fromString(change.package_url)
17-
} catch (error) {
18-
core.error(`Error parsing package URL '${change.package_url}': ${error}`)
19-
continue
20-
}
21-
2215
for (const denied of deniedPackages) {
2316
if (
24-
(!denied.version || changedPackage.version === denied.version) &&
25-
changedPackage.name === denied.name
17+
(!denied.version || change.version === denied.version) &&
18+
change.name === denied.name
2619
) {
2720
changesDenied.push(change)
2821
hasDeniedPackage = true
2922
}
3023
}
3124

3225
for (const denied of deniedGroups) {
33-
if (
34-
changedPackage.namespace &&
35-
changedPackage.namespace === denied.namespace
36-
) {
26+
const namespace = getNamespace(change)
27+
if (namespace && namespace === denied.namespace) {
3728
changesDenied.push(change)
3829
hasDeniedPackage = true
3930
}
@@ -48,3 +39,31 @@ export async function getDeniedChanges(
4839

4940
return changesDenied
5041
}
42+
43+
// getNamespace returns the namespace associated with the given change.
44+
// it tries to get this from the package_url member, but that won't exist
45+
// for all changes, so as a fallback it may create a new purl based on the
46+
// ecosystem and name associated with the change, then extract the namespace
47+
// from that.
48+
// returns '' if there is no namespace.
49+
export const getNamespace = (change: Change): string => {
50+
let purl_str: string
51+
if (change.package_url) {
52+
purl_str = change.package_url
53+
} else {
54+
purl_str = `pkg:${change.ecosystem}/${change.name}`
55+
}
56+
57+
try {
58+
const purl = parsePURL(purl_str)
59+
const namespace = purl.namespace
60+
if (namespace === undefined || namespace === null) {
61+
return ''
62+
} else {
63+
return namespace
64+
}
65+
} catch (e) {
66+
core.error(`Error parsing purl '${purl_str}': ${e}`)
67+
return ''
68+
}
69+
}

src/licenses.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import spdxSatisfies from 'spdx-satisfies'
22
import {Change, Changes} from './schemas'
3-
import {isSPDXValid, octokitClient} from './utils'
3+
import {isSPDXValid, octokitClient, parsePURL} from './utils'
44
import {PackageURL} from 'packageurl-js'
55

66
/**
@@ -45,9 +45,7 @@ export async function getInvalidLicenseChanges(
4545
return true
4646
}
4747

48-
const changeAsPackageURL = PackageURL.fromString(
49-
encodeURI(change.package_url)
50-
)
48+
const changeAsPackageURL = parsePURL(encodeURI(change.package_url))
5149

5250
// We want to find if the licenseExclussion list contains the PackageURL of the Change
5351
// If it does, we want to filter it out and therefore return false

src/utils.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,36 @@ export const parsePURL = (purlString: string): PackageURL => {
8080
) {
8181
//packageurl-js does not support empty names, so will manually override it for deny-groups
8282
//https://github.com/package-url/packageurl-js/blob/master/src/package-url.js#L216
83-
const purl = PackageURL.fromString(`${purlString}TEMP_NAME`)
83+
const fixedPurlString = addTempName(purlString)
84+
const purl = PackageURL.fromString(fixedPurlString)
8485
purl.name = ''
8586
return purl
87+
} else if ((error as Error).message === `version must be percent-encoded`) {
88+
core.error(
89+
`Version must be percent-encoded. Removing version from purl: '${purlString}.`
90+
)
91+
const fixedPurlString = removeVersion(purlString)
92+
const purl = parsePURL(fixedPurlString)
93+
purl.version = ''
94+
return purl
8695
}
96+
core.error(`Error parsing purl: ${purlString}`)
8797
throw error
8898
}
8999
}
100+
101+
export const removeVersion = (purlString: string): string => {
102+
// sometimes these errors are actually caused by a final '/', so try removing that first
103+
if (purlString.endsWith('/')) {
104+
return purlString.substring(0, purlString.length - 1)
105+
}
106+
const idx = purlString.lastIndexOf('@')
107+
return purlString.substring(0, idx)
108+
}
109+
110+
export const addTempName = (purlString: string): string => {
111+
if (purlString.endsWith('/')) {
112+
return `${purlString}TEMP_NAME`
113+
}
114+
return `${purlString}/TEMP_NAME`
115+
}

0 commit comments

Comments
 (0)