Compare commits

..

1 Commits

Author SHA1 Message Date
Justin Hutchings 9a82545adb Remove Ruby from devcontainer 2024-03-21 11:28:18 -07:00
13 changed files with 100 additions and 260 deletions
+1 -4
View File
@@ -2,8 +2,5 @@
"name": "Dependency Review Action",
"image": "mcr.microsoft.com/devcontainers/typescript-node:18",
"postCreateCommand": "npm install",
"remoteUser": "node",
"features": {
"ghcr.io/devcontainers/features/ruby:1": {}
}
"remoteUser": "node"
}
+4 -4
View File
@@ -77,14 +77,14 @@ Configure this action by either inlining these options in your workflow file, or
| `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` |
| `allow-dependencies-licenses`\* | Contains a list of packages that will be excluded from license checks. | Any package(s) in [purl](https://github.com/package-url/purl-spec) format | none |
| `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 the `pull-requests: write` permission. | `always`, `on-failure`, `never` | `never` |
| `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 the `pull-requests: write` permission. | `always`, `on-failure`, `never` | `never` |
| `deny-packages` | Any number of packages to block in a PR. | Package(s) in [purl](https://github.com/package-url/purl-spec) format | empty |
| `deny-groups` | Any number of groups (namespaces) to block in a PR. | Namespace(s) in [purl](https://github.com/package-url/purl-spec) format (no package name, no version number) | empty |
| `retry-on-snapshot-warnings`\* | Enable or disable retrying the action every 10 seconds while waiting for dependency submission actions to complete. | `true`, `false` | `false` |
| `retry-on-snapshot-warnings-timeout`\* | Maximum amount of time (in seconds) to retry the action while waiting for dependency submission actions to complete. | Any positive integer | 120 |
| `warn-only`+ | When set to `true`, the action will log all vulnerabilities as warnings regardless of the severity, and the action will complete with a `success` status. This overrides the `fail-on-severity` option. | `true`, `false` | `false` |
| `show-openssf-scorecard` | When set to `true`, the action will output information about all the known OpenSSF Scorecard scores for the dependencies changed in this pull request. | `true`, `false` | `true` |
| `warn-on-openssf-scorecard-level` | When `show-openssf-scorecard` is set to `true`, this option lets you configure the threshold for when a score is considered too low and gets a :warning: warning in the CI. | Any positive integer | 3 |
| `show-openssf-scorecard-levels` | When set to `true`, the action will output information about all the known OpenSSF Scorecard scores for the dependencies changed in this pull request. | `true`, `false` | `true` |
| `warn-on-openssf-scorecard-level` | When `show-openssf-scorecard-levels` is set to `true`, this option lets you configure the threshold for when a score is considered too low and gets a :warning: warning in the CI. | Any positive integer | 3 |
\*not supported for use with GitHub Enterprise Server
@@ -171,7 +171,7 @@ The Dependency Review GitHub Action check will only block a pull request from be
> Action outputs are unicode strings [with a 1MB size limit](https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#outputs-for-docker-container-and-javascript-actions).
> [!IMPORTANT]
> If you use these outputs in a run-step, you must store the output data in an environment variable instead of using the output directly. Using an output directly might break shell scripts. For example:
> If you use these outputs in a run-step, you must store the ouput data in an envrioment variable instead of using the output directly. Using an output directly might break shell scripts. For example:
>
> ```yaml
> env:
-21
View File
@@ -22,19 +22,6 @@ const npmChange: Change = {
]
}
const actionsChange: Change = {
manifest: 'workflow.yml',
change_type: 'added',
ecosystem: 'actions',
name: 'actions/checkout/',
version: 'v3',
package_url: 'pkg:githubactions/actions@v3',
license: 'MIT',
source_repository_url: 'null',
scope: 'runtime',
vulnerabilities: []
}
test('Get scorecard from API', async () => {
const changes: Changes = [npmChange]
const scorecard = await getScorecardLevels(changes)
@@ -51,11 +38,3 @@ test('Get project URL from deps.dev API', async () => {
)
expect(result).not.toBeNull()
})
test('Handles Actions special case', async () => {
const changes: Changes = [actionsChange]
const result = await getScorecardLevels(changes)
expect(result).not.toBeNull()
expect(result.dependencies).toHaveLength(1)
expect(result.dependencies[0].scorecard?.score).toBeGreaterThan(0)
})
+1 -37
View File
@@ -1,5 +1,5 @@
import {expect, jest, test} from '@jest/globals'
import {Change, Changes, ConfigurationOptions, Scorecard} from '../src/schemas'
import {Changes, ConfigurationOptions, Scorecard} from '../src/schemas'
import * as summary from '../src/summary'
import * as core from '@actions/core'
import {createTestChange} from './fixtures/create-test-change'
@@ -109,42 +109,6 @@ test('prints headline as h1', () => {
expect(text).toContain('<h1>Dependency Review</h1>')
})
test('returns minimal summary in case the core.summary is too large for a PR comment', () => {
let changes: Changes = [
createTestChange({name: 'lodash', version: '1.2.3'}),
createTestChange({name: 'colors', version: '2.3.4'}),
createTestChange({name: '@foo/bar', version: '*'}),
]
let minSummary: string = summary.addSummaryToSummary(
changes,
emptyInvalidLicenseChanges,
emptyChanges,
scorecard,
defaultConfig
)
// side effect DR report into core.summary as happens in main.ts
summary.addScannedDependencies(changes)
const text = core.summary.stringify()
expect(text).toContain('<h1>Dependency Review</h1>')
expect(minSummary).toContain('# Dependency Review')
expect(text).toContain('❌ 3 vulnerable package(s)')
expect(text).not.toContain('* ❌ 3 vulnerable package(s)')
expect(text).toContain('lodash')
expect(text).toContain('colors')
expect(text).toContain('@foo/bar')
expect(minSummary).toContain('* ❌ 3 vulnerable package(s)')
expect(minSummary).not.toContain('lodash')
expect(minSummary).not.toContain('colors')
expect(minSummary).not.toContain('@foo/bar')
expect(text.length).toBeGreaterThan(minSummary.length)
})
test('only includes "No vulnerabilities or license issues found"-message if both are configured and nothing was found', () => {
summary.addSummaryToSummary(
emptyChanges,
+7 -10
View File
@@ -1,13 +1,5 @@
# IMPORTANT
#
# Avoid setting default values for configuration options in
# this file, they will overwrite external configurations.
#
# If you are trying to find out the default value for a config
# option please take a look at the README or src/schemas.ts.
#
# If you are adding an option, make sure the Zod definition
# contains a default value.
# Avoid using default values for options here since they will
# end up overriding external configurations.
name: 'Dependency Review'
description: 'Prevent the introduction of dependencies with known vulnerabilities'
author: 'GitHub'
@@ -64,18 +56,23 @@ inputs:
retry-on-snapshot-warnings:
description: Whether to retry on snapshot warnings
required: false
default: false
retry-on-snapshot-warnings-timeout:
description: Number of seconds to wait before stopping snapshot retries.
required: false
default: 120
warn-only:
description: When set to `true` this action will always complete with success, overriding the `fail-on-severity` parameter.
required: false
default: false
show-openssf-scorecard:
description: Show a summary of the OpenSSF Scorecard scores.
required: false
default: true
warn-on-openssf-scorecard-level:
description: Numeric threshold for the OpenSSF Scorecard score. If the score is below this threshold, the action will warn you.
required: false
default: 3
outputs:
comment-content:
description: Prepared dependency report comment
Generated Vendored
+8 -62
View File
@@ -56,10 +56,10 @@ 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 -->';
const MAX_COMMENT_LENGTH = 65536;
function commentPr(summary, config, minComment) {
function commentPr(summary, config) {
return __awaiter(this, void 0, void 0, function* () {
const commentContent = summary.stringify();
core.setOutput('comment-content', commentContent);
if (!(config.comment_summary_in_pr === 'always' ||
(config.comment_summary_in_pr === 'on-failure' &&
process.exitCode === core.ExitCode.Failure))) {
@@ -69,11 +69,7 @@ function commentPr(summary, config, minComment) {
core.warning('Not in the context of a pull request. Skipping comment creation.');
return;
}
let commentBody = `${commentContent}\n\n${COMMENT_MARKER}`;
if (commentBody.length >= MAX_COMMENT_LENGTH) {
core.debug('The comment was too big for the GitHub API. Falling back on a minimum comment');
commentBody = `${minComment}\n\n${COMMENT_MARKER}`;
}
const commentBody = `${commentContent}\n\n${COMMENT_MARKER}`;
try {
const existingCommentId = yield findCommentByMarker(COMMENT_MARKER);
if (existingCommentId) {
@@ -642,7 +638,6 @@ function run() {
const deniedChanges = yield (0, deny_1.getDeniedChanges)(filteredChanges, config.deny_packages, config.deny_groups);
const scorecard = yield (0, scorecard_1.getScorecardLevels)(filteredChanges);
summary.addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedChanges, scorecard, config);
const minSummary = summary.getMinSummaryForComment(vulnerableChanges, invalidLicenseChanges, deniedChanges, scorecard, config);
if (snapshot_warnings) {
summary.addSnapshotWarnings(config, snapshot_warnings);
}
@@ -669,7 +664,7 @@ function run() {
core.setOutput('dependency-changes', JSON.stringify(changes));
summary.addScannedDependencies(changes);
printScannedDependencies(changes);
yield (0, comment_pr_1.commentPr)(core.summary, config, minSummary);
yield (0, comment_pr_1.commentPr)(core.summary, config);
}
catch (error) {
if (error instanceof request_error_1.RequestError && error.status === 404) {
@@ -1048,15 +1043,8 @@ function getScorecardLevels(changes) {
if (repositoryUrl === null || repositoryUrl === void 0 ? void 0 : repositoryUrl.startsWith('https://')) {
repositoryUrl = repositoryUrl.replace('https://', '');
}
// Handle the special case for GitHub Actions, where the repository URL is null
if (ecosystem === 'actions') {
// The package name for GitHub Actions in the API is in the format `owner/repo/`, so we can use that to get the repository URL
// If the package name has more than 2 slashes, it's referencing a sub-action, and we need to strip the last part out
const parts = packageName.split('/');
repositoryUrl = `github.com/${parts[0]}/${parts[1]}`; // e.g. github.com/actions/checkout
}
// If GitHub API doesn't have the repository URL, query deps.dev for it.
if (!repositoryUrl) {
if (repositoryUrl) {
// Call the deps.dev API to get the repository URL from there
repositoryUrl = yield getProjectUrl(ecosystem, packageName, version);
}
@@ -1081,7 +1069,7 @@ function getScorecardLevels(changes) {
exports.getScorecardLevels = getScorecardLevels;
function getScorecard(repositoryUrl) {
return __awaiter(this, void 0, void 0, function* () {
const apiRoot = 'https://api.securityscorecards.dev';
const apiRoot = 'https://api.securityscorecards.dev/';
let scorecardResponse = {};
const url = `${apiRoot}/projects/${repositoryUrl}`;
const response = yield fetch(url);
@@ -1143,7 +1131,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.addDeniedToSummary = exports.addSnapshotWarnings = exports.addScorecardToSummary = exports.addScannedDependencies = exports.addLicensesToSummary = exports.addChangeVulnerabilitiesToSummary = exports.addSummaryToSummary = exports.getMinSummaryForComment = void 0;
exports.addDeniedToSummary = exports.addSnapshotWarnings = exports.addScorecardToSummary = exports.addScannedDependencies = exports.addLicensesToSummary = exports.addChangeVulnerabilitiesToSummary = exports.addSummaryToSummary = void 0;
const core = __importStar(__nccwpck_require__(2186));
const utils_1 = __nccwpck_require__(918);
const icons = {
@@ -1151,48 +1139,6 @@ const icons = {
cross: '❌',
warning: '⚠️'
};
function getMinSummaryForComment(vulnerableChanges, invalidLicenseChanges, deniedChanges, scorecard, config) {
const scorecardWarnings = countScorecardWarnings(scorecard, config);
const licenseIssues = countLicenseIssues(invalidLicenseChanges);
let minSummary = '# Dependency Review\n';
if (vulnerableChanges.length === 0 &&
licenseIssues === 0 &&
deniedChanges.length === 0 &&
scorecardWarnings === 0) {
const issueTypes = [
config.vulnerability_check ? 'vulnerabilities' : '',
config.license_check ? 'license issues' : '',
config.show_openssf_scorecard ? 'OpenSSF Scorecard issues' : ''
];
if (issueTypes.filter(Boolean).length === 0) {
minSummary += `${icons.check} No issues found.`;
}
else {
minSummary += `${icons.check} No ${issueTypes.filter(Boolean).join(' or ')} found.`;
}
}
minSummary += 'The following issues were found:\n';
minSummary += config.vulnerability_check
? `* ${checkOrFailIcon(vulnerableChanges.length)} ${vulnerableChanges.length} vulnerable package(s)\n`
: '';
minSummary += config.license_check
? `* ${checkOrFailIcon(invalidLicenseChanges.forbidden.length)} ${invalidLicenseChanges.forbidden.length} package(s) with incompatible licenses\n
* ${checkOrFailIcon(invalidLicenseChanges.unresolved.length)} ${invalidLicenseChanges.unresolved.length} package(s) with invalid SPDX license definitions\n
* ${checkOrWarnIcon(invalidLicenseChanges.unlicensed.length)} ${invalidLicenseChanges.unlicensed.length} package(s) with unknown licenses.\n`
: '';
minSummary +=
deniedChanges.length > 0
? `* ${checkOrWarnIcon(deniedChanges.length)} ${deniedChanges.length} package(s) denied.\n`
: '';
minSummary +=
config.show_openssf_scorecard && scorecardWarnings > 0
? `* ${checkOrWarnIcon(scorecardWarnings)} ${scorecardWarnings ? scorecardWarnings : 'No'} packages with OpenSSF Scorecard issues.\n`
: '';
// Add the link to the job summary provided by GitHub Actions for this workflow run
minSummary += `\n[View full job summary](${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID})`;
return minSummary;
}
exports.getMinSummaryForComment = getMinSummaryForComment;
function addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedChanges, scorecard, config) {
const scorecardWarnings = countScorecardWarnings(scorecard, config);
const licenseIssues = countLicenseIssues(invalidLicenseChanges);
@@ -1405,7 +1351,7 @@ function addScorecardToSummary(scorecard, config) {
: ':green_circle:';
}
//Add a row for the dependency
core.summary.addRaw(`<tr><td>${dependency.change.source_repository_url ? `<a href="${dependency.change.source_repository_url}">` : ''} ${dependency.change.ecosystem}/${dependency.change.name} ${dependency.change.source_repository_url ? `</a>` : ''}</td><td>${dependency.change.version}</td>
core.summary.addRaw(`<tr><td>${dependency.change.source_repository_url ? `<a href="https://${dependency.change.source_repository_url}">` : ''} ${dependency.change.ecosystem}/${dependency.change.name} ${dependency.change.source_repository_url ? `</a>` : ''}</td><td>${dependency.change.version}</td>
<td>${overallIcon} ${((_d = dependency.scorecard) === null || _d === void 0 ? void 0 : _d.score) === undefined || ((_e = dependency.scorecard) === null || _e === void 0 ? void 0 : _e.score) === null ? 'Unknown' : (_f = dependency.scorecard) === null || _f === void 0 ? void 0 : _f.score}</td>`, false);
//Add details table in the last column
if (((_g = dependency.scorecard) === null || _g === void 0 ? void 0 : _g.checks) !== undefined) {
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+19 -19
View File
@@ -1,12 +1,12 @@
{
"name": "dependency-review-action",
"version": "4.2.5",
"version": "4.2.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dependency-review-action",
"version": "4.2.5",
"version": "4.2.3",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.1",
@@ -33,7 +33,7 @@
"@typescript-eslint/parser": "^6.21.0",
"@vercel/ncc": "^0.38.0",
"esbuild-register": "^3.5.0",
"eslint": "^8.57.0",
"eslint": "^8.56.0",
"eslint-plugin-github": "^4.10.1",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-prettier": "^5.1.3",
@@ -1096,9 +1096,9 @@
}
},
"node_modules/@eslint/js": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1119,13 +1119,13 @@
"dev": true
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
"version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
"dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^2.0.2",
"debug": "^4.3.1",
"@humanwhocodes/object-schema": "^2.0.1",
"debug": "^4.1.1",
"minimatch": "^3.0.5"
},
"engines": {
@@ -1146,9 +1146,9 @@
}
},
"node_modules/@humanwhocodes/object-schema": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
},
"node_modules/@istanbuljs/load-nyc-config": {
@@ -3542,16 +3542,16 @@
}
},
"node_modules/eslint": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
"integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.4",
"@eslint/js": "8.57.0",
"@humanwhocodes/config-array": "^0.11.14",
"@eslint/js": "8.56.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"@ungap/structured-clone": "^1.2.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "dependency-review-action",
"version": "4.2.5",
"version": "4.2.3",
"private": true,
"description": "A GitHub Action for Dependency Review",
"main": "lib/main.js",
@@ -49,7 +49,7 @@
"@typescript-eslint/parser": "^6.21.0",
"@vercel/ncc": "^0.38.0",
"esbuild-register": "^3.5.0",
"eslint": "^8.57.0",
"eslint": "^8.56.0",
"eslint-plugin-github": "^4.10.1",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-prettier": "^5.1.3",
+7 -5
View File
@@ -5,8 +5,6 @@ import * as retry from '@octokit/plugin-retry'
import {RequestError} from '@octokit/request-error'
import {ConfigurationOptions} from './schemas'
export const MAX_COMMENT_LENGTH = 65536
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry)
const octo = new retryingOctokit(
githubUtils.getOctokitOptions(core.getInput('repo-token', {required: true}))
@@ -16,9 +14,13 @@ const octo = new retryingOctokit(
const COMMENT_MARKER = '<!-- dependency-review-pr-comment-marker -->'
export async function commentPr(
commentContent: string,
config: ConfigurationOptions,
summary: typeof core.summary,
config: ConfigurationOptions
): Promise<void> {
const commentContent = summary.stringify()
core.setOutput('comment-content', commentContent)
if (
!(
config.comment_summary_in_pr === 'always' ||
@@ -36,7 +38,7 @@ export async function commentPr(
return
}
let commentBody = `${commentContent}\n\n${COMMENT_MARKER}`
const commentBody = `${commentContent}\n\n${COMMENT_MARKER}`
try {
const existingCommentId = await findCommentByMarker(COMMENT_MARKER)
+3 -21
View File
@@ -22,13 +22,9 @@ import * as summary from './summary'
import {getRefs} from './git-refs'
import {groupDependenciesByManifest} from './utils'
import {
commentPr,
MAX_COMMENT_LENGTH
} from './comment-pr'
import {commentPr} from './comment-pr'
import {getDeniedChanges} from './deny'
async function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -131,7 +127,7 @@ async function run(): Promise<void> {
const scorecard = await getScorecardLevels(filteredChanges)
const minSummary = summary.addSummaryToSummary(
summary.addSummaryToSummary(
vulnerableChanges,
invalidLicenseChanges,
deniedChanges,
@@ -170,21 +166,7 @@ async function run(): Promise<void> {
core.setOutput('dependency-changes', JSON.stringify(changes))
summary.addScannedDependencies(changes)
printScannedDependencies(changes)
// include full summary in output; Actions will truncate if oversized
let rendered = core.summary.stringify()
core.setOutput('content-comment', rendered)
// if the summary is oversized, replace with minimal version
if (rendered.length >= MAX_COMMENT_LENGTH) {
core.debug(
'The comment was too big for the GitHub API. Falling back on a minimum comment'
)
rendered = minSummary
}
// update the PR comment if needed with the right-sized summary
await commentPr(rendered, config)
await commentPr(core.summary, config)
} catch (error) {
if (error instanceof RequestError && error.status === 404) {
core.setFailed(
+2 -10
View File
@@ -17,16 +17,8 @@ export async function getScorecardLevels(
repositoryUrl = repositoryUrl.replace('https://', '')
}
// Handle the special case for GitHub Actions, where the repository URL is null
if (ecosystem === 'actions') {
// The package name for GitHub Actions in the API is in the format `owner/repo/`, so we can use that to get the repository URL
// If the package name has more than 2 slashes, it's referencing a sub-action, and we need to strip the last part out
const parts = packageName.split('/')
repositoryUrl = `github.com/${parts[0]}/${parts[1]}` // e.g. github.com/actions/checkout
}
// If GitHub API doesn't have the repository URL, query deps.dev for it.
if (!repositoryUrl) {
if (repositoryUrl) {
// Call the deps.dev API to get the repository URL from there
repositoryUrl = await getProjectUrl(ecosystem, packageName, version)
}
@@ -49,7 +41,7 @@ export async function getScorecardLevels(
}
async function getScorecard(repositoryUrl: string): Promise<ScorecardApi> {
const apiRoot = 'https://api.securityscorecards.dev'
const apiRoot = 'https://api.securityscorecards.dev/'
let scorecardResponse: ScorecardApi = {} as ScorecardApi
const url = `${apiRoot}/projects/${repositoryUrl}`
+45 -64
View File
@@ -10,23 +10,17 @@ const icons = {
warning: '⚠️'
}
// generates the DR report summmary and caches it to the Action's core.summary.
// returns the DR summary string, ready to be posted as a PR comment if the
// final DR report is too large
export function addSummaryToSummary(
vulnerableChanges: Changes,
invalidLicenseChanges: InvalidLicenseChanges,
deniedChanges: Changes,
scorecard: Scorecard,
config: ConfigurationOptions,
): string {
let out: string[] = [];
config: ConfigurationOptions
): void {
const scorecardWarnings = countScorecardWarnings(scorecard, config)
const licenseIssues = countLicenseIssues(invalidLicenseChanges)
core.summary.addHeading('Dependency Review', 1)
out.push('# Dependency Review')
if (
vulnerableChanges.length === 0 &&
@@ -39,67 +33,54 @@ export function addSummaryToSummary(
config.license_check ? 'license issues' : '',
config.show_openssf_scorecard ? 'OpenSSF Scorecard issues' : ''
]
let msg = ''
if (issueTypes.filter(Boolean).length === 0) {
msg = `${icons.check} No issues found.`
core.summary.addRaw(`${icons.check} No issues found.`)
} else {
msg = `${icons.check} No ${issueTypes.filter(Boolean).join(' or ')} found.`
core.summary.addRaw(
`${icons.check} No ${issueTypes.filter(Boolean).join(' or ')} found.`
)
}
core.summary.addRaw(msg)
out.push(msg)
return out.join('\n')
return
}
const foundIssuesHeader = 'The following issues were found:'
core.summary.addRaw(foundIssuesHeader)
out.push(foundIssuesHeader)
const summaryList: string[] = [
...(config.vulnerability_check
? [
`${checkOrFailIcon(vulnerableChanges.length)} ${
vulnerableChanges.length
} vulnerable package(s)`
]
: []),
...(config.license_check
? [
`${checkOrFailIcon(invalidLicenseChanges.forbidden.length)} ${
invalidLicenseChanges.forbidden.length
} package(s) with incompatible licenses`,
`${checkOrFailIcon(invalidLicenseChanges.unresolved.length)} ${
invalidLicenseChanges.unresolved.length
} package(s) with invalid SPDX license definitions`,
`${checkOrWarnIcon(invalidLicenseChanges.unlicensed.length)} ${
invalidLicenseChanges.unlicensed.length
} package(s) with unknown licenses.`
]
: []),
...(deniedChanges.length > 0
? [
`${checkOrWarnIcon(deniedChanges.length)} ${
deniedChanges.length
} package(s) denied.`
]
: []),
...(config.show_openssf_scorecard && scorecardWarnings > 0
? [
`${checkOrWarnIcon(scorecardWarnings)} ${scorecardWarnings ? scorecardWarnings : 'No'} packages with OpenSSF Scorecard issues.`
]
: [])
];
core.summary.addList(summaryList)
summaryList.forEach( (line) => {
out.push('* ' + line)
})
core.summary.addRaw('See the Details below.')
out.push(`\n[View full job summary](${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID})`)
return out.join('\n')
core.summary
.addRaw('The following issues were found:')
.addList([
...(config.vulnerability_check
? [
`${checkOrFailIcon(vulnerableChanges.length)} ${
vulnerableChanges.length
} vulnerable package(s)`
]
: []),
...(config.license_check
? [
`${checkOrFailIcon(invalidLicenseChanges.forbidden.length)} ${
invalidLicenseChanges.forbidden.length
} package(s) with incompatible licenses`,
`${checkOrFailIcon(invalidLicenseChanges.unresolved.length)} ${
invalidLicenseChanges.unresolved.length
} package(s) with invalid SPDX license definitions`,
`${checkOrWarnIcon(invalidLicenseChanges.unlicensed.length)} ${
invalidLicenseChanges.unlicensed.length
} package(s) with unknown licenses.`
]
: []),
...(deniedChanges.length > 0
? [
`${checkOrWarnIcon(deniedChanges.length)} ${
deniedChanges.length
} package(s) denied.`
]
: []),
...(config.show_openssf_scorecard && scorecardWarnings > 0
? [
`${checkOrWarnIcon(scorecardWarnings)} ${scorecardWarnings ? scorecardWarnings : 'No'} packages with OpenSSF Scorecard issues.`
]
: [])
])
.addRaw('See the Details below.')
}
function countScorecardWarnings(
@@ -323,7 +304,7 @@ export function addScorecardToSummary(
//Add a row for the dependency
core.summary.addRaw(
`<tr><td>${dependency.change.source_repository_url ? `<a href="${dependency.change.source_repository_url}">` : ''} ${dependency.change.ecosystem}/${dependency.change.name} ${dependency.change.source_repository_url ? `</a>` : ''}</td><td>${dependency.change.version}</td>
`<tr><td>${dependency.change.source_repository_url ? `<a href="https://${dependency.change.source_repository_url}">` : ''} ${dependency.change.ecosystem}/${dependency.change.name} ${dependency.change.source_repository_url ? `</a>` : ''}</td><td>${dependency.change.version}</td>
<td>${overallIcon} ${dependency.scorecard?.score === undefined || dependency.scorecard?.score === null ? 'Unknown' : dependency.scorecard?.score}</td>`,
false
)