From 33bcfbbcf02f7d0a066063195f43a9a8153e412f Mon Sep 17 00:00:00 2001 From: agusgroh Date: Tue, 20 Aug 2024 11:34:45 -0300 Subject: [PATCH 1/3] feat: SP-1343 Add configurable Copyleft license options to pipeline --- README.md | 25 +++++----- action.yml | 9 ++++ dist/index.js | 67 +++++++++++++++++++++++---- package-lock.json | 4 +- package.json | 2 +- src/app.input.ts | 3 ++ src/policies/copyleft-policy-check.ts | 18 +++---- src/utils/license.utils.ts | 23 +++++++++ 8 files changed, 119 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 8215e13..51706ae 100644 --- a/README.md +++ b/README.md @@ -52,17 +52,20 @@ For example workflow runs, check out our ### Action Input Parameters -| **Parameter** | **Description** | **Required** | **Default** | -|--------------------------|------------------------------------------------------------------------------------|--------------|-------------------------------------| -| output.filepath | Scan output file name. | Optional | `results.json` | -| sbom.enabled | Enable or disable scanning based on the SBOM file | Optional | `true` | -| sbom.filepath | Filepath of the SBOM file to be used for scanning | Optional | `sbom.json` | -| sbom.type | Type of SBOM operation: either 'identify' or 'ignore | Optional | `identify` | -| dependencies.enabled | Option to enable or disable scanning of dependencies. | Optional | `false` | -| policies | List of policies separated by commas, options available are: copyleft, undeclared. | Optional | - | -| policies.halt_on_failure | Halt check on policy failure. If set to false checks will not fail. | Optional | `true` | -| api.url | SCANOSS API URL | Optional | `https://osskb.org/api/scan/direct` | -| api.key | SCANOSS API Key | Optional | - | +| **Parameter** | **Description** | **Required** | **Default** | +|----------------------------|------------------------------------------------------------------------------------------------------|--------------|-------------------------------------| +| output.filepath | Scan output file name. | Optional | `results.json` | +| sbom.enabled | Enable or disable scanning based on the SBOM file | Optional | `true` | +| sbom.filepath | Filepath of the SBOM file to be used for scanning | Optional | `sbom.json` | +| sbom.type | Type of SBOM operation: either 'identify' or 'ignore | Optional | `identify` | +| dependencies.enabled | Option to enable or disable scanning of dependencies. | Optional | `false` | +| policies | List of policies separated by commas, options available are: copyleft, undeclared. | Optional | - | +| policies.halt_on_failure | Halt check on policy failure. If set to false checks will not fail. | Optional | `true` | +| api.url | SCANOSS API URL | Optional | `https://osskb.org/api/scan/direct` | +| api.key | SCANOSS API Key | Optional | - | +| licenses.copyleft.include | List of Copyleft licenses to append to the default list. Provide licenses as a comma-separated list. | Optional | - | +| licenses.copyleft.exclude | List of Copyleft licenses to remove from default list. Provide licenses as a comma-separated list. | Optional | - | +| licenses.copyleft.implicit | Explicit list of Copyleft licenses to consider. Provide licenses as a comma-separated list. | Optional | - | ### Action Output Parameters diff --git a/action.yml b/action.yml index d5bc7cc..645503e 100644 --- a/action.yml +++ b/action.yml @@ -43,6 +43,15 @@ inputs: description: 'Your GitHub token' required: false default: ${{ github.token }} + licenses.copyleft.include: + description: 'List of Copyleft licenses to append to the default list. Provide licenses as a comma-separated list.' + required: false + licenses.copyleft.exclude: + description: 'List of Copyleft licenses to remove from default list. Provide licenses as a comma-separated list.' + required: false + licenses.copyleft.explicit: + description: 'Explicit list of Copyleft licenses to consider. Provide licenses as a comma-separated list.' + required: false outputs: result-filepath: diff --git a/dist/index.js b/dist/index.js index b4eaf61..fd78825 100644 --- a/dist/index.js +++ b/dist/index.js @@ -125780,7 +125780,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.REPO_DIR = exports.GITHUB_TOKEN = exports.OUTPUT_FILEPATH = exports.API_URL = exports.API_KEY = exports.DEPENDENCIES_ENABLED = exports.SBOM_TYPE = exports.SBOM_FILEPATH = exports.SBOM_ENABLED = exports.POLICIES_HALT_ON_FAILURE = exports.POLICIES = void 0; +exports.REPO_DIR = exports.COPYLEFT_LICENSE_EXPLICIT = exports.COPYLEFT_LICENSE_EXCLUDE = exports.COPYLEFT_LICENSE_INCLUDE = exports.GITHUB_TOKEN = exports.OUTPUT_FILEPATH = exports.API_URL = exports.API_KEY = exports.DEPENDENCIES_ENABLED = exports.SBOM_TYPE = exports.SBOM_FILEPATH = exports.SBOM_ENABLED = exports.POLICIES_HALT_ON_FAILURE = exports.POLICIES = void 0; const core = __importStar(__nccwpck_require__(42186)); exports.POLICIES = core.getInput('policies'); exports.POLICIES_HALT_ON_FAILURE = core.getInput('policies.halt_on_failure') === 'true'; @@ -125792,6 +125792,9 @@ exports.API_KEY = core.getInput('api.key'); exports.API_URL = core.getInput('api.url'); exports.OUTPUT_FILEPATH = core.getInput('output.filepath'); exports.GITHUB_TOKEN = core.getInput('github.token'); +exports.COPYLEFT_LICENSE_INCLUDE = core.getInput('licenses.copyleft.include'); +exports.COPYLEFT_LICENSE_EXCLUDE = core.getInput('licenses.copyleft.exclude'); +exports.COPYLEFT_LICENSE_EXPLICIT = core.getInput('licenses.copyleft.explicit'); exports.REPO_DIR = process.env.GITHUB_WORKSPACE; @@ -126032,14 +126035,16 @@ class CopyleftPolicyCheck extends policy_check_1.PolicyCheck { const rows = []; components.forEach(component => { component.licenses.forEach(license => { - const copyleftIcon = license_utils_1.licenseUtil.isCopyLeft(license.spdxid?.trim().toLowerCase()) ? 'YES' : 'NO'; - rows.push([ - component.purl, - component.version, - license.spdxid, - `${license_utils_1.licenseUtil.getOSADL(license?.spdxid) || ''}`, - copyleftIcon - ]); + if (license_utils_1.licenseUtil.isCopyLeft(license.spdxid?.trim().toLowerCase())) { + const copyleftIcon = license_utils_1.licenseUtil.isCopyLeft(license.spdxid?.trim().toLowerCase()) ? 'YES' : 'NO'; + rows.push([ + component.purl, + component.version, + license.spdxid, + `${license_utils_1.licenseUtil.getOSADL(license?.spdxid) || ''}`, + copyleftIcon + ]); + } }); }); return `### Copyleft licenses \n ${(0, markdown_utils_1.generateTable)(headers, rows, centeredColumns)}`; @@ -127011,12 +127016,37 @@ exports.createCommentOnPR = createCommentOnPR; /***/ }), /***/ 52210: -/***/ ((__unused_webpack_module, exports) => { +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.licenseUtil = exports.LicenseUtil = void 0; +const inputs = __importStar(__nccwpck_require__(483)); +const core = __importStar(__nccwpck_require__(42186)); class LicenseUtil { BASE_OSADL_URL = 'https://spdx.org/licenses'; HTML = 'html'; @@ -127048,7 +127078,24 @@ class LicenseUtil { ].map(l => l.toLowerCase())); copyLeftLicenses = new Set(); init() { + if (inputs.COPYLEFT_LICENSE_EXPLICIT) { + const explicitCopyleftLicenses = inputs.COPYLEFT_LICENSE_EXPLICIT.split(',').map(pn => pn.trim().toLowerCase()); + core.debug(`Explicit licenses: ${explicitCopyleftLicenses}`); + this.copyLeftLicenses = new Set(explicitCopyleftLicenses); + return; + } + core.debug(`Explicit licenses not defined, setting default licenses...`); this.copyLeftLicenses = this.defaultCopyleftLicenses; + if (inputs.COPYLEFT_LICENSE_INCLUDE) { + const includedCopyleftLicenses = inputs.COPYLEFT_LICENSE_INCLUDE.split(',').map(pn => pn.trim()); + core.debug(`Included copyleft licenses: ${includedCopyleftLicenses}`); + includedCopyleftLicenses.forEach(l => this.copyLeftLicenses.add(l.toLowerCase())); + } + if (inputs.COPYLEFT_LICENSE_EXCLUDE) { + const excludedCopyleftLicenses = inputs.COPYLEFT_LICENSE_EXCLUDE.split(',').map(pn => pn.trim()); + core.debug(`Excluded copyleft licenses: ${excludedCopyleftLicenses}`); + excludedCopyleftLicenses.forEach(l => this.copyLeftLicenses.delete(l.toLowerCase())); + } } isCopyLeft(spdxid) { return this.copyLeftLicenses.has(spdxid); diff --git a/package-lock.json b/package-lock.json index 868116c..3a5f42d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "scanoss-code-scan-action", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scanoss-code-scan-action", - "version": "0.1.7", + "version": "0.1.8", "license": "MIT", "dependencies": { "@actions/artifact": "^2.1.0", diff --git a/package.json b/package.json index 557891e..e4779fd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "scanoss-code-scan-action", "description": "SCANOSS Code Scan Action", - "version": "0.1.7", + "version": "0.1.8", "author": "SCANOSS", "private": true, "homepage": "https://github.com/scanoss/code-scan-action/", diff --git a/src/app.input.ts b/src/app.input.ts index c8fa341..7578d4f 100644 --- a/src/app.input.ts +++ b/src/app.input.ts @@ -33,4 +33,7 @@ export const API_KEY = core.getInput('api.key'); export const API_URL = core.getInput('api.url'); export const OUTPUT_FILEPATH = core.getInput('output.filepath'); export const GITHUB_TOKEN = core.getInput('github.token'); +export const COPYLEFT_LICENSE_INCLUDE = core.getInput('licenses.copyleft.include'); +export const COPYLEFT_LICENSE_EXCLUDE = core.getInput('licenses.copyleft.exclude'); +export const COPYLEFT_LICENSE_EXPLICIT = core.getInput('licenses.copyleft.explicit'); export const REPO_DIR = process.env.GITHUB_WORKSPACE as string; diff --git a/src/policies/copyleft-policy-check.ts b/src/policies/copyleft-policy-check.ts index 8f9aed5..b9ccefd 100644 --- a/src/policies/copyleft-policy-check.ts +++ b/src/policies/copyleft-policy-check.ts @@ -106,14 +106,16 @@ export class CopyleftPolicyCheck extends PolicyCheck { components.forEach(component => { component.licenses.forEach(license => { - const copyleftIcon = licenseUtil.isCopyLeft(license.spdxid?.trim().toLowerCase()) ? 'YES' : 'NO'; - rows.push([ - component.purl, - component.version, - license.spdxid, - `${licenseUtil.getOSADL(license?.spdxid) || ''}`, - copyleftIcon - ]); + if (licenseUtil.isCopyLeft(license.spdxid?.trim().toLowerCase())) { + const copyleftIcon = licenseUtil.isCopyLeft(license.spdxid?.trim().toLowerCase()) ? 'YES' : 'NO'; + rows.push([ + component.purl, + component.version, + license.spdxid, + `${licenseUtil.getOSADL(license?.spdxid) || ''}`, + copyleftIcon + ]); + } }); }); return `### Copyleft licenses \n ${generateTable(headers, rows, centeredColumns)}`; diff --git a/src/utils/license.utils.ts b/src/utils/license.utils.ts index 970ed36..52401c0 100644 --- a/src/utils/license.utils.ts +++ b/src/utils/license.utils.ts @@ -1,3 +1,6 @@ +import * as inputs from '../app.input'; +import * as core from '@actions/core'; + export class LicenseUtil { private BASE_OSADL_URL = 'https://spdx.org/licenses'; private HTML = 'html'; @@ -34,7 +37,27 @@ export class LicenseUtil { private copyLeftLicenses = new Set(); private init(): void { + if (inputs.COPYLEFT_LICENSE_EXPLICIT) { + const explicitCopyleftLicenses = inputs.COPYLEFT_LICENSE_EXPLICIT.split(',').map(pn => pn.trim().toLowerCase()); + core.debug(`Explicit licenses: ${explicitCopyleftLicenses}`); + this.copyLeftLicenses = new Set(explicitCopyleftLicenses); + return; + } + + core.debug(`Explicit licenses not defined, setting default licenses...`); this.copyLeftLicenses = this.defaultCopyleftLicenses; + + if (inputs.COPYLEFT_LICENSE_INCLUDE) { + const includedCopyleftLicenses = inputs.COPYLEFT_LICENSE_INCLUDE.split(',').map(pn => pn.trim()); + core.debug(`Included copyleft licenses: ${includedCopyleftLicenses}`); + includedCopyleftLicenses.forEach(l => this.copyLeftLicenses.add(l.toLowerCase())); + } + + if (inputs.COPYLEFT_LICENSE_EXCLUDE) { + const excludedCopyleftLicenses = inputs.COPYLEFT_LICENSE_EXCLUDE.split(',').map(pn => pn.trim()); + core.debug(`Excluded copyleft licenses: ${excludedCopyleftLicenses}`); + excludedCopyleftLicenses.forEach(l => this.copyLeftLicenses.delete(l.toLowerCase())); + } } isCopyLeft(spdxid: string): boolean { From 8c862cc181f6db390b36267dae847de630cd04ba Mon Sep 17 00:00:00 2001 From: agusgroh Date: Wed, 21 Aug 2024 08:36:30 -0300 Subject: [PATCH 2/3] chore: SP-1357 Adds reference to default copyleft license list in README.md file --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 51706ae..073555e 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ For example workflow runs, check out our | api.key | SCANOSS API Key | Optional | - | | licenses.copyleft.include | List of Copyleft licenses to append to the default list. Provide licenses as a comma-separated list. | Optional | - | | licenses.copyleft.exclude | List of Copyleft licenses to remove from default list. Provide licenses as a comma-separated list. | Optional | - | -| licenses.copyleft.implicit | Explicit list of Copyleft licenses to consider. Provide licenses as a comma-separated list. | Optional | - | +| licenses.copyleft.explicit | Explicit list of Copyleft licenses to consider. Provide licenses as a comma-separated list. | Optional | - | ### Action Output Parameters @@ -81,7 +81,7 @@ the output into your custom workflow The SCANOSS Code Scan Action includes two configurable policies: 1. Copyleft: This policy checks if any component or code snippet is associated with a copyleft license. If such a - license is detected, the pull request (PR) is rejected. + license is detected, the pull request (PR) is rejected. The default list of Copyleft licenses is defined in the following [file](https://github.com/scanoss/gha-code-scan/blob/main/src/utils/license.utils.ts). 2. Undeclared: This policy compares the components detected in the repository against those declared in an sbom.json file (customizable through the sbom.filepath parameter). If there are undeclared components, the PR is rejected. From 170f2806cecd9cef07c1f6b630d17c935f39bbde Mon Sep 17 00:00:00 2001 From: agusgroh Date: Wed, 21 Aug 2024 12:17:32 -0300 Subject: [PATCH 3/3] Upgrades version to v0.2.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3a5f42d..c36a0ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "scanoss-code-scan-action", - "version": "0.1.8", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scanoss-code-scan-action", - "version": "0.1.8", + "version": "0.2.0", "license": "MIT", "dependencies": { "@actions/artifact": "^2.1.0", diff --git a/package.json b/package.json index e4779fd..2e9212f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "scanoss-code-scan-action", "description": "SCANOSS Code Scan Action", - "version": "0.1.8", + "version": "0.2.0", "author": "SCANOSS", "private": true, "homepage": "https://github.com/scanoss/code-scan-action/",