diff --git a/package-lock.json b/package-lock.json index 1c226fe..c8abae7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@octokit/request-error": "^2.1.0", "ansi-styles": "^6.2.1", "got": "^12.6.0", + "nodemon": "^2.0.21", "octokit": "^2.0.14", "spdx-expression-parse": "^3.0.1", "spdx-satisfies": "^5.0.1", diff --git a/src/config.ts b/src/config.ts index 1c3db9d..7082f98 100644 --- a/src/config.ts +++ b/src/config.ts @@ -130,7 +130,8 @@ function parseConfigFile(configData: string): ConfigurationOptionsPartial { 'allow-licenses', 'deny-licenses', 'fail-on-scopes', - 'allow-ghsas' + 'allow-ghsas', + 'allow-dependencies-licenses' ] for (const key of Object.keys(data)) { @@ -149,6 +150,19 @@ function parseConfigFile(configData: string): ConfigurationOptionsPartial { validateLicenses(key, data[key]) } + // parse the allow-dependencies-licenses configs value + // per-ecosystem: ['pkg1', 'pkg2'] + 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 + } + // get rid of the ugly dashes from the actions conventions if (key.includes('-')) { data[key.replace(/-/g, '_')] = data[key] diff --git a/src/licenses.ts b/src/licenses.ts index ccd26ad..59b4459 100644 --- a/src/licenses.ts +++ b/src/licenses.ts @@ -1,6 +1,7 @@ import spdxSatisfies from 'spdx-satisfies' import {Change, Changes} from './schemas' -import {isSPDXValid, octokitClient} from './utils' +import {isSPDXValid, octokitClient, isDefined} from './utils' +import * as core from '@actions/core' /** * Loops through a list of changes, filtering and returning the @@ -24,11 +25,22 @@ export async function getInvalidLicenseChanges( licenses: { allow?: string[] deny?: string[] + exception?: Record } ): Promise { - const {allow, deny} = licenses + const {allow, deny, exception} = licenses const groupedChanges = await groupChanges(changes) + // filter out changes that are part of exclusions list - config.allow_license_exceptions + groupedChanges.licensed = groupedChanges.licensed.filter(change => { + if ( + isDefined(exception) && + exception[change.ecosystem].includes(change.name) + ) { + return false + } + return true + }) const licensedChanges: Changes = groupedChanges.licensed const invalidLicenseChanges: InvalidLicenseChanges = { diff --git a/src/main.ts b/src/main.ts index 39a2754..17a1863 100644 --- a/src/main.ts +++ b/src/main.ts @@ -55,7 +55,8 @@ async function run(): Promise { filteredChanges, { allow: config.allow_licenses, - deny: config.deny_licenses + deny: config.deny_licenses, + exception: config.allow_dependencies_licenses } ) diff --git a/src/schemas.ts b/src/schemas.ts index 523418a..2caa4dc 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -40,6 +40,9 @@ 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_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 e73ce56..05c1fe2 100644 --- a/src/summary.ts +++ b/src/summary.ts @@ -143,6 +143,16 @@ export function addLicensesToSummary( `Denied Licenses: ${config.deny_licenses.join(', ')}` ) } + if (config.allow_dependencies_licenses) { + core.summary.addRaw( + "Allow Dependencies (Exceptions)' Licenses: true" + ) + for (const [ecosystem, dependencies] of Object.entries( + config.allow_dependencies_licenses + )) { + core.summary.addRaw(`- ${ecosystem}: ${dependencies.join(', ')}`) + } + } core.debug( `found ${invalidLicenseChanges.unlicensed.length} unknown licenses` diff --git a/src/utils.ts b/src/utils.ts index 81af13d..3db4ad4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -41,6 +41,11 @@ export function isSPDXValid(license: string): boolean { } } +// function to check if a value is not null or undefined +export function isDefined(value: T | null | undefined): value is T { + return value !== null && value !== undefined +} + function isEnterprise(): boolean { const serverUrl = new URL( process.env['GITHUB_SERVER_URL'] ?? 'https://github.com'