Compare commits

...
Author SHA1 Message Date
Federico Builes 5ea8fbfb83 Update docs for config file paths. 2022-11-10 08:18:58 +01:00
Federico BuilesandCourtney Claessens c72eb06e71 Update README.md
Co-authored-by: Courtney Claessens <[email protected]>
2022-11-10 07:59:35 +01:00
Federico BuilesandCourtney Claessens aa409fa6cd Update README.md
Co-authored-by: Courtney Claessens <[email protected]>
2022-11-10 07:59:28 +01:00
Federico BuilesandCourtney Claessens 5aaa78ce3c Update README.md
Co-authored-by: Courtney Claessens <[email protected]>
2022-11-10 07:59:15 +01:00
Federico Builes 59a4f4c4ba Fixing typo in README.md 2022-11-09 13:24:07 +01:00
Federico Builes bf8cfe8b38 Linting, adding dist files. 2022-11-09 13:22:33 +01:00
Federico Builes ae538ebe32 Linting and whitespace. Smol rename. 2022-11-09 13:17:12 +01:00
Federico Builes b4126ce983 Shuffle things around. 2022-11-09 13:16:53 +01:00
Federico Builes 418ae59d51 Replace TODO with instructions for getting PAT. 2022-11-08 17:51:31 +01:00
Federico Builes c38007a979 Don't abbreviate repo in docs.
In general let's try not to use abbreviations in public
documentation.
2022-11-08 17:45:23 +01:00
cnagadya ebe5527e72 Fix readme typo 2022-11-08 11:23:48 +00:00
cnagadya 1589654682 Add dist changes 2022-11-08 11:16:48 +00:00
cnagadya f0ff0b670a Rename config token > external-repo-token 2022-11-08 11:16:26 +00:00
cnagadya 336da03de2 Update empty allow-licenses tests 2022-11-08 11:15:36 +00:00
78565a954f Dont merge config lists
Co-authored-by: Henri Maurer<[email protected]>
Co-authored-by: Federico Builes<[email protected]>
2022-11-08 10:52:30 +00:00
cnagadya 3c73a622ba Fix config-file tests 2022-11-08 09:53:36 +00:00
cnagadya 13455c7175 Merge array config options 2022-11-07 17:57:05 +00:00
cnagadya 6d941b396a Fix inconsistencies due to zod defaults / partials mixup 2022-11-07 17:08:00 +00:00
cnagadya 49ed3f2876 Merge lists in configs instead of overwritting them 2022-11-07 12:33:54 +00:00
cnagadya b55cddb69d Use config-file for both remote and local config-files 2022-11-07 12:12:03 +00:00
cnagadyaandFederico Builes dcdeb7de77 Remove redundant skips
Co-authored-by: Federico Builes <[email protected]>
2022-11-04 16:12:05 +00:00
cnagadya b4a2fbfa16 Complete functionality for handling remote config file 2022-11-04 14:51:41 +00:00
cnagadyaandHenri Maurer 97e5a607ba Handle getContent response as is
Co-authored-by: Henri Maurer <[email protected]>
2022-11-04 10:08:00 +00:00
cnagadya 3b410dc4ad Load remote config file 2022-11-04 09:05:45 +00:00
Federico Builes 683cbc4872 Merge branch 'main' into external-repo-config 2022-11-01 08:11:26 +01:00
Federico Builes 2f696d8c7a Merge pull request #314 from actions/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-5.42.0
Bump @typescript-eslint/eslint-plugin from 5.41.0 to 5.42.0
2022-11-01 08:10:28 +01:00
dependabot[bot] 1a9033d563 Bump @typescript-eslint/eslint-plugin from 5.41.0 to 5.42.0
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.41.0 to 5.42.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.42.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-11-01 07:10:21 +00:00
Federico Builes ad6e320da1 Merge pull request #313 from actions/dependabot/npm_and_yarn/typescript-eslint/parser-5.42.0
Bump @typescript-eslint/parser from 5.41.0 to 5.42.0
2022-11-01 08:09:22 +01:00
dependabot[bot] 3d86825394 Bump @typescript-eslint/parser from 5.41.0 to 5.42.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.41.0 to 5.42.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.42.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-11-01 01:49:12 +00:00
Federico Builes 10dc05ba09 Merge pull request #311 from ericcornelissen/308-disable-license-or-vuln
Add `license-check` and `vulnerability-check` inputs
2022-10-31 07:56:37 +01:00
Federico Builes 04f48dec81 Update __tests__/config.test.ts 2022-10-31 07:55:17 +01:00
Federico Builes bb5c0c7ca0 Merge pull request #312 from actions/dependabot/npm_and_yarn/types/node-16.18.3
Bump @types/node from 16.18.2 to 16.18.3
2022-10-31 06:42:09 +01:00
dependabot[bot] 2d7d700469 Bump @types/node from 16.18.2 to 16.18.3
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.18.2 to 16.18.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-10-31 01:33:50 +00:00
Federico Builes 9760f87258 Fix config-file description in action.yml 2022-10-21 17:38:18 +02:00
Federico Builes 74c047086c Adding README and action.yml for external config files. 2022-10-21 17:34:20 +02:00
14 changed files with 626 additions and 408 deletions
+27 -7
View File
@@ -71,13 +71,20 @@ or by inlining these options in your workflow file.
### config-file
A string representing the path to an external configuration file. By
default external configuration files are not used.
A string representing the path to a configuraton file. It can be a
local file, or a file located in an external repository. You can use
this syntax for external repositories: `OWNER/REPOSITORY/FILENAME@BRANCH`.
**Possible values**: A string representing the absolute path to the
configuration file.
If the configuration file is located in an external private repository,
use the [external-repo-token](#external-repo-token) parameter of the
action to specify a token that has read access to the repository.
**Example**: `config-file: ./.github/dependency-review-config.yml`.
**Possible values**: A string representing a path to a file located
in the current repository, or in an external one.
**Example**: `config-file: ./.github/dependency-review-config.yml # local file`.
**Example**: `config-file: github/octorepo/dependency-review-config.yml@main # external repo`
### fail-on-severity
@@ -141,9 +148,9 @@ deny-licenses:
### allow-ghsas
Add a custom list of GitHub Advisory IDs that can be skipped during detection.
A list of GitHub Security Advisory IDs that can be skipped during detection.
**Possible values**: Any valid advisory GHSA ids.
**Possible values**: Any valid GHSAs from the [GitHub Advisory Database](https://github.com/advisories).
**Inline example**: `allow-ghsas: GHSA-abcd-1234-5679, GHSA-efgh-1234-5679`
@@ -185,6 +192,19 @@ base-ref: 8bb8a58d6a4028b6c2e314d5caaf273f57644896
head-ref: 69af5638bf660cf218aad5709a4c100e42a2f37b
```
### external-repo-token
A token for fetching external configuration files if they live in
an external private repository.
Visit the [developer settings](https://github.com/settings/tokens) to
create a new personal access token with `read` permissions for the
repository that hosts the config file.
**Possible values**: Any GitHub token with read access to the external repository.
**Example**: `external-repo-token: ghp_123456789abcdef...`
### Configuration File
You can use an external configuration file to specify the settings for
+80 -71
View File
@@ -1,5 +1,5 @@
import {expect, test, beforeEach} from '@jest/globals'
import {readConfig, readConfigFile} from '../src/config'
import {readConfig} from '../src/config'
import {getRefs} from '../src/git-refs'
import * as Utils from '../src/utils'
@@ -39,43 +39,53 @@ beforeEach(() => {
})
test('it defaults to low severity', async () => {
const options = readConfig()
expect(options.fail_on_severity).toEqual('low')
const config = await readConfig()
expect(config.fail_on_severity).toEqual('low')
})
test('it reads custom configs', async () => {
setInput('fail-on-severity', 'critical')
setInput('allow-licenses', ' BSD, GPL 2')
const options = readConfig()
expect(options.fail_on_severity).toEqual('critical')
expect(options.allow_licenses).toEqual(['BSD', 'GPL 2'])
const config = await readConfig()
expect(config.fail_on_severity).toEqual('critical')
expect(config.allow_licenses).toEqual(['BSD', 'GPL 2'])
})
test('it defaults to empty allow/deny lists ', async () => {
const options = readConfig()
const config = await readConfig()
expect(options.allow_licenses).toEqual(undefined)
expect(options.deny_licenses).toEqual(undefined)
expect(config.allow_licenses).toEqual(undefined)
expect(config.deny_licenses).toEqual(undefined)
})
test('it raises an error if both an allow and denylist are specified', async () => {
setInput('allow-licenses', 'MIT')
setInput('deny-licenses', 'BSD')
expect(() => readConfig()).toThrow()
await expect(readConfig()).rejects.toThrow(
'You cannot specify both allow-licenses and deny-licenses'
)
})
test('it raises an error if an empty allow list is specified', async () => {
setInput('config-file', './__tests__/fixtures/config-empty-allow-sample.yml')
await expect(readConfig()).rejects.toThrow(
'You should provide at least one license in allow-licenses'
)
})
test('it raises an error when given an unknown severity', async () => {
setInput('fail-on-severity', 'zombies')
expect(() => readConfig()).toThrow()
await expect(readConfig()).rejects.toThrow(/received 'zombies'/)
})
test('it uses the given refs when the event is not a pull request', async () => {
setInput('base-ref', 'a-custom-base-ref')
setInput('head-ref', 'a-custom-head-ref')
const refs = getRefs(readConfig(), {
const refs = getRefs(await readConfig(), {
payload: {},
eventName: 'workflow_dispatch'
})
@@ -84,9 +94,9 @@ test('it uses the given refs when the event is not a pull request', async () =>
})
test('it raises an error when no refs are provided and the event is not a pull request', async () => {
const options = readConfig()
const config = await readConfig()
expect(() =>
getRefs(options, {
getRefs(config, {
payload: {},
eventName: 'workflow_dispatch'
})
@@ -94,133 +104,132 @@ test('it raises an error when no refs are provided and the event is not a pull r
})
test('it reads an external config file', async () => {
let options = readConfigFile('./__tests__/fixtures/config-allow-sample.yml')
expect(options.fail_on_severity).toEqual('critical')
expect(options.allow_licenses).toEqual(['BSD', 'GPL 2'])
setInput('config-file', './__tests__/fixtures/config-allow-sample.yml')
const config = await readConfig()
expect(config.fail_on_severity).toEqual('critical')
expect(config.allow_licenses).toEqual(['BSD', 'GPL 2'])
})
test('raises an error when the the config file was not found', async () => {
expect(() => readConfigFile('fixtures/i-dont-exist')).toThrow()
setInput('config-file', 'fixtures/i-dont-exist')
await expect(readConfig()).rejects.toThrow(/Unable to fetch config file/)
})
test('it parses options from both sources', async () => {
setInput('config-file', './__tests__/fixtures/config-allow-sample.yml')
let options = readConfig()
expect(options.fail_on_severity).toEqual('critical')
let config = await readConfig()
expect(config.fail_on_severity).toEqual('critical')
setInput('base-ref', 'a-custom-base-ref')
options = readConfig()
expect(options.base_ref).toEqual('a-custom-base-ref')
config = await readConfig()
expect(config.base_ref).toEqual('a-custom-base-ref')
})
test('in case of conflicts, the external config is the source of truth', async () => {
test('in case of conflicts, the inline config is the source of truth', async () => {
setInput('fail-on-severity', 'low')
setInput('config-file', './__tests__/fixtures/config-allow-sample.yml') // this will set fail-on-severity to 'critical'
let options = readConfig()
expect(options.fail_on_severity).toEqual('critical')
// this should not overwite the previous value
setInput('fail-on-severity', 'low')
options = readConfig()
expect(options.fail_on_severity).toEqual('critical')
const config = await readConfig()
expect(config.fail_on_severity).toEqual('low')
})
test('it uses the default values when loading external files', async () => {
setInput('config-file', './__tests__/fixtures/no-licenses-config.yml')
let options = readConfig()
expect(options.allow_licenses).toEqual(undefined)
expect(options.deny_licenses).toEqual(undefined)
let config = await readConfig()
expect(config.allow_licenses).toEqual(undefined)
expect(config.deny_licenses).toEqual(undefined)
setInput('config-file', './__tests__/fixtures/license-config-sample.yml')
options = readConfig()
expect(options.fail_on_severity).toEqual('low')
config = await readConfig()
expect(config.fail_on_severity).toEqual('low')
})
test('it accepts an external configuration filename', async () => {
setInput('config-file', './__tests__/fixtures/no-licenses-config.yml')
const options = readConfig()
expect(options.fail_on_severity).toEqual('critical')
const config = await readConfig()
expect(config.fail_on_severity).toEqual('critical')
})
test('it raises an error when given an unknown severity in an external config file', async () => {
setInput('config-file', './__tests__/fixtures/invalid-severity-config.yml')
expect(() => readConfig()).toThrow()
await expect(readConfig()).rejects.toThrow()
})
test('it defaults to runtime scope', async () => {
const options = readConfig()
expect(options.fail_on_scopes).toEqual(['runtime'])
const config = await readConfig()
expect(config.fail_on_scopes).toEqual(['runtime'])
})
test('it parses custom scopes preference', async () => {
setInput('fail-on-scopes', 'runtime, development')
let options = readConfig()
expect(options.fail_on_scopes).toEqual(['runtime', 'development'])
let config = await readConfig()
expect(config.fail_on_scopes).toEqual(['runtime', 'development'])
clearInputs()
setInput('fail-on-scopes', 'development')
options = readConfig()
expect(options.fail_on_scopes).toEqual(['development'])
config = await readConfig()
expect(config.fail_on_scopes).toEqual(['development'])
})
test('it raises an error when given invalid scope', async () => {
setInput('fail-on-scopes', 'runtime, zombies')
expect(() => readConfig()).toThrow()
await expect(readConfig()).rejects.toThrow(/received 'zombies'/)
})
test('it defaults to an empty GHSA allowlist', async () => {
const options = readConfig()
expect(options.allow_ghsas).toEqual(undefined)
const config = await readConfig()
expect(config.allow_ghsas).toEqual([])
})
test('it successfully parses GHSA allowlist', async () => {
setInput('allow-ghsas', 'GHSA-abcd-1234-5679, GHSA-efgh-1234-5679')
const options = readConfig()
expect(options.allow_ghsas).toEqual([
const config = await readConfig()
expect(config.allow_ghsas).toEqual([
'GHSA-abcd-1234-5679',
'GHSA-efgh-1234-5679'
])
})
test('it defaults to checking licenses', async () => {
const options = readConfig()
expect(options.license_check).toBe(true)
const config = await readConfig()
expect(config.license_check).toBe(true)
})
test('it parses the license-check input', async () => {
setInput('license-check', 'false')
let options = readConfig()
expect(options.license_check).toEqual(false)
let config = await readConfig()
expect(config.license_check).toEqual(false)
clearInputs()
setInput('license-check', 'true')
options = readConfig()
expect(options.license_check).toEqual(true)
config = await readConfig()
expect(config.license_check).toEqual(true)
})
test('it defaults to checking vulnerabilities', async () => {
const options = readConfig()
expect(options.vulnerability_check).toBe(true)
const config = await readConfig()
expect(config.vulnerability_check).toBe(true)
})
test('it parses the vulnerability-check input', async () => {
setInput('vulnerability-check', 'false')
let options = readConfig()
expect(options.vulnerability_check).toEqual(false)
let config = await readConfig()
expect(config.vulnerability_check).toEqual(false)
clearInputs()
setInput('vulnerability-check', 'true')
options = readConfig()
expect(options.vulnerability_check).toEqual(true)
config = await readConfig()
expect(config.vulnerability_check).toEqual(true)
})
test('it is not impossible to disable both checks', async () => {
test('it is not possible to disable both checks', async () => {
setInput('license-check', 'false')
setInput('vulnerability-check', 'false')
expect(() => {
readConfig()
}).toThrow("Can't disable both license-check and vulnerability-check")
await expect(readConfig()).rejects.toThrow(
/Can't disable both license-check and vulnerability-check/
)
})
describe('licenses that are not valid SPDX licenses', () => {
@@ -230,15 +239,15 @@ describe('licenses that are not valid SPDX licenses', () => {
test('it raises an error for invalid licenses in allow-licenses', async () => {
setInput('allow-licenses', ' BSD, GPL 2')
expect(() => {
readConfig()
}).toThrow('Invalid license(s) in allow-licenses: BSD, GPL 2')
await expect(readConfig()).rejects.toThrow(
'Invalid license(s) in allow-licenses: BSD, GPL 2'
)
})
test('it raises an error for invalid licenses in deny-licenses', async () => {
setInput('deny-licenses', ' BSD, GPL 2')
expect(() => {
readConfig()
}).toThrow('Invalid license(s) in deny-licenses: BSD, GPL 2')
await expect(readConfig()).rejects.toThrow(
'Invalid license(s) in deny-licenses: BSD, GPL 2'
)
})
})
@@ -0,0 +1,2 @@
fail_on_severity: critical
allow_licenses: []
-11
View File
@@ -99,17 +99,6 @@ test('it adds license inside the deny list to forbidden changes', async () => {
expect(forbidden.length).toEqual(1)
})
// This is more of a "here's a behavior that might be surprising" than an actual
// thing we want in the system. Please remove this test after refactoring.
test('it adds all licenses to forbidden changes when allow is provided an empty array', async () => {
const changes: Changes = [npmChange, rubyChange]
let {forbidden} = await getInvalidLicenseChanges(changes, {
allow: [],
deny: ['BSD']
})
expect(forbidden.length).toBe(2)
})
test('it does not add license outside the allow list to forbidden changes if it is in removed changes', async () => {
const changes: Changes = [
{...npmChange, change_type: 'removed'},
+5 -1
View File
@@ -21,7 +21,7 @@ inputs:
description: The head git ref to be used for this check. Has a default value when the workflow event is `pull_request` or `pull_request_target`. Must be provided otherwise.
required: false
config-file:
description: A filepath to the configuration file for the action.
description: A path to the configuration file for the action.
required: false
allow-licenses:
description: Comma-separated list of allowed licenses (e.g. "MIT, GPL 3.0, BSD 2 Clause")
@@ -32,6 +32,10 @@ inputs:
allow-ghsas:
description: Comma-separated list of allowed Github Advisory IDs (e.g. "GHSA-abcd-1234-5679, GHSA-efgh-1234-5679")
required: false
external-repo-token:
description: A token for fetching external configuration file if it lives in another repository. It is required if the repository is private
required: false
runs:
using: 'node16'
main: 'dist/index.js'
Generated Vendored
+239 -119
View File
@@ -107,29 +107,6 @@ exports.getRefs = getRefs;
"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;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
@@ -144,9 +121,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getInvalidLicenseChanges = void 0;
const core = __importStar(__nccwpck_require__(2186));
const spdx_satisfies_1 = __importDefault(__nccwpck_require__(4424));
const octokit_1 = __nccwpck_require__(7467);
const utils_1 = __nccwpck_require__(918);
/**
* Loops through a list of changes, filtering and returning the
@@ -205,11 +180,11 @@ function getInvalidLicenseChanges(changes, licenses) {
exports.getInvalidLicenseChanges = getInvalidLicenseChanges;
const fetchGHLicense = (owner, repo) => __awaiter(void 0, void 0, void 0, function* () {
var _a, _b;
const octokit = new octokit_1.Octokit({
auth: core.getInput('repo-token', { required: true })
});
try {
const response = yield octokit.rest.licenses.getForRepo({ owner, repo });
const response = yield (0, utils_1.octokitClient)().rest.licenses.getForRepo({
owner,
repo
});
return (_b = (_a = response.data.license) === null || _a === void 0 ? void 0 : _a.spdx_id) !== null && _b !== void 0 ? _b : null;
}
catch (_) {
@@ -350,7 +325,7 @@ const utils_1 = __nccwpck_require__(918);
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
const config = (0, config_1.readConfig)();
const config = yield (0, config_1.readConfig)();
const refs = (0, git_refs_1.getRefs)(config, github.context);
const changes = yield dependencyGraph.compare({
owner: github.context.repo.owner,
@@ -557,17 +532,36 @@ exports.ConfigurationOptionsSchema = z
.object({
fail_on_severity: exports.SeveritySchema,
fail_on_scopes: z.array(z.enum(exports.SCOPES)).default(['runtime']),
allow_licenses: z.array(z.string()).default([]),
deny_licenses: z.array(z.string()).default([]),
allow_licenses: z.array(z.string()).optional(),
deny_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),
config_file: z.string().optional().default('false'),
base_ref: z.string(),
head_ref: z.string()
config_file: z.string().optional(),
base_ref: z.string().optional(),
head_ref: z.string().optional()
})
.partial()
.refine(obj => !(obj.allow_licenses && obj.deny_licenses), 'Your workflow file has both an allow_licenses list and deny_licenses list, but you can only set one or the other.');
.superRefine((config, context) => {
if (config.allow_licenses && config.deny_licenses) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'You cannot specify both allow-licenses and deny-licenses'
});
}
if (config.allow_licenses && config.allow_licenses.length < 1) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'You should provide at least one license in allow-licenses'
});
}
if (config.license_check === false &&
config.vulnerability_check === false) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: "Can't disable both license-check and vulnerability-check"
});
}
});
exports.ChangesSchema = z.array(exports.ChangeSchema);
@@ -741,11 +735,36 @@ exports.addScannedDependencies = addScannedDependencies;
"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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.isSPDXValid = exports.renderUrl = exports.getManifestsSet = exports.groupDependenciesByManifest = void 0;
exports.octokitClient = exports.isSPDXValid = exports.renderUrl = exports.getManifestsSet = exports.groupDependenciesByManifest = void 0;
const core = __importStar(__nccwpck_require__(2186));
const octokit_1 = __nccwpck_require__(7467);
const spdx_expression_parse_1 = __importDefault(__nccwpck_require__(1620));
function groupDependenciesByManifest(changes) {
var _a;
@@ -783,6 +802,17 @@ function isSPDXValid(license) {
}
}
exports.isSPDXValid = isSPDXValid;
function octokitClient(token = 'repo-token', required = true) {
const opts = {};
// auth is only added if token is present. For remote config files in public
// repos the token is optional, so it could be undefined.
const auth = core.getInput(token, { required });
if (auth !== undefined) {
opts['auth'] = auth;
}
return new octokit_1.Octokit(opts);
}
exports.octokitClient = octokitClient;
/***/ }),
@@ -27397,11 +27427,20 @@ var __importStar = (this && this.__importStar) || function (mod) {
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.readConfigFile = exports.readInlineConfig = exports.readConfig = void 0;
exports.readConfig = void 0;
const fs = __importStar(__nccwpck_require__(7147));
const path_1 = __importDefault(__nccwpck_require__(1017));
const yaml_1 = __importDefault(__nccwpck_require__(4083));
@@ -27409,6 +27448,43 @@ const core = __importStar(__nccwpck_require__(2186));
const z = __importStar(__nccwpck_require__(3301));
const schemas_1 = __nccwpck_require__(1129);
const utils_1 = __nccwpck_require__(1314);
function readConfig() {
return __awaiter(this, void 0, void 0, function* () {
const inlineConfig = readInlineConfig();
const configFile = getOptionalInput('config-file');
if (configFile !== undefined) {
const externalConfig = yield readConfigFile(configFile);
return schemas_1.ConfigurationOptionsSchema.parse(Object.assign(Object.assign({}, externalConfig), inlineConfig));
}
return schemas_1.ConfigurationOptionsSchema.parse(inlineConfig);
});
}
exports.readConfig = readConfig;
function readInlineConfig() {
const fail_on_severity = getOptionalInput('fail-on-severity');
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_ghsas = parseList(getOptionalInput('allow-ghsas'));
const license_check = getOptionalBoolean('license-check');
const vulnerability_check = getOptionalBoolean('vulnerability-check');
const base_ref = getOptionalInput('base-ref');
const head_ref = getOptionalInput('head-ref');
validateLicenses('allow-licenses', allow_licenses);
validateLicenses('deny-licenses', deny_licenses);
const keys = {
fail_on_severity,
fail_on_scopes,
allow_licenses,
deny_licenses,
allow_ghsas,
license_check,
vulnerability_check,
base_ref,
head_ref
};
return Object.fromEntries(Object.entries(keys).filter(([_, value]) => value !== undefined));
}
function getOptionalBoolean(name) {
const value = core.getInput(name);
return value.length > 0 ? core.getBooleanInput(name) : undefined;
@@ -27434,85 +27510,74 @@ function validateLicenses(key, licenses) {
throw new Error(`Invalid license(s) in ${key}: ${invalid_licenses.join(', ')}`);
}
}
function readConfig() {
const externalConfig = getOptionalInput('config-file');
if (externalConfig !== undefined) {
const config = readConfigFile(externalConfig);
// the reasoning behind reading the inline config when an external
// config file is provided is that we still want to allow users to
// pass inline options in the presence of an external config file.
const inlineConfig = readInlineConfig();
// the external config takes precedence
return Object.assign({}, inlineConfig, config);
}
else {
return readInlineConfig();
}
}
exports.readConfig = readConfig;
function readInlineConfig() {
const fail_on_severity = schemas_1.SeveritySchema.parse(getOptionalInput('fail-on-severity'));
const fail_on_scopes = z
.array(z.enum(schemas_1.SCOPES))
.default(['runtime'])
.parse(parseList(getOptionalInput('fail-on-scopes')));
const allow_licenses = parseList(getOptionalInput('allow-licenses'));
const deny_licenses = parseList(getOptionalInput('deny-licenses'));
if (allow_licenses !== undefined && deny_licenses !== undefined) {
throw new Error("Can't specify both allow_licenses and deny_licenses");
}
validateLicenses('allow-licenses', allow_licenses);
validateLicenses('deny-licenses', deny_licenses);
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'));
const license_check = z
.boolean()
.default(true)
.parse(getOptionalBoolean('license-check'));
const vulnerability_check = z
.boolean()
.default(true)
.parse(getOptionalBoolean('vulnerability-check'));
if (license_check === false && vulnerability_check === false) {
throw new Error("Can't disable both license-check and vulnerability-check");
}
const base_ref = getOptionalInput('base-ref');
const head_ref = getOptionalInput('head-ref');
return {
fail_on_severity,
fail_on_scopes,
allow_licenses,
deny_licenses,
allow_ghsas,
license_check,
vulnerability_check,
base_ref,
head_ref
};
}
exports.readInlineConfig = readInlineConfig;
function readConfigFile(filePath) {
let data;
return __awaiter(this, void 0, void 0, function* () {
// match a remote config (e.g. 'owner/repo/filepath@someref')
const format = new RegExp('(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)');
let data;
const pieces = format.exec(filePath);
try {
if ((pieces === null || pieces === void 0 ? void 0 : pieces.groups) && pieces.length === 5) {
data = yield getRemoteConfig({
owner: pieces.groups.owner,
repo: pieces.groups.repo,
path: pieces.groups.path,
ref: pieces.groups.ref
});
}
else {
data = fs.readFileSync(path_1.default.resolve(filePath), 'utf-8');
}
return parseConfigFile(data);
}
catch (error) {
core.debug(error);
throw new Error('Unable to fetch config file');
}
});
}
function parseConfigFile(configData) {
try {
data = fs.readFileSync(path_1.default.resolve(filePath), 'utf-8');
const data = yaml_1.default.parse(configData);
for (const key of Object.keys(data)) {
if (key === 'allow-licenses' || key === 'deny-licenses') {
validateLicenses(key, data[key]);
}
// get rid of the ugly dashes from the actions conventions
if (key.includes('-')) {
data[key.replace(/-/g, '_')] = data[key];
delete data[key];
}
}
return data;
}
catch (error) {
throw error;
}
data = yaml_1.default.parse(data);
for (const key of Object.keys(data)) {
if (key === 'allow-licenses' || key === 'deny-licenses') {
validateLicenses(key, data[key]);
}
// get rid of the ugly dashes from the actions conventions
if (key.includes('-')) {
data[key.replace(/-/g, '_')] = data[key];
delete data[key];
}
}
const values = schemas_1.ConfigurationOptionsSchema.parse(data);
return values;
}
exports.readConfigFile = readConfigFile;
function getRemoteConfig(configOpts) {
return __awaiter(this, void 0, void 0, function* () {
try {
const { data } = yield (0, utils_1.octokitClient)('external-repo-token', false).rest.repos.getContent({
mediaType: {
format: 'raw'
},
owner: configOpts.owner,
repo: configOpts.repo,
path: configOpts.path,
ref: configOpts.ref
});
// When using mediaType.format = 'raw', the response.data is a string
// but this is not reflected in the return type of getContent, so we're
// casting the return value to a string.
return z.string().parse(data);
}
catch (error) {
core.debug(error);
throw new Error('Error fetching remote config file');
}
});
}
/***/ }),
@@ -27658,17 +27723,36 @@ exports.ConfigurationOptionsSchema = z
.object({
fail_on_severity: exports.SeveritySchema,
fail_on_scopes: z.array(z.enum(exports.SCOPES)).default(['runtime']),
allow_licenses: z.array(z.string()).default([]),
deny_licenses: z.array(z.string()).default([]),
allow_licenses: z.array(z.string()).optional(),
deny_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),
config_file: z.string().optional().default('false'),
base_ref: z.string(),
head_ref: z.string()
config_file: z.string().optional(),
base_ref: z.string().optional(),
head_ref: z.string().optional()
})
.partial()
.refine(obj => !(obj.allow_licenses && obj.deny_licenses), 'Your workflow file has both an allow_licenses list and deny_licenses list, but you can only set one or the other.');
.superRefine((config, context) => {
if (config.allow_licenses && config.deny_licenses) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'You cannot specify both allow-licenses and deny-licenses'
});
}
if (config.allow_licenses && config.allow_licenses.length < 1) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'You should provide at least one license in allow-licenses'
});
}
if (config.license_check === false &&
config.vulnerability_check === false) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: "Can't disable both license-check and vulnerability-check"
});
}
});
exports.ChangesSchema = z.array(exports.ChangeSchema);
@@ -27679,11 +27763,36 @@ exports.ChangesSchema = z.array(exports.ChangeSchema);
"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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.isSPDXValid = exports.renderUrl = exports.getManifestsSet = exports.groupDependenciesByManifest = void 0;
exports.octokitClient = exports.isSPDXValid = exports.renderUrl = exports.getManifestsSet = exports.groupDependenciesByManifest = void 0;
const core = __importStar(__nccwpck_require__(2186));
const octokit_1 = __nccwpck_require__(7467);
const spdx_expression_parse_1 = __importDefault(__nccwpck_require__(1620));
function groupDependenciesByManifest(changes) {
var _a;
@@ -27721,6 +27830,17 @@ function isSPDXValid(license) {
}
}
exports.isSPDXValid = isSPDXValid;
function octokitClient(token = 'repo-token', required = true) {
const opts = {};
// auth is only added if token is present. For remote config files in public
// repos the token is optional, so it could be undefined.
const auth = core.getInput(token, { required });
if (auth !== undefined) {
opts['auth'] = auth;
}
return new octokit_1.Octokit(opts);
}
exports.octokitClient = octokitClient;
/***/ }),
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+103 -89
View File
@@ -24,11 +24,11 @@
},
"devDependencies": {
"@types/jest": "^27.5.2",
"@types/node": "^16.18.3",
"@types/spdx-expression-parse": "^3.0.2",
"@types/spdx-satisfies": "^0.1.0",
"@types/node": "^16.18.2",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/parser": "^5.41.0",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0",
"@vercel/ncc": "^0.34.0",
"esbuild-register": "^3.3.3",
"eslint": "^8.26.0",
@@ -1802,9 +1802,9 @@
"integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
},
"node_modules/@types/node": {
"version": "16.18.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.2.tgz",
"integrity": "sha512-KIGQJyya+opDCFvDSZMNNS899ov5jlNdtN7PypgHWeb8e+5vWISdwTRo/ClsNVlmDihzOGqFyNBDamUs7TQQCA=="
"version": "16.18.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz",
"integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg=="
},
"node_modules/@types/prettier": {
"version": "2.7.1",
@@ -1852,16 +1852,17 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz",
"integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.0.tgz",
"integrity": "sha512-5TJh2AgL6+wpL8H/GTSjNb4WrjKoR2rqvFxR/DDTqYNk6uXn8BJMEcncLSpMbf/XV1aS0jAjYwn98uvVCiAywQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.41.0",
"@typescript-eslint/type-utils": "5.41.0",
"@typescript-eslint/utils": "5.41.0",
"@typescript-eslint/scope-manager": "5.42.0",
"@typescript-eslint/type-utils": "5.42.0",
"@typescript-eslint/utils": "5.42.0",
"debug": "^4.3.4",
"ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0",
"regexpp": "^3.2.0",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
@@ -1884,14 +1885,14 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz",
"integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.42.0.tgz",
"integrity": "sha512-Ixh9qrOTDRctFg3yIwrLkgf33AHyEIn6lhyf5cCfwwiGtkWhNpVKlEZApi3inGQR/barWnY7qY8FbGKBO7p3JA==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.41.0",
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/typescript-estree": "5.41.0",
"@typescript-eslint/scope-manager": "5.42.0",
"@typescript-eslint/types": "5.42.0",
"@typescript-eslint/typescript-estree": "5.42.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1911,13 +1912,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz",
"integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.42.0.tgz",
"integrity": "sha512-l5/3IBHLH0Bv04y+H+zlcLiEMEMjWGaCX6WyHE5Uk2YkSGAMlgdUPsT/ywTSKgu9D1dmmKMYgYZijObfA39Wow==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/visitor-keys": "5.41.0"
"@typescript-eslint/types": "5.42.0",
"@typescript-eslint/visitor-keys": "5.42.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1928,13 +1929,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz",
"integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.42.0.tgz",
"integrity": "sha512-HW14TXC45dFVZxnVW8rnUGnvYyRC0E/vxXShFCthcC9VhVTmjqOmtqj6H5rm9Zxv+ORxKA/1aLGD7vmlLsdlOg==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "5.41.0",
"@typescript-eslint/utils": "5.41.0",
"@typescript-eslint/typescript-estree": "5.42.0",
"@typescript-eslint/utils": "5.42.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
},
@@ -1955,9 +1956,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz",
"integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.42.0.tgz",
"integrity": "sha512-t4lzO9ZOAUcHY6bXQYRuu+3SSYdD9TS8ooApZft4WARt4/f2Cj/YpvbTe8A4GuhT4bNW72goDMOy7SW71mZwGw==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1968,13 +1969,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz",
"integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.42.0.tgz",
"integrity": "sha512-2O3vSq794x3kZGtV7i4SCWZWCwjEtkWfVqX4m5fbUBomOsEOyd6OAD1qU2lbvV5S8tgy/luJnOYluNyYVeOTTg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/visitor-keys": "5.41.0",
"@typescript-eslint/types": "5.42.0",
"@typescript-eslint/visitor-keys": "5.42.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -1995,16 +1996,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz",
"integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.42.0.tgz",
"integrity": "sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.41.0",
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/typescript-estree": "5.41.0",
"@typescript-eslint/scope-manager": "5.42.0",
"@typescript-eslint/types": "5.42.0",
"@typescript-eslint/typescript-estree": "5.42.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
@@ -2021,12 +2022,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz",
"integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.42.0.tgz",
"integrity": "sha512-QHbu5Hf/2lOEOwy+IUw0GoSCuAzByTAWWrOTKzTzsotiUnWFpuKnXcAhC9YztAf2EElQ0VvIK+pHJUPkM0q7jg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/types": "5.42.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@@ -6277,6 +6278,12 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
"node_modules/natural-compare-lite": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
"dev": true
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -9592,9 +9599,9 @@
"integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
},
"@types/node": {
"version": "16.18.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.2.tgz",
"integrity": "sha512-KIGQJyya+opDCFvDSZMNNS899ov5jlNdtN7PypgHWeb8e+5vWISdwTRo/ClsNVlmDihzOGqFyNBDamUs7TQQCA=="
"version": "16.18.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz",
"integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg=="
},
"@types/prettier": {
"version": "2.7.1",
@@ -9642,69 +9649,70 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz",
"integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.0.tgz",
"integrity": "sha512-5TJh2AgL6+wpL8H/GTSjNb4WrjKoR2rqvFxR/DDTqYNk6uXn8BJMEcncLSpMbf/XV1aS0jAjYwn98uvVCiAywQ==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.41.0",
"@typescript-eslint/type-utils": "5.41.0",
"@typescript-eslint/utils": "5.41.0",
"@typescript-eslint/scope-manager": "5.42.0",
"@typescript-eslint/type-utils": "5.42.0",
"@typescript-eslint/utils": "5.42.0",
"debug": "^4.3.4",
"ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0",
"regexpp": "^3.2.0",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/parser": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz",
"integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.42.0.tgz",
"integrity": "sha512-Ixh9qrOTDRctFg3yIwrLkgf33AHyEIn6lhyf5cCfwwiGtkWhNpVKlEZApi3inGQR/barWnY7qY8FbGKBO7p3JA==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.41.0",
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/typescript-estree": "5.41.0",
"@typescript-eslint/scope-manager": "5.42.0",
"@typescript-eslint/types": "5.42.0",
"@typescript-eslint/typescript-estree": "5.42.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz",
"integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.42.0.tgz",
"integrity": "sha512-l5/3IBHLH0Bv04y+H+zlcLiEMEMjWGaCX6WyHE5Uk2YkSGAMlgdUPsT/ywTSKgu9D1dmmKMYgYZijObfA39Wow==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/visitor-keys": "5.41.0"
"@typescript-eslint/types": "5.42.0",
"@typescript-eslint/visitor-keys": "5.42.0"
}
},
"@typescript-eslint/type-utils": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz",
"integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.42.0.tgz",
"integrity": "sha512-HW14TXC45dFVZxnVW8rnUGnvYyRC0E/vxXShFCthcC9VhVTmjqOmtqj6H5rm9Zxv+ORxKA/1aLGD7vmlLsdlOg==",
"dev": true,
"requires": {
"@typescript-eslint/typescript-estree": "5.41.0",
"@typescript-eslint/utils": "5.41.0",
"@typescript-eslint/typescript-estree": "5.42.0",
"@typescript-eslint/utils": "5.42.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/types": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz",
"integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.42.0.tgz",
"integrity": "sha512-t4lzO9ZOAUcHY6bXQYRuu+3SSYdD9TS8ooApZft4WARt4/f2Cj/YpvbTe8A4GuhT4bNW72goDMOy7SW71mZwGw==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz",
"integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.42.0.tgz",
"integrity": "sha512-2O3vSq794x3kZGtV7i4SCWZWCwjEtkWfVqX4m5fbUBomOsEOyd6OAD1qU2lbvV5S8tgy/luJnOYluNyYVeOTTg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/visitor-keys": "5.41.0",
"@typescript-eslint/types": "5.42.0",
"@typescript-eslint/visitor-keys": "5.42.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -9713,28 +9721,28 @@
}
},
"@typescript-eslint/utils": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz",
"integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.42.0.tgz",
"integrity": "sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.41.0",
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/typescript-estree": "5.41.0",
"@typescript-eslint/scope-manager": "5.42.0",
"@typescript-eslint/types": "5.42.0",
"@typescript-eslint/typescript-estree": "5.42.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz",
"integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.42.0.tgz",
"integrity": "sha512-QHbu5Hf/2lOEOwy+IUw0GoSCuAzByTAWWrOTKzTzsotiUnWFpuKnXcAhC9YztAf2EElQ0VvIK+pHJUPkM0q7jg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.41.0",
"@typescript-eslint/types": "5.42.0",
"eslint-visitor-keys": "^3.3.0"
}
},
@@ -12841,6 +12849,12 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
"natural-compare-lite": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
"dev": true
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+3 -3
View File
@@ -40,9 +40,9 @@
},
"devDependencies": {
"@types/jest": "^27.5.2",
"@types/node": "^16.18.2",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/parser": "^5.41.0",
"@types/node": "^16.18.3",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0",
"@types/spdx-expression-parse": "^3.0.2",
"@types/spdx-satisfies": "^0.1.0",
"@vercel/ncc": "^0.34.0",
+116 -86
View File
@@ -3,15 +3,57 @@ import path from 'path'
import YAML from 'yaml'
import * as core from '@actions/core'
import * as z from 'zod'
import {
ConfigurationOptions,
ConfigurationOptionsSchema,
SeveritySchema,
SCOPES
} from './schemas'
import {isSPDXValid} from './utils'
import {ConfigurationOptions, ConfigurationOptionsSchema} from './schemas'
import {isSPDXValid, octokitClient} from './utils'
type licenseKey = 'allow-licenses' | 'deny-licenses'
type ConfigurationOptionsPartial = Partial<ConfigurationOptions>
export async function readConfig(): Promise<ConfigurationOptions> {
const inlineConfig = readInlineConfig()
const configFile = getOptionalInput('config-file')
if (configFile !== undefined) {
const externalConfig = await readConfigFile(configFile)
return ConfigurationOptionsSchema.parse({
...externalConfig,
...inlineConfig
})
}
return ConfigurationOptionsSchema.parse(inlineConfig)
}
function readInlineConfig(): ConfigurationOptionsPartial {
const fail_on_severity = getOptionalInput('fail-on-severity')
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_ghsas = parseList(getOptionalInput('allow-ghsas'))
const license_check = getOptionalBoolean('license-check')
const vulnerability_check = getOptionalBoolean('vulnerability-check')
const base_ref = getOptionalInput('base-ref')
const head_ref = getOptionalInput('head-ref')
validateLicenses('allow-licenses', allow_licenses)
validateLicenses('deny-licenses', deny_licenses)
const keys = {
fail_on_severity,
fail_on_scopes,
allow_licenses,
deny_licenses,
allow_ghsas,
license_check,
vulnerability_check,
base_ref,
head_ref
}
return Object.fromEntries(
Object.entries(keys).filter(([_, value]) => value !== undefined)
)
}
function getOptionalBoolean(name: string): boolean | undefined {
const value = core.getInput(name)
@@ -32,7 +74,7 @@ function parseList(list: string | undefined): string[] | undefined {
}
function validateLicenses(
key: licenseKey,
key: 'allow-licenses' | 'deny-licenses',
licenses: string[] | undefined
): void {
if (licenses === undefined) {
@@ -47,89 +89,77 @@ function validateLicenses(
}
}
export function readConfig(): ConfigurationOptions {
const externalConfig = getOptionalInput('config-file')
if (externalConfig !== undefined) {
const config = readConfigFile(externalConfig)
// the reasoning behind reading the inline config when an external
// config file is provided is that we still want to allow users to
// pass inline options in the presence of an external config file.
const inlineConfig = readInlineConfig()
// the external config takes precedence
return Object.assign({}, inlineConfig, config)
} else {
return readInlineConfig()
}
}
export function readInlineConfig(): ConfigurationOptions {
const fail_on_severity = SeveritySchema.parse(
getOptionalInput('fail-on-severity')
async function readConfigFile(
filePath: string
): Promise<ConfigurationOptionsPartial> {
// match a remote config (e.g. 'owner/repo/filepath@someref')
const format = new RegExp(
'(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)'
)
const fail_on_scopes = z
.array(z.enum(SCOPES))
.default(['runtime'])
.parse(parseList(getOptionalInput('fail-on-scopes')))
const allow_licenses = parseList(getOptionalInput('allow-licenses'))
const deny_licenses = parseList(getOptionalInput('deny-licenses'))
if (allow_licenses !== undefined && deny_licenses !== undefined) {
throw new Error("Can't specify both allow_licenses and deny_licenses")
}
validateLicenses('allow-licenses', allow_licenses)
validateLicenses('deny-licenses', deny_licenses)
const allow_ghsas = parseList(getOptionalInput('allow-ghsas'))
const license_check = z
.boolean()
.default(true)
.parse(getOptionalBoolean('license-check'))
const vulnerability_check = z
.boolean()
.default(true)
.parse(getOptionalBoolean('vulnerability-check'))
if (license_check === false && vulnerability_check === false) {
throw new Error("Can't disable both license-check and vulnerability-check")
}
const base_ref = getOptionalInput('base-ref')
const head_ref = getOptionalInput('head-ref')
return {
fail_on_severity,
fail_on_scopes,
allow_licenses,
deny_licenses,
allow_ghsas,
license_check,
vulnerability_check,
base_ref,
head_ref
}
}
export function readConfigFile(filePath: string): ConfigurationOptions {
let data
let data: string
const pieces = format.exec(filePath)
try {
data = fs.readFileSync(path.resolve(filePath), 'utf-8')
} catch (error: unknown) {
if (pieces?.groups && pieces.length === 5) {
data = await getRemoteConfig({
owner: pieces.groups.owner,
repo: pieces.groups.repo,
path: pieces.groups.path,
ref: pieces.groups.ref
})
} else {
data = fs.readFileSync(path.resolve(filePath), 'utf-8')
}
return parseConfigFile(data)
} catch (error) {
core.debug(error as string)
throw new Error('Unable to fetch config file')
}
}
function parseConfigFile(configData: string): ConfigurationOptionsPartial {
try {
const data = YAML.parse(configData)
for (const key of Object.keys(data)) {
if (key === 'allow-licenses' || key === 'deny-licenses') {
validateLicenses(key, data[key])
}
// get rid of the ugly dashes from the actions conventions
if (key.includes('-')) {
data[key.replace(/-/g, '_')] = data[key]
delete data[key]
}
}
return data
} catch (error) {
throw error
}
data = YAML.parse(data)
}
for (const key of Object.keys(data)) {
if (key === 'allow-licenses' || key === 'deny-licenses') {
validateLicenses(key, data[key])
}
// get rid of the ugly dashes from the actions conventions
if (key.includes('-')) {
data[key.replace(/-/g, '_')] = data[key]
delete data[key]
}
async function getRemoteConfig(configOpts: {
[key: string]: string
}): Promise<string> {
try {
const {data} = await octokitClient(
'external-repo-token',
false
).rest.repos.getContent({
mediaType: {
format: 'raw'
},
owner: configOpts.owner,
repo: configOpts.repo,
path: configOpts.path,
ref: configOpts.ref
})
// When using mediaType.format = 'raw', the response.data is a string
// but this is not reflected in the return type of getContent, so we're
// casting the return value to a string.
return z.string().parse(data as unknown)
} catch (error) {
core.debug(error as string)
throw new Error('Error fetching remote config file')
}
const values = ConfigurationOptionsSchema.parse(data)
return values
}
+5 -8
View File
@@ -1,8 +1,6 @@
import * as core from '@actions/core'
import spdxSatisfies from 'spdx-satisfies'
import {Octokit} from 'octokit'
import {Change, Changes} from './schemas'
import {isSPDXValid} from './utils'
import {isSPDXValid, octokitClient} from './utils'
/**
* Loops through a list of changes, filtering and returning the
@@ -76,12 +74,11 @@ const fetchGHLicense = async (
owner: string,
repo: string
): Promise<string | null> => {
const octokit = new Octokit({
auth: core.getInput('repo-token', {required: true})
})
try {
const response = await octokit.rest.licenses.getForRepo({owner, repo})
const response = await octokitClient().rest.licenses.getForRepo({
owner,
repo
})
return response.data.license?.spdx_id ?? null
} catch (_) {
return null
+2 -2
View File
@@ -18,7 +18,7 @@ import {groupDependenciesByManifest} from './utils'
async function run(): Promise<void> {
try {
const config = readConfig()
const config = await readConfig()
const refs = getRefs(config, github.context)
const changes = await dependencyGraph.compare({
@@ -28,7 +28,7 @@ async function run(): Promise<void> {
headRef: refs.head
})
const minSeverity = config.fail_on_severity as Severity
const minSeverity = config.fail_on_severity
const scopedChanges = filterChangesByScopes(config.fail_on_scopes, changes)
const filteredChanges = filterAllowedAdvisories(
config.allow_ghsas,
+28 -10
View File
@@ -38,20 +38,38 @@ export const ConfigurationOptionsSchema = z
.object({
fail_on_severity: SeveritySchema,
fail_on_scopes: z.array(z.enum(SCOPES)).default(['runtime']),
allow_licenses: z.array(z.string()).default([]),
deny_licenses: z.array(z.string()).default([]),
allow_licenses: z.array(z.string()).optional(),
deny_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),
config_file: z.string().optional().default('false'),
base_ref: z.string(),
head_ref: z.string()
config_file: z.string().optional(),
base_ref: z.string().optional(),
head_ref: z.string().optional()
})
.superRefine((config, context) => {
if (config.allow_licenses && config.deny_licenses) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'You cannot specify both allow-licenses and deny-licenses'
})
}
if (config.allow_licenses && config.allow_licenses.length < 1) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'You should provide at least one license in allow-licenses'
})
}
if (
config.license_check === false &&
config.vulnerability_check === false
) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: "Can't disable both license-check and vulnerability-check"
})
}
})
.partial()
.refine(
obj => !(obj.allow_licenses && obj.deny_licenses),
'Your workflow file has both an allow_licenses list and deny_licenses list, but you can only set one or the other.'
)
export const ChangesSchema = z.array(ChangeSchema)
+15
View File
@@ -1,3 +1,5 @@
import * as core from '@actions/core'
import {Octokit} from 'octokit'
import spdxParse from 'spdx-expression-parse'
import {Changes} from './schemas'
@@ -38,3 +40,16 @@ export function isSPDXValid(license: string): boolean {
return false
}
}
export function octokitClient(token = 'repo-token', required = true): Octokit {
const opts: Record<string, unknown> = {}
// auth is only added if token is present. For remote config files in public
// repos the token is optional, so it could be undefined.
const auth = core.getInput(token, {required})
if (auth !== undefined) {
opts['auth'] = auth
}
return new Octokit(opts)
}