switched to purl format

This commit is contained in:
Stefan Petrushevski
2023-04-06 09:37:42 +02:00
parent e17845d155
commit 9ad7edb033
8 changed files with 81 additions and 28 deletions
+11
View File
@@ -17,6 +17,7 @@
"got": "^12.6.0", "got": "^12.6.0",
"nodemon": "^2.0.21", "nodemon": "^2.0.21",
"octokit": "^2.0.14", "octokit": "^2.0.14",
"packageurl-js": "^1.0.2",
"spdx-expression-parse": "^3.0.1", "spdx-expression-parse": "^3.0.1",
"spdx-satisfies": "^5.0.1", "spdx-satisfies": "^5.0.1",
"yaml": "^2.2.1", "yaml": "^2.2.1",
@@ -7120,6 +7121,11 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/packageurl-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/packageurl-js/-/packageurl-js-1.0.2.tgz",
"integrity": "sha512-fWC4ZPxo80qlh3xN5FxfIoQD3phVY4+EyzTIqyksjhKNDmaicdpxSvkWwIrYTtv9C1/RcUN6pxaTwGmj2NzS6A=="
},
"node_modules/parent-module": { "node_modules/parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -13990,6 +13996,11 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true "dev": true
}, },
"packageurl-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/packageurl-js/-/packageurl-js-1.0.2.tgz",
"integrity": "sha512-fWC4ZPxo80qlh3xN5FxfIoQD3phVY4+EyzTIqyksjhKNDmaicdpxSvkWwIrYTtv9C1/RcUN6pxaTwGmj2NzS6A=="
},
"parent-module": { "parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+1 -2
View File
@@ -33,6 +33,7 @@
"got": "^12.6.0", "got": "^12.6.0",
"nodemon": "^2.0.21", "nodemon": "^2.0.21",
"octokit": "^2.0.14", "octokit": "^2.0.14",
"packageurl-js": "^1.0.2",
"spdx-expression-parse": "^3.0.1", "spdx-expression-parse": "^3.0.1",
"spdx-satisfies": "^5.0.1", "spdx-satisfies": "^5.0.1",
"yaml": "^2.2.1", "yaml": "^2.2.1",
@@ -41,8 +42,6 @@
"devDependencies": { "devDependencies": {
"@types/jest": "^27.5.2", "@types/jest": "^27.5.2",
"@types/node": "^16.18.14", "@types/node": "^16.18.14",
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.0",
"@types/spdx-expression-parse": "^3.0.2", "@types/spdx-expression-parse": "^3.0.2",
"@types/spdx-satisfies": "^0.1.0", "@types/spdx-satisfies": "^0.1.0",
"@typescript-eslint/eslint-plugin": "^5.54.0", "@typescript-eslint/eslint-plugin": "^5.54.0",
+6 -4
View File
@@ -22,10 +22,12 @@ const defaultConfig: ConfigurationOptions = {
allow_ghsas: [], allow_ghsas: [],
allow_licenses: ['MIT'], allow_licenses: ['MIT'],
deny_licenses: [], deny_licenses: [],
allow_dependencies_licenses: { allow_dependencies_licenses: [
npm: ['pkg1'], 'pkg:npm/[email protected]',
pip: ['pkg2'] 'pkg:pip/requests',
}, 'pkg:pip/certifi',
'pkg:pip/[email protected]'
],
comment_summary_in_pr: true comment_summary_in_pr: true
} }
+24 -10
View File
@@ -5,6 +5,7 @@ import * as core from '@actions/core'
import * as z from 'zod' import * as z from 'zod'
import {ConfigurationOptions, ConfigurationOptionsSchema} from './schemas' import {ConfigurationOptions, ConfigurationOptionsSchema} from './schemas'
import {isSPDXValid, octokitClient} from './utils' import {isSPDXValid, octokitClient} from './utils'
import {PackageURL} from 'packageurl-js'
type ConfigurationOptionsPartial = Partial<ConfigurationOptions> type ConfigurationOptionsPartial = Partial<ConfigurationOptions>
@@ -29,6 +30,9 @@ function readInlineConfig(): ConfigurationOptionsPartial {
const fail_on_scopes = parseList(getOptionalInput('fail-on-scopes')) const fail_on_scopes = parseList(getOptionalInput('fail-on-scopes'))
const allow_licenses = parseList(getOptionalInput('allow-licenses')) const allow_licenses = parseList(getOptionalInput('allow-licenses'))
const deny_licenses = parseList(getOptionalInput('deny-licenses')) const deny_licenses = parseList(getOptionalInput('deny-licenses'))
const allow_dependencies_licenses = parseList(
getOptionalInput('allow-dependencies-licenses')
)
const allow_ghsas = parseList(getOptionalInput('allow-ghsas')) const allow_ghsas = parseList(getOptionalInput('allow-ghsas'))
const license_check = getOptionalBoolean('license-check') const license_check = getOptionalBoolean('license-check')
const vulnerability_check = getOptionalBoolean('vulnerability-check') const vulnerability_check = getOptionalBoolean('vulnerability-check')
@@ -36,6 +40,7 @@ function readInlineConfig(): ConfigurationOptionsPartial {
const head_ref = getOptionalInput('head-ref') const head_ref = getOptionalInput('head-ref')
const comment_summary_in_pr = getOptionalBoolean('comment-summary-in-pr') const comment_summary_in_pr = getOptionalBoolean('comment-summary-in-pr')
validatepurl(allow_dependencies_licenses)
validateLicenses('allow-licenses', allow_licenses) validateLicenses('allow-licenses', allow_licenses)
validateLicenses('deny-licenses', deny_licenses) validateLicenses('deny-licenses', deny_licenses)
@@ -44,6 +49,7 @@ function readInlineConfig(): ConfigurationOptionsPartial {
fail_on_scopes, fail_on_scopes,
allow_licenses, allow_licenses,
deny_licenses, deny_licenses,
allow_dependencies_licenses,
allow_ghsas, allow_ghsas,
license_check, license_check,
vulnerability_check, vulnerability_check,
@@ -150,17 +156,9 @@ function parseConfigFile(configData: string): ConfigurationOptionsPartial {
validateLicenses(key, data[key]) validateLicenses(key, data[key])
} }
// parse the allow-dependencies-licenses configs value // validate purls from the allow-dependencies-licenses
// per-ecosystem: ['pkg1', 'pkg2']
if (key === 'allow-dependencies-licenses') { if (key === 'allow-dependencies-licenses') {
const val = data[key] validatepurl(data[key])
for (const ecosystem of Object.keys(val)) {
const pkgs = val[ecosystem]
if (typeof pkgs === 'string') {
val[ecosystem] = pkgs.split(',').map(x => x.trim())
}
}
data[key] = val
} }
// get rid of the ugly dashes from the actions conventions // get rid of the ugly dashes from the actions conventions
@@ -201,3 +199,19 @@ async function getRemoteConfig(configOpts: {
throw new Error('Error fetching remote config file') throw new Error('Error fetching remote config file')
} }
} }
function validatepurl(allow_dependencies_licenses: string[] | undefined): void {
//validate that the provided elements of the string are in valid purl format
if (allow_dependencies_licenses === undefined) {
return
}
const invalid_purls = allow_dependencies_licenses.filter(
purl => !PackageURL.fromString(purl)
)
if (invalid_purls.length > 0) {
throw new Error(
`Invalid purl(s) in allow-dependencies-licenses: ${invalid_purls}`
)
}
return
}
+34 -5
View File
@@ -1,6 +1,8 @@
import spdxSatisfies from 'spdx-satisfies' import spdxSatisfies from 'spdx-satisfies'
import {Change, Changes} from './schemas' import {Change, Changes} from './schemas'
import {isSPDXValid, octokitClient, isDefined} from './utils' import {isSPDXValid, octokitClient, isDefined} from './utils'
import {PackageURL} from 'packageurl-js'
import * as core from '@actions/core'
/** /**
* Loops through a list of changes, filtering and returning the * Loops through a list of changes, filtering and returning the
@@ -24,22 +26,49 @@ export async function getInvalidLicenseChanges(
licenses: { licenses: {
allow?: string[] allow?: string[]
deny?: string[] deny?: string[]
exception?: Record<string, string[]> licenseExclusions?: string[]
} }
): Promise<InvalidLicenseChanges> { ): Promise<InvalidLicenseChanges> {
const {allow, deny, exception} = licenses const {allow, deny} = licenses
// licenseExclusions = licenseExclusions.map((pkgUrl: string) => {
// return PackageURL.fromString(pkgUrl)
// })
const licenseExclusions = licenses.licenseExclusions?.map(
(pkgUrl: string) => {
return PackageURL.fromString(pkgUrl)
}
)
const groupedChanges = await groupChanges(changes) const groupedChanges = await groupChanges(changes)
// filter out changes that are part of exclusions list - config.allow_license_exceptions core.info(
`Grouped changes BEFORE filter size: ${groupedChanges.licensed.length}`
)
// filter out changes that are part of exclusions list - config.allow_dependencies_licenses
groupedChanges.licensed = groupedChanges.licensed.filter(change => { groupedChanges.licensed = groupedChanges.licensed.filter(change => {
const changeAsPackageURL = new PackageURL(
change.ecosystem,
undefined,
change.name,
change.version,
undefined,
undefined
)
if ( if (
isDefined(exception) && isDefined(licenseExclusions) &&
exception[change.ecosystem].includes(change.name) licenseExclusions.findIndex(
exclusion =>
exclusion.type === changeAsPackageURL.type &&
exclusion.name === changeAsPackageURL.name
) !== -1
) { ) {
return false return false
} }
return true return true
}) })
core.info(
`Grouped changes after filter size: ${groupedChanges.licensed.length}`
)
const licensedChanges: Changes = groupedChanges.licensed const licensedChanges: Changes = groupedChanges.licensed
const invalidLicenseChanges: InvalidLicenseChanges = { const invalidLicenseChanges: InvalidLicenseChanges = {
+3 -1
View File
@@ -16,10 +16,12 @@ import {getRefs} from './git-refs'
import {groupDependenciesByManifest} from './utils' import {groupDependenciesByManifest} from './utils'
import {commentPr} from './comment-pr' import {commentPr} from './comment-pr'
import {PackageURL} from 'packageurl-js'
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const config = await readConfig() const config = await readConfig()
const refs = getRefs(config, github.context) const refs = getRefs(config, github.context)
const changes = await dependencyGraph.compare({ const changes = await dependencyGraph.compare({
@@ -56,7 +58,7 @@ async function run(): Promise<void> {
{ {
allow: config.allow_licenses, allow: config.allow_licenses,
deny: config.deny_licenses, deny: config.deny_licenses,
exception: config.allow_dependencies_licenses licenseExclusions: config.allow_dependencies_licenses
} }
) )
+1 -3
View File
@@ -40,9 +40,7 @@ export const ConfigurationOptionsSchema = z
fail_on_scopes: z.array(z.enum(SCOPES)).default(['runtime']), fail_on_scopes: z.array(z.enum(SCOPES)).default(['runtime']),
allow_licenses: z.array(z.string()).optional(), allow_licenses: z.array(z.string()).optional(),
deny_licenses: z.array(z.string()).optional(), deny_licenses: z.array(z.string()).optional(),
allow_dependencies_licenses: z allow_dependencies_licenses: z.array(z.string()).optional(),
.record(z.string(), z.array(z.string()))
.optional(),
allow_ghsas: z.array(z.string()).default([]), allow_ghsas: z.array(z.string()).default([]),
license_check: z.boolean().default(true), license_check: z.boolean().default(true),
vulnerability_check: z.boolean().default(true), vulnerability_check: z.boolean().default(true),
+1 -3
View File
@@ -149,9 +149,7 @@ export function addLicensesToSummary(
for (const [ecosystem, dependencies] of Object.entries( for (const [ecosystem, dependencies] of Object.entries(
config.allow_dependencies_licenses config.allow_dependencies_licenses
)) { )) {
core.summary.addRaw( core.summary.addRaw(`<li> ${ecosystem}</strong>: ${dependencies} </li>`)
`<li> ${ecosystem}</strong>: ${dependencies.join(', ')} </li>`
)
} }
} }