From 9ad7edb033732c61e69c85d649c21e612fa9ad57 Mon Sep 17 00:00:00 2001 From: Stefan Petrushevski Date: Thu, 6 Apr 2023 09:37:42 +0200 Subject: [PATCH] switched to purl format --- package-lock.json | 11 +++++++++++ package.json | 3 +-- scripts/create_summary.ts | 10 ++++++---- src/config.ts | 34 ++++++++++++++++++++++++---------- src/licenses.ts | 39 ++++++++++++++++++++++++++++++++++----- src/main.ts | 4 +++- src/schemas.ts | 4 +--- src/summary.ts | 4 +--- 8 files changed, 81 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index c8abae7..d8bac78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "got": "^12.6.0", "nodemon": "^2.0.21", "octokit": "^2.0.14", + "packageurl-js": "^1.0.2", "spdx-expression-parse": "^3.0.1", "spdx-satisfies": "^5.0.1", "yaml": "^2.2.1", @@ -7120,6 +7121,11 @@ "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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -13990,6 +13996,11 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", diff --git a/package.json b/package.json index 68ce216..44f54dc 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "got": "^12.6.0", "nodemon": "^2.0.21", "octokit": "^2.0.14", + "packageurl-js": "^1.0.2", "spdx-expression-parse": "^3.0.1", "spdx-satisfies": "^5.0.1", "yaml": "^2.2.1", @@ -41,8 +42,6 @@ "devDependencies": { "@types/jest": "^27.5.2", "@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-satisfies": "^0.1.0", "@typescript-eslint/eslint-plugin": "^5.54.0", diff --git a/scripts/create_summary.ts b/scripts/create_summary.ts index ca1affa..625fed0 100644 --- a/scripts/create_summary.ts +++ b/scripts/create_summary.ts @@ -22,10 +22,12 @@ const defaultConfig: ConfigurationOptions = { allow_ghsas: [], allow_licenses: ['MIT'], deny_licenses: [], - allow_dependencies_licenses: { - npm: ['pkg1'], - pip: ['pkg2'] - }, + allow_dependencies_licenses: [ + 'pkg:npm/express@4.17.1', + 'pkg:pip/requests', + 'pkg:pip/certifi', + 'pkg:pip/pycrypto@2.6.1' + ], comment_summary_in_pr: true } diff --git a/src/config.ts b/src/config.ts index 7082f98..7a395e5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,6 +5,7 @@ import * as core from '@actions/core' import * as z from 'zod' import {ConfigurationOptions, ConfigurationOptionsSchema} from './schemas' import {isSPDXValid, octokitClient} from './utils' +import {PackageURL} from 'packageurl-js' type ConfigurationOptionsPartial = Partial @@ -29,6 +30,9 @@ function readInlineConfig(): ConfigurationOptionsPartial { const fail_on_scopes = parseList(getOptionalInput('fail-on-scopes')) const allow_licenses = parseList(getOptionalInput('allow-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 license_check = getOptionalBoolean('license-check') const vulnerability_check = getOptionalBoolean('vulnerability-check') @@ -36,6 +40,7 @@ function readInlineConfig(): ConfigurationOptionsPartial { const head_ref = getOptionalInput('head-ref') const comment_summary_in_pr = getOptionalBoolean('comment-summary-in-pr') + validatepurl(allow_dependencies_licenses) validateLicenses('allow-licenses', allow_licenses) validateLicenses('deny-licenses', deny_licenses) @@ -44,6 +49,7 @@ function readInlineConfig(): ConfigurationOptionsPartial { fail_on_scopes, allow_licenses, deny_licenses, + allow_dependencies_licenses, allow_ghsas, license_check, vulnerability_check, @@ -150,17 +156,9 @@ function parseConfigFile(configData: string): ConfigurationOptionsPartial { validateLicenses(key, data[key]) } - // parse the allow-dependencies-licenses configs value - // per-ecosystem: ['pkg1', 'pkg2'] + // validate purls from the allow-dependencies-licenses if (key === 'allow-dependencies-licenses') { - const val = 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 + validatepurl(data[key]) } // 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') } } +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 +} diff --git a/src/licenses.ts b/src/licenses.ts index 490baa9..9c0ef62 100644 --- a/src/licenses.ts +++ b/src/licenses.ts @@ -1,6 +1,8 @@ import spdxSatisfies from 'spdx-satisfies' import {Change, Changes} from './schemas' 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 @@ -24,22 +26,49 @@ export async function getInvalidLicenseChanges( licenses: { allow?: string[] deny?: string[] - exception?: Record + licenseExclusions?: string[] } ): Promise { - 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) - // 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 => { + const changeAsPackageURL = new PackageURL( + change.ecosystem, + undefined, + change.name, + change.version, + undefined, + undefined + ) if ( - isDefined(exception) && - exception[change.ecosystem].includes(change.name) + isDefined(licenseExclusions) && + licenseExclusions.findIndex( + exclusion => + exclusion.type === changeAsPackageURL.type && + exclusion.name === changeAsPackageURL.name + ) !== -1 ) { return false } return true }) + core.info( + `Grouped changes after filter size: ${groupedChanges.licensed.length}` + ) const licensedChanges: Changes = groupedChanges.licensed const invalidLicenseChanges: InvalidLicenseChanges = { diff --git a/src/main.ts b/src/main.ts index 17a1863..b381ff6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -16,10 +16,12 @@ import {getRefs} from './git-refs' import {groupDependenciesByManifest} from './utils' import {commentPr} from './comment-pr' +import {PackageURL} from 'packageurl-js' async function run(): Promise { try { const config = await readConfig() + const refs = getRefs(config, github.context) const changes = await dependencyGraph.compare({ @@ -56,7 +58,7 @@ async function run(): Promise { { allow: config.allow_licenses, deny: config.deny_licenses, - exception: config.allow_dependencies_licenses + licenseExclusions: config.allow_dependencies_licenses } ) diff --git a/src/schemas.ts b/src/schemas.ts index 2caa4dc..b7268ea 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -40,9 +40,7 @@ export const ConfigurationOptionsSchema = z fail_on_scopes: z.array(z.enum(SCOPES)).default(['runtime']), allow_licenses: z.array(z.string()).optional(), deny_licenses: z.array(z.string()).optional(), - allow_dependencies_licenses: z - .record(z.string(), z.array(z.string())) - .optional(), + allow_dependencies_licenses: z.array(z.string()).optional(), allow_ghsas: z.array(z.string()).default([]), license_check: z.boolean().default(true), vulnerability_check: z.boolean().default(true), diff --git a/src/summary.ts b/src/summary.ts index bcc3250..bd1c904 100644 --- a/src/summary.ts +++ b/src/summary.ts @@ -149,9 +149,7 @@ export function addLicensesToSummary( for (const [ecosystem, dependencies] of Object.entries( config.allow_dependencies_licenses )) { - core.summary.addRaw( - `
  • ${ecosystem}: ${dependencies.join(', ')}
  • ` - ) + core.summary.addRaw(`
  • ${ecosystem}: ${dependencies}
  • `) } }