Compare commits

..
Author SHA1 Message Date
Federico Builes 605a14dcda adding dist 2023-02-16 14:41:52 +01:00
David LosertandGitHub f69167c9be Build files for current version 2023-02-16 10:04:56 +00:00
David LosertandGitHub 1c85e9db8d Adds option to write summary into a pr comment 2023-02-16 10:03:16 +00:00
9 changed files with 264 additions and 25 deletions
+18 -18
View File
@@ -66,25 +66,25 @@ jobs:
Configure this action by either inlining these options in your workflow file, or by using an external configuration file. All configuration options are optional.
| Option | Usage | Possible values | Default value |
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|---------------|
| `fail-on-severity` | Defines the threshold for the level of severity. The action will fail on any pull requests that introduce vulnerabilities of the specified severity level or higher. | `low`, `moderate`, `high`, `critical` | `low` |
| `allow-licenses`* | Contains a list of allowed licenses. The action will fail on pull requests that introduce dependencies with licenses that do not match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
| `deny-licenses`* | Contains a list of prohibited licenses. The action will fail on pull requests that introduce dependencies with licenses that match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
| `fail-on-scopes`† | Contains a list of strings of the build environments you want to support. The action will fail on pull requests that introduce vulnerabilities in the scopes that match the list. |`runtime`, `development`, `unknown` | `runtime` |
| `allow-ghsas` | Contains a list of GitHub Advisory Database IDs that can be skipped during detection. | Any GHSAs from the [GitHub Advisory Database](https://github.com/advisories) | none |
| `license-check` | Enable or disable the license check performed by the action. | `true`, `false` | `true` |
| `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` |
| `base-ref`/`head-ref` | Provide custom git references for the git base/head when performing the comparison check. This is only used for event types other than `pull_request` and `pull_request_target`. | Any valid git ref(s) in your project | none |
| Option | Usage | Possible values | Default value |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------- |
| `fail-on-severity` | Defines the threshold for the level of severity. The action will fail on any pull requests that introduce vulnerabilities of the specified severity level or higher. | `low`, `moderate`, `high`, `critical` | `low` |
| `allow-licenses`* | Contains a list of allowed licenses. The action will fail on pull requests that introduce dependencies with licenses that do not match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
| `deny-licenses`* | Contains a list of prohibited licenses. The action will fail on pull requests that introduce dependencies with licenses that match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
| `fail-on-scopes` | Contains a list of strings of the build environments you want to support. The action will fail on pull requests that introduce vulnerabilities in the scopes that match the list. | `runtime`, `development`, `unknown` | `runtime` |
| `allow-ghsas` | Contains a list of GitHub Advisory Database IDs that can be skipped during detection. | Any GHSAs from the [GitHub Advisory Database](https://github.com/advisories) | none |
| `license-check` | Enable or disable the license check performed by the action. | `true`, `false` | `true` |
| `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` |
| `base-ref`/`head-ref` | Provide custom git references for the git base/head when performing the comparison check. This is only used for event types other than `pull_request` and `pull_request_target`. | Any valid git ref(s) in your project | none |
| `comment-summary-in-pr` | Enable or disable reporting the review summary as a comment in the pull request. If enabled, you must give the workflow or job permission `pull-requests: write`. | `true`, `false` | `false` |
*not supported for use with GitHub Enterprise Server
†will be supported with GitHub Enterprise Server 3.8
†will be supported with GitHub Enterprise Server 3.8
### Inline Configuration
You can pass options to the Dependency Review GitHub Action using your workflow file.
You can pass options to the Dependency Review GitHub Action using your workflow file.
#### Example
@@ -112,10 +112,10 @@ jobs:
You can use an external configuration file to specify the settings for this action. It can be a local file or a file in an external repository. Refer to the following options for the specification.
| Option | Usage | Possible values |
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
| `config-file` | A path to a file in the current repository or an external repository. Use this syntax for external files: `OWNER/REPOSITORY/FILENAME@BRANCH` | **Local file**: `./.github/dependency-review-config.yml` <br> **External repo**: `github/octorepo/dependency-review-config.yml@main` |
| `external-repo-token` | Specifies a token for fetching the configuration file. It is required if the file resides in a private external repository and for all GitHub Enterprise Server repositories. Create a token in [developer settings](https://github.com/settings/tokens). | Any token with `read` permissions to the repository hosting the config file. |
| Option | Usage | Possible values |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `config-file` | A path to a file in the current repository or an external repository. Use this syntax for external files: `OWNER/REPOSITORY/FILENAME@BRANCH` | **Local file**: `./.github/dependency-review-config.yml` <br> **External repo**: `github/octorepo/dependency-review-config.yml@main` |
| `external-repo-token` | Specifies a token for fetching the configuration file. It is required if the file resides in a private external repository and for all GitHub Enterprise Server repositories. Create a token in [developer settings](https://github.com/settings/tokens). | Any token with `read` permissions to the repository hosting the config file. |
#### Example
@@ -128,7 +128,7 @@ Start by specifying that you will be using an external configuration file:
config-file: './.github/dependency-review-config.yml'
```
And then create the file in the path you just specified:
And then create the file in the path you just specified:
```yaml
fail-on-severity: 'critical'
+2 -1
View File
@@ -22,7 +22,8 @@ function clearInputs() {
'VULNERABILITY-CHECK',
'CONFIG-FILE',
'BASE-REF',
'HEAD-REF'
'HEAD-REF',
'COMMENT-SUMMARY-IN-PR'
]
allowedOptions.forEach(option => {
+3
View File
@@ -41,6 +41,9 @@ inputs:
vulnerability-check:
description: A boolean to determine if vulnerability checks should be performed
required: false
comment-summary-in-pr:
description: A boolean to determine if the report should be posted as a comment in the PR itself. Setting this to true requires you to give the workflow the write permissions for pull-requests
required: false
runs:
using: 'node16'
main: 'dist/index.js'
Generated Vendored
+147 -3
View File
@@ -1,6 +1,142 @@
require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 5842:
/***/ (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;
};
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 __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.commentPr = void 0;
const github = __importStar(__nccwpck_require__(5438));
const core = __importStar(__nccwpck_require__(2186));
const githubUtils = __importStar(__nccwpck_require__(3030));
const retry = __importStar(__nccwpck_require__(6298));
const request_error_1 = __nccwpck_require__(537);
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry);
const octo = new retryingOctokit(githubUtils.getOctokitOptions(core.getInput('repo-token', { required: true })));
// Comment Marker to identify an existing comment to update, so we don't spam the PR with comments
const COMMENT_MARKER = '<!-- dependency-review-pr-comment-marker -->';
function commentPr(summary) {
return __awaiter(this, void 0, void 0, function* () {
if (!github.context.payload.pull_request) {
core.warning('Not in the context of a pull request. Skipping comment creation.');
return;
}
const commentBody = `${summary.stringify()}\n\n${COMMENT_MARKER}`;
try {
const existingCommentId = yield findCommentByMarker(COMMENT_MARKER);
if (existingCommentId) {
yield octo.rest.issues.updateComment({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
comment_id: existingCommentId,
body: commentBody
});
}
else {
yield octo.rest.issues.createComment({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: github.context.payload.pull_request.number,
body: commentBody
});
}
}
catch (error) {
if (error instanceof request_error_1.RequestError && error.status === 403) {
core.warning(`Unable to write summary to pull-request. Make sure you are giving this workflow the permission 'pull-requests: write'.`);
}
else {
if (error instanceof Error) {
core.warning(`Unable to comment summary to pull-request, received error: ${error.message}`);
}
else {
core.warning('Unable to comment summary to pull-request: Unexpected fatal error');
}
}
}
});
}
exports.commentPr = commentPr;
function findCommentByMarker(commentBodyIncludes) {
var _a, e_1, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
const commentsIterator = octo.paginate.iterator(octo.rest.issues.listComments, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
// We are already checking if we are in the context of a pull request in the caller
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
issue_number: github.context.payload.pull_request.number
});
try {
for (var _d = true, commentsIterator_1 = __asyncValues(commentsIterator), commentsIterator_1_1; commentsIterator_1_1 = yield commentsIterator_1.next(), _a = commentsIterator_1_1.done, !_a;) {
_c = commentsIterator_1_1.value;
_d = false;
try {
const { data: comments } = _c;
const existingComment = comments.find(comment => { var _a; return (_a = comment.body) === null || _a === void 0 ? void 0 : _a.includes(commentBodyIncludes); });
if (existingComment)
return existingComment.id;
}
finally {
_d = true;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = commentsIterator_1.return)) yield _b.call(commentsIterator_1);
}
finally { if (e_1) throw e_1.error; }
}
return undefined;
});
}
/***/ }),
/***/ 4966:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
@@ -322,6 +458,7 @@ const licenses_1 = __nccwpck_require__(3247);
const summary = __importStar(__nccwpck_require__(8608));
const git_refs_1 = __nccwpck_require__(1086);
const utils_1 = __nccwpck_require__(918);
const comment_pr_1 = __nccwpck_require__(5842);
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
@@ -354,6 +491,9 @@ function run() {
}
summary.addScannedDependencies(changes);
printScannedDependencies(changes);
if (config.comment_summary_in_pr) {
yield (0, comment_pr_1.commentPr)(core.summary);
}
}
catch (error) {
if (error instanceof request_error_1.RequestError && error.status === 404) {
@@ -539,7 +679,8 @@ exports.ConfigurationOptionsSchema = z
vulnerability_check: z.boolean().default(true),
config_file: z.string().optional(),
base_ref: z.string().optional(),
head_ref: z.string().optional()
head_ref: z.string().optional(),
comment_summary_in_pr: z.boolean().default(false)
})
.superRefine((config, context) => {
if (config.allow_licenses && config.deny_licenses) {
@@ -44555,6 +44696,7 @@ function readInlineConfig() {
const vulnerability_check = getOptionalBoolean('vulnerability-check');
const base_ref = getOptionalInput('base-ref');
const head_ref = getOptionalInput('head-ref');
const comment_summary_in_pr = getOptionalBoolean('comment-summary-in-pr');
validateLicenses('allow-licenses', allow_licenses);
validateLicenses('deny-licenses', deny_licenses);
const keys = {
@@ -44566,7 +44708,8 @@ function readInlineConfig() {
license_check,
vulnerability_check,
base_ref,
head_ref
head_ref,
comment_summary_in_pr
};
return Object.fromEntries(Object.entries(keys).filter(([_, value]) => value !== undefined));
}
@@ -44831,7 +44974,8 @@ exports.ConfigurationOptionsSchema = z
vulnerability_check: z.boolean().default(true),
config_file: z.string().optional(),
base_ref: z.string().optional(),
head_ref: z.string().optional()
head_ref: z.string().optional(),
comment_summary_in_pr: z.boolean().default(false)
})
.superRefine((config, context) => {
if (config.allow_licenses && config.deny_licenses) {
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+84
View File
@@ -0,0 +1,84 @@
import * as github from '@actions/github'
import * as core from '@actions/core'
import * as githubUtils from '@actions/github/lib/utils'
import * as retry from '@octokit/plugin-retry'
import {RequestError} from '@octokit/request-error'
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry)
const octo = new retryingOctokit(
githubUtils.getOctokitOptions(core.getInput('repo-token', {required: true}))
)
// Comment Marker to identify an existing comment to update, so we don't spam the PR with comments
const COMMENT_MARKER = '<!-- dependency-review-pr-comment-marker -->'
export async function commentPr(summary: typeof core.summary): Promise<void> {
if (!github.context.payload.pull_request) {
core.warning(
'Not in the context of a pull request. Skipping comment creation.'
)
return
}
const commentBody = `${summary.stringify()}\n\n${COMMENT_MARKER}`
try {
const existingCommentId = await findCommentByMarker(COMMENT_MARKER)
if (existingCommentId) {
await octo.rest.issues.updateComment({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
comment_id: existingCommentId,
body: commentBody
})
} else {
await octo.rest.issues.createComment({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: github.context.payload.pull_request.number,
body: commentBody
})
}
} catch (error) {
if (error instanceof RequestError && error.status === 403) {
core.warning(
`Unable to write summary to pull-request. Make sure you are giving this workflow the permission 'pull-requests: write'.`
)
} else {
if (error instanceof Error) {
core.warning(
`Unable to comment summary to pull-request, received error: ${error.message}`
)
} else {
core.warning(
'Unable to comment summary to pull-request: Unexpected fatal error'
)
}
}
}
}
async function findCommentByMarker(
commentBodyIncludes: string
): Promise<number | undefined> {
const commentsIterator = octo.paginate.iterator(
octo.rest.issues.listComments,
{
owner: github.context.repo.owner,
repo: github.context.repo.repo,
// We are already checking if we are in the context of a pull request in the caller
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
issue_number: github.context.payload.pull_request!.number
}
)
for await (const {data: comments} of commentsIterator) {
const existingComment = comments.find(comment =>
comment.body?.includes(commentBodyIncludes)
)
if (existingComment) return existingComment.id
}
return undefined
}
+3 -1
View File
@@ -34,6 +34,7 @@ function readInlineConfig(): ConfigurationOptionsPartial {
const vulnerability_check = getOptionalBoolean('vulnerability-check')
const base_ref = getOptionalInput('base-ref')
const head_ref = getOptionalInput('head-ref')
const comment_summary_in_pr = getOptionalBoolean('comment-summary-in-pr')
validateLicenses('allow-licenses', allow_licenses)
validateLicenses('deny-licenses', deny_licenses)
@@ -47,7 +48,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
license_check,
vulnerability_check,
base_ref,
head_ref
head_ref,
comment_summary_in_pr
}
return Object.fromEntries(
+4
View File
@@ -15,6 +15,7 @@ import * as summary from './summary'
import {getRefs} from './git-refs'
import {groupDependenciesByManifest} from './utils'
import {commentPr} from './comment-pr'
async function run(): Promise<void> {
try {
@@ -69,6 +70,9 @@ async function run(): Promise<void> {
summary.addScannedDependencies(changes)
printScannedDependencies(changes)
if (config.comment_summary_in_pr) {
await commentPr(core.summary)
}
} catch (error) {
if (error instanceof RequestError && error.status === 404) {
core.setFailed(
+2 -1
View File
@@ -45,7 +45,8 @@ export const ConfigurationOptionsSchema = z
vulnerability_check: z.boolean().default(true),
config_file: z.string().optional(),
base_ref: z.string().optional(),
head_ref: z.string().optional()
head_ref: z.string().optional(),
comment_summary_in_pr: z.boolean().default(false)
})
.superRefine((config, context) => {
if (config.allow_licenses && config.deny_licenses) {