merge from main and fix code review comment from @juxtin
This commit is contained in:
@@ -74,8 +74,8 @@ async function findCommentByMarker(
|
||||
)
|
||||
|
||||
for await (const {data: comments} of commentsIterator) {
|
||||
const existingComment = comments.find(comment =>
|
||||
comment.body?.includes(commentBodyIncludes)
|
||||
const existingComment = comments.find(
|
||||
comment => comment.body?.includes(commentBodyIncludes)
|
||||
)
|
||||
if (existingComment) return existingComment.id
|
||||
}
|
||||
|
||||
@@ -33,12 +33,20 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
const allow_dependencies_licenses = parseList(
|
||||
getOptionalInput('allow-dependencies-licenses')
|
||||
)
|
||||
const deny_packages = parseList(getOptionalInput('deny-packages'))
|
||||
const deny_groups = parseList(getOptionalInput('deny-groups'))
|
||||
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')
|
||||
const comment_summary_in_pr = getOptionalBoolean('comment-summary-in-pr')
|
||||
const comment_summary_in_pr = getOptionalInput('comment-summary-in-pr')
|
||||
const retry_on_snapshot_warnings = getOptionalBoolean(
|
||||
'retry-on-snapshot-warnings'
|
||||
)
|
||||
const retry_on_snapshot_warnings_timeout = getOptionalNumber(
|
||||
'retry-on-snapshot-warnings-timeout'
|
||||
)
|
||||
const warn_only = getOptionalBoolean('warn-only')
|
||||
|
||||
validatePURL(allow_dependencies_licenses)
|
||||
@@ -50,6 +58,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
fail_on_scopes,
|
||||
allow_licenses,
|
||||
deny_licenses,
|
||||
deny_packages,
|
||||
deny_groups,
|
||||
allow_dependencies_licenses,
|
||||
allow_ghsas,
|
||||
license_check,
|
||||
@@ -57,6 +67,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
base_ref,
|
||||
head_ref,
|
||||
comment_summary_in_pr,
|
||||
retry_on_snapshot_warnings,
|
||||
retry_on_snapshot_warnings_timeout,
|
||||
warn_only
|
||||
}
|
||||
|
||||
@@ -65,6 +77,12 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
)
|
||||
}
|
||||
|
||||
function getOptionalNumber(name: string): number | undefined {
|
||||
const value = core.getInput(name)
|
||||
const parsed = z.string().regex(/^\d+$/).transform(Number).safeParse(value)
|
||||
return parsed.success ? parsed.data : undefined
|
||||
}
|
||||
|
||||
function getOptionalBoolean(name: string): boolean | undefined {
|
||||
const value = core.getInput(name)
|
||||
return value.length > 0 ? core.getBooleanInput(name) : undefined
|
||||
@@ -139,7 +157,9 @@ function parseConfigFile(configData: string): ConfigurationOptionsPartial {
|
||||
'deny-licenses',
|
||||
'fail-on-scopes',
|
||||
'allow-ghsas',
|
||||
'allow-dependencies-licenses'
|
||||
'allow-dependencies-licenses',
|
||||
'deny-packages',
|
||||
'deny-groups'
|
||||
]
|
||||
|
||||
for (const key of Object.keys(data)) {
|
||||
|
||||
42
src/deny.ts
Normal file
42
src/deny.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import {Change} from './schemas'
|
||||
import * as core from '@actions/core'
|
||||
|
||||
export async function getDeniedChanges(
|
||||
changes: Change[],
|
||||
deniedPackages: string[],
|
||||
deniedGroups: string[]
|
||||
): Promise<Change[]> {
|
||||
const changesDenied: Change[] = []
|
||||
|
||||
let failed = false
|
||||
for (const change of changes) {
|
||||
change.name = change.name.toLowerCase()
|
||||
const packageUrl = change.package_url.toLowerCase().split('@')[0]
|
||||
|
||||
if (deniedPackages) {
|
||||
for (const denied of deniedPackages) {
|
||||
if (packageUrl === denied.split('@')[0].toLowerCase()) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deniedGroups) {
|
||||
for (const denied of deniedGroups) {
|
||||
if (packageUrl.startsWith(denied.toLowerCase())) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
core.setFailed('Dependency review detected denied packages.')
|
||||
} else {
|
||||
core.info('Dependency review did not detect any denied packages')
|
||||
}
|
||||
|
||||
return changesDenied
|
||||
}
|
||||
@@ -31,7 +31,8 @@ export async function compare({
|
||||
url: '/repos/{owner}/{repo}/dependency-graph/compare/{basehead}',
|
||||
owner,
|
||||
repo,
|
||||
basehead: `${baseRef}...${headRef}`
|
||||
basehead: `${baseRef}...${headRef}`,
|
||||
per_page: 5
|
||||
},
|
||||
response => {
|
||||
if (
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import {Changes, Severity, SEVERITIES, Scope} from './schemas'
|
||||
|
||||
/**
|
||||
* Filters changes by a severity level. Only vulnerable
|
||||
* dependencies will be returned.
|
||||
*
|
||||
* @param severity - The severity level to filter by.
|
||||
* @param changes - The array of changes to filter.
|
||||
* @returns The filtered array of changes that match the specified severity level and have vulnerabilities.
|
||||
*/
|
||||
export function filterChangesBySeverity(
|
||||
severity: Severity,
|
||||
changes: Changes
|
||||
@@ -31,7 +39,14 @@ export function filterChangesBySeverity(
|
||||
filteredChanges = filteredChanges.filter(
|
||||
change => change.vulnerabilities.length > 0
|
||||
)
|
||||
return filteredChanges
|
||||
|
||||
// only report vulnerability additions
|
||||
return filteredChanges.filter(
|
||||
change =>
|
||||
change.change_type === 'added' &&
|
||||
change.vulnerabilities !== undefined &&
|
||||
change.vulnerabilities.length > 0
|
||||
)
|
||||
}
|
||||
|
||||
export function filterChangesByScopes(
|
||||
@@ -67,25 +82,20 @@ export function filterAllowedAdvisories(
|
||||
return changes
|
||||
}
|
||||
|
||||
const filteredChanges = changes.filter(change => {
|
||||
const filteredChanges = changes.map(change => {
|
||||
const noAdvisories =
|
||||
change.vulnerabilities === undefined ||
|
||||
change.vulnerabilities.length === 0
|
||||
|
||||
if (noAdvisories) {
|
||||
return true
|
||||
return change
|
||||
}
|
||||
const newChange = {...change}
|
||||
newChange.vulnerabilities = change.vulnerabilities.filter(
|
||||
vuln => !ghsas.includes(vuln.advisory_ghsa_id)
|
||||
)
|
||||
|
||||
let allAllowedAdvisories = true
|
||||
// if there's at least one advisory that is not allowlisted, we will keep the change
|
||||
for (const vulnerability of change.vulnerabilities) {
|
||||
if (!ghsas.includes(vulnerability.advisory_ghsa_id)) {
|
||||
allAllowedAdvisories = false
|
||||
}
|
||||
if (!allAllowedAdvisories) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return newChange
|
||||
})
|
||||
|
||||
return filteredChanges
|
||||
|
||||
@@ -32,7 +32,7 @@ export async function getInvalidLicenseChanges(
|
||||
const {allow, deny} = licenses
|
||||
const licenseExclusions = licenses.licenseExclusions?.map(
|
||||
(pkgUrl: string) => {
|
||||
return PackageURL.fromString(pkgUrl)
|
||||
return PackageURL.fromString(encodeURI(pkgUrl))
|
||||
}
|
||||
)
|
||||
|
||||
@@ -45,7 +45,9 @@ export async function getInvalidLicenseChanges(
|
||||
return true
|
||||
}
|
||||
|
||||
const changeAsPackageURL = PackageURL.fromString(change.package_url)
|
||||
const changeAsPackageURL = PackageURL.fromString(
|
||||
encodeURI(change.package_url)
|
||||
)
|
||||
|
||||
// We want to find if the licenseExclussion list contains the PackageURL of the Change
|
||||
// If it does, we want to filter it out and therefore return false
|
||||
|
||||
147
src/main.ts
147
src/main.ts
@@ -3,7 +3,7 @@ import * as dependencyGraph from './dependency-graph'
|
||||
import * as github from '@actions/github'
|
||||
import styles from 'ansi-styles'
|
||||
import {RequestError} from '@octokit/request-error'
|
||||
import {Change, Severity, Changes} from './schemas'
|
||||
import {Change, Severity, Changes, ConfigurationOptions} from './schemas'
|
||||
import {readConfig} from '../src/config'
|
||||
import {
|
||||
filterChangesBySeverity,
|
||||
@@ -16,6 +16,43 @@ import {getRefs} from './git-refs'
|
||||
|
||||
import {groupDependenciesByManifest} from './utils'
|
||||
import {commentPr} from './comment-pr'
|
||||
import {getDeniedChanges} from './deny'
|
||||
|
||||
async function delay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
async function getComparison(
|
||||
baseRef: string,
|
||||
headRef: string,
|
||||
retryOpts?: {
|
||||
retryUntil: number
|
||||
retryDelay: number
|
||||
}
|
||||
): ReturnType<typeof dependencyGraph.compare> {
|
||||
const comparison = await dependencyGraph.compare({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
baseRef,
|
||||
headRef
|
||||
})
|
||||
|
||||
if (comparison.snapshot_warnings.trim() !== '') {
|
||||
core.info(comparison.snapshot_warnings)
|
||||
if (retryOpts !== undefined) {
|
||||
if (retryOpts.retryUntil < Date.now()) {
|
||||
core.info(`Retry timeout exceeded. Proceeding...`)
|
||||
return comparison
|
||||
} else {
|
||||
core.info(`Retrying in ${retryOpts.retryDelay} seconds...`)
|
||||
await delay(retryOpts.retryDelay * 1000)
|
||||
return getComparison(baseRef, headRef, retryOpts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return comparison
|
||||
}
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
@@ -23,12 +60,18 @@ async function run(): Promise<void> {
|
||||
|
||||
const refs = getRefs(config, github.context)
|
||||
|
||||
const comparison = await dependencyGraph.compare({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
baseRef: refs.base,
|
||||
headRef: refs.head
|
||||
})
|
||||
const comparison = await getComparison(
|
||||
refs.base,
|
||||
refs.head,
|
||||
config.retry_on_snapshot_warnings
|
||||
? {
|
||||
retryUntil:
|
||||
Date.now() + config.retry_on_snapshot_warnings_timeout * 1000,
|
||||
retryDelay: 10
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
|
||||
const changes = comparison.changes
|
||||
const snapshot_warnings = comparison.snapshot_warnings
|
||||
|
||||
@@ -37,28 +80,24 @@ async function run(): Promise<void> {
|
||||
return
|
||||
}
|
||||
|
||||
const failOnSeverityParams = config.fail_on_severity
|
||||
const warnOnly = config.warn_only
|
||||
let minSeverity: Severity = 'low'
|
||||
// If failOnSeverityParams is not set or warnOnly is true, the minSeverity is low, to allow all vulnerabilities to be reported as warnings
|
||||
if (failOnSeverityParams && !warnOnly) {
|
||||
minSeverity = failOnSeverityParams
|
||||
}
|
||||
|
||||
const scopedChanges = filterChangesByScopes(config.fail_on_scopes, changes)
|
||||
|
||||
const filteredChanges = filterAllowedAdvisories(
|
||||
config.allow_ghsas,
|
||||
scopedChanges
|
||||
)
|
||||
|
||||
let minSeverity = config.fail_on_severity
|
||||
const failOnSeverityParams = config.fail_on_severity
|
||||
const warnOnly = config.warn_only
|
||||
// If failOnSeverityParams is not set or warnOnly is true, the minSeverity is low, to allow all vulnerabilities to be reported as warnings
|
||||
if (failOnSeverityParams && !warnOnly) {
|
||||
minSeverity = failOnSeverityParams
|
||||
}
|
||||
|
||||
const vulnerableChanges = filterChangesBySeverity(
|
||||
minSeverity,
|
||||
filteredChanges
|
||||
).filter(
|
||||
change =>
|
||||
change.change_type === 'added' &&
|
||||
change.vulnerabilities !== undefined &&
|
||||
change.vulnerabilities.length > 0
|
||||
)
|
||||
|
||||
const invalidLicenseChanges = await getInvalidLicenseChanges(
|
||||
@@ -70,28 +109,46 @@ async function run(): Promise<void> {
|
||||
}
|
||||
)
|
||||
|
||||
core.debug(`Filtered Changes: ${JSON.stringify(filteredChanges)}`)
|
||||
core.debug(`Config Deny Packages: ${JSON.stringify(config)}`)
|
||||
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
filteredChanges,
|
||||
config.deny_packages,
|
||||
config.deny_groups
|
||||
)
|
||||
|
||||
summary.addSummaryToSummary(
|
||||
vulnerableChanges,
|
||||
invalidLicenseChanges,
|
||||
deniedChanges,
|
||||
config
|
||||
)
|
||||
|
||||
if (snapshot_warnings) {
|
||||
summary.addSnapshotWarnings(snapshot_warnings)
|
||||
summary.addSnapshotWarnings(config, snapshot_warnings)
|
||||
}
|
||||
|
||||
if (config.vulnerability_check) {
|
||||
summary.addChangeVulnerabilitiesToSummary(vulnerableChanges, minSeverity)
|
||||
printVulnerabilitiesBlock(vulnerableChanges, minSeverity, warnOnly)
|
||||
printVulnerabilitiesBlock(vulnerableChanges, minSeverity)
|
||||
}
|
||||
if (config.license_check) {
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config)
|
||||
printLicensesBlock(invalidLicenseChanges, warnOnly)
|
||||
printLicensesBlock(invalidLicenseChanges)
|
||||
}
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
summary.addDeniedToSummary(deniedChanges)
|
||||
printDeniedDependencies(deniedChanges, config)
|
||||
}
|
||||
|
||||
summary.addScannedDependencies(changes)
|
||||
printScannedDependencies(changes)
|
||||
if (config.comment_summary_in_pr) {
|
||||
if (
|
||||
config.comment_summary_in_pr === 'always' ||
|
||||
(config.comment_summary_in_pr === 'on-failure' &&
|
||||
process.exitCode === core.ExitCode.Failure)
|
||||
) {
|
||||
await commentPr(core.summary)
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -117,25 +174,19 @@ async function run(): Promise<void> {
|
||||
|
||||
function printVulnerabilitiesBlock(
|
||||
addedChanges: Changes,
|
||||
minSeverity: Severity,
|
||||
warnOnly: boolean
|
||||
minSeverity: Severity
|
||||
): void {
|
||||
let vulFound = false
|
||||
let failed = false
|
||||
core.group('Vulnerabilities', async () => {
|
||||
if (addedChanges.length > 0) {
|
||||
for (const change of addedChanges) {
|
||||
printChangeVulnerabilities(change)
|
||||
}
|
||||
vulFound = true
|
||||
failed = true
|
||||
}
|
||||
|
||||
if (vulFound) {
|
||||
const msg = 'Dependency review detected vulnerable packages.'
|
||||
if (warnOnly) {
|
||||
core.warning(msg)
|
||||
} else {
|
||||
core.setFailed(msg)
|
||||
}
|
||||
if (failed) {
|
||||
core.setFailed('Dependency review detected vulnerable packages.')
|
||||
} else {
|
||||
core.info(
|
||||
`Dependency review did not detect any vulnerable packages with severity level "${minSeverity}" or higher.`
|
||||
@@ -158,19 +209,13 @@ function printChangeVulnerabilities(change: Change): void {
|
||||
}
|
||||
|
||||
function printLicensesBlock(
|
||||
invalidLicenseChanges: Record<string, Changes>,
|
||||
warnOnly: boolean
|
||||
invalidLicenseChanges: Record<string, Changes>
|
||||
): void {
|
||||
core.group('Licenses', async () => {
|
||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
||||
core.info('\nThe following dependencies have incompatible licenses:')
|
||||
printLicensesError(invalidLicenseChanges.forbidden)
|
||||
const msg = 'Dependency review detected incompatible licenses.'
|
||||
if (warnOnly) {
|
||||
core.warning(msg)
|
||||
} else {
|
||||
core.setFailed(msg)
|
||||
}
|
||||
core.setFailed('Dependency review detected incompatible licenses.')
|
||||
}
|
||||
if (invalidLicenseChanges.unresolved.length > 0) {
|
||||
core.warning(
|
||||
@@ -258,4 +303,20 @@ function printScannedDependencies(changes: Changes): void {
|
||||
})
|
||||
}
|
||||
|
||||
function printDeniedDependencies(
|
||||
changes: Change[],
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.group('Denied', async () => {
|
||||
for (const denied of config.deny_packages) {
|
||||
core.info(`Config: ${denied}`)
|
||||
}
|
||||
|
||||
for (const change of changes) {
|
||||
core.info(`Change: ${change.name}@${change.version} is denied`)
|
||||
core.info(`Change: ${change.package_url} is denied`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import * as z from 'zod'
|
||||
|
||||
export const FAIL_ON_SEVERITIES = [
|
||||
'critical',
|
||||
'high',
|
||||
'moderate',
|
||||
'low'
|
||||
] as const
|
||||
export const SEVERITIES = ['critical', 'high', 'moderate', 'low'] as const
|
||||
export const SCOPES = ['unknown', 'runtime', 'development'] as const
|
||||
|
||||
export const FailOnSeveritySchema = z.enum(FAIL_ON_SEVERITIES).default('low')
|
||||
export const SeveritySchema = z.enum(SEVERITIES).default('low')
|
||||
|
||||
export const ChangeSchema = z.object({
|
||||
@@ -43,20 +36,40 @@ export const PullRequestSchema = z.object({
|
||||
|
||||
export const ConfigurationOptionsSchema = z
|
||||
.object({
|
||||
fail_on_severity: FailOnSeveritySchema,
|
||||
fail_on_severity: SeveritySchema,
|
||||
fail_on_scopes: z.array(z.enum(SCOPES)).default(['runtime']),
|
||||
allow_licenses: z.array(z.string()).optional(),
|
||||
deny_licenses: z.array(z.string()).optional(),
|
||||
allow_dependencies_licenses: z.array(z.string()).optional(),
|
||||
allow_ghsas: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(z.string()).default([]),
|
||||
deny_groups: z.array(z.string()).default([]),
|
||||
license_check: z.boolean().default(true),
|
||||
vulnerability_check: z.boolean().default(true),
|
||||
config_file: z.string().optional(),
|
||||
base_ref: z.string().optional(),
|
||||
head_ref: z.string().optional(),
|
||||
comment_summary_in_pr: z.boolean().default(false),
|
||||
retry_on_snapshot_warnings: z.boolean().default(false),
|
||||
retry_on_snapshot_warnings_timeout: z.number().default(120),
|
||||
comment_summary_in_pr: z
|
||||
.union([
|
||||
z.preprocess(
|
||||
val => (val === 'true' ? true : val === 'false' ? false : val),
|
||||
z.boolean()
|
||||
),
|
||||
z.enum(['always', 'never', 'on-failure'])
|
||||
])
|
||||
.default('never'),
|
||||
warn_only: z.boolean().default(false)
|
||||
})
|
||||
.transform(config => {
|
||||
if (config.comment_summary_in_pr === true) {
|
||||
config.comment_summary_in_pr = 'always'
|
||||
} else if (config.comment_summary_in_pr === false) {
|
||||
config.comment_summary_in_pr = 'never'
|
||||
}
|
||||
return config
|
||||
})
|
||||
.superRefine((config, context) => {
|
||||
if (config.allow_licenses && config.deny_licenses) {
|
||||
context.addIssue({
|
||||
@@ -91,6 +104,5 @@ export type Change = z.infer<typeof ChangeSchema>
|
||||
export type Changes = z.infer<typeof ChangesSchema>
|
||||
export type ComparisonResponse = z.infer<typeof ComparisonResponseSchema>
|
||||
export type ConfigurationOptions = z.infer<typeof ConfigurationOptionsSchema>
|
||||
export type FailOnSeverity = z.infer<typeof FailOnSeveritySchema>
|
||||
export type Severity = z.infer<typeof SeveritySchema>
|
||||
export type Scope = (typeof SCOPES)[number]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as core from '@actions/core'
|
||||
import {ConfigurationOptions, Changes} from './schemas'
|
||||
import {ConfigurationOptions, Changes, Change} from './schemas'
|
||||
import {SummaryTableRow} from '@actions/core/lib/summary'
|
||||
import {InvalidLicenseChanges, InvalidLicenseChangeTypes} from './licenses'
|
||||
import {groupDependenciesByManifest, getManifestsSet, renderUrl} from './utils'
|
||||
@@ -13,13 +13,15 @@ const icons = {
|
||||
export function addSummaryToSummary(
|
||||
vulnerableChanges: Changes,
|
||||
invalidLicenseChanges: InvalidLicenseChanges,
|
||||
deniedChanges: Changes,
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.summary.addHeading('Dependency Review', 1)
|
||||
|
||||
if (
|
||||
vulnerableChanges.length === 0 &&
|
||||
countLicenseIssues(invalidLicenseChanges) === 0
|
||||
countLicenseIssues(invalidLicenseChanges) === 0 &&
|
||||
deniedChanges.length === 0
|
||||
) {
|
||||
if (!config.license_check) {
|
||||
core.summary.addRaw(`${icons.check} No vulnerabilities found.`)
|
||||
@@ -56,6 +58,13 @@ export function addSummaryToSummary(
|
||||
invalidLicenseChanges.unlicensed.length
|
||||
} package(s) with unknown licenses.`
|
||||
]
|
||||
: []),
|
||||
...(deniedChanges.length > 0
|
||||
? [
|
||||
`${checkOrWarnIcon(deniedChanges.length)} ${
|
||||
deniedChanges.length
|
||||
} package(s) denied.`
|
||||
]
|
||||
: [])
|
||||
])
|
||||
.addRaw('See the Details below.')
|
||||
@@ -222,21 +231,34 @@ export function addScannedDependencies(changes: Changes): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function addSnapshotWarnings(warnings: string): void {
|
||||
// For now, we want to ignore warnings that just complain
|
||||
// about missing snapshots on the head SHA. This is a product
|
||||
// decision to avoid presenting warnings to users who simply
|
||||
// don't use snapshots.
|
||||
const ignore_regex = new RegExp(/No.*snapshot.*found.*head.*/, 'i')
|
||||
if (ignore_regex.test(warnings)) {
|
||||
return
|
||||
function snapshotWarningRecommendation(
|
||||
config: ConfigurationOptions,
|
||||
warnings: string
|
||||
): string {
|
||||
const no_pr_snaps = warnings.includes(
|
||||
'No snapshots were found for the head SHA'
|
||||
)
|
||||
const retries_disabled = !config.retry_on_snapshot_warnings
|
||||
if (no_pr_snaps && retries_disabled) {
|
||||
return 'Ensure that dependencies are being submitted on PR branches and consider enabling <em>retry-on-snapshot-warnings</em>.'
|
||||
} else if (no_pr_snaps) {
|
||||
return 'Ensure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue.'
|
||||
} else if (retries_disabled) {
|
||||
return 'Consider enabling <em>retry-on-snapshot-warnings</em>.'
|
||||
}
|
||||
return 'Re-running this action after a short time may resolve the issue.'
|
||||
}
|
||||
|
||||
export function addSnapshotWarnings(
|
||||
config: ConfigurationOptions,
|
||||
warnings: string
|
||||
): void {
|
||||
core.summary.addHeading('Snapshot Warnings', 2)
|
||||
core.summary.addQuote(`${icons.warning}: ${warnings}`)
|
||||
core.summary.addRaw(
|
||||
'Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice.'
|
||||
)
|
||||
const recommendation = snapshotWarningRecommendation(config, warnings)
|
||||
const docsLink =
|
||||
'See <a href="https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#best-practices-for-using-the-dependency-review-api-and-the-dependency-submission-api-together">the documentation</a> for more information and troubleshooting advice.'
|
||||
core.summary.addRaw(`${recommendation} ${docsLink}`)
|
||||
}
|
||||
|
||||
function countLicenseIssues(
|
||||
@@ -248,6 +270,25 @@ function countLicenseIssues(
|
||||
)
|
||||
}
|
||||
|
||||
export function addDeniedToSummary(deniedChanges: Change[]): void {
|
||||
if (deniedChanges.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
core.summary.addHeading('Denied dependencies', 2)
|
||||
for (const change of deniedChanges) {
|
||||
core.summary.addHeading(`<em>Denied dependencies</em>`, 4)
|
||||
core.summary.addTable([
|
||||
['Package', 'Version', 'License'],
|
||||
[
|
||||
renderUrl(change.source_repository_url, change.name),
|
||||
change.version,
|
||||
change.license || ''
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
function checkOrFailIcon(count: number): string {
|
||||
return count === 0 ? icons.check : icons.cross
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user