Add unresolved licenses section
This commit is contained in:
+1
-5
@@ -26,10 +26,6 @@ function parseList(list: string | undefined): string[] | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
function getInvalidLicenses(licenses: string[]): string[] {
|
||||
return licenses.filter(license => !isSPDXValid(license))
|
||||
}
|
||||
|
||||
function validateLicenses(
|
||||
key: licenseKey,
|
||||
licenses: string[] | undefined
|
||||
@@ -37,7 +33,7 @@ function validateLicenses(
|
||||
if (licenses === undefined) {
|
||||
return
|
||||
}
|
||||
const invalid_licenses = getInvalidLicenses(licenses)
|
||||
const invalid_licenses = licenses.filter(license => !isSPDXValid(license))
|
||||
|
||||
if (invalid_licenses.length > 0) {
|
||||
throw new Error(
|
||||
|
||||
+18
-16
@@ -15,32 +15,39 @@ import {isSPDXValid} from './utils'
|
||||
* we will ignore the deny list.
|
||||
* @param {Change[]} changes The list of changes to filter.
|
||||
* @param { { allow?: string[], deny?: string[]}} licenses An object with `allow`/`deny` keys, each containing a list of licenses.
|
||||
* @returns {Promise<[Array.<Change>, Array.<Change>]>} A promise to a 2 element tuple. The first element is the list of denied changes and the second one is the list of changes with unknown licenses
|
||||
* @returns {Promise<{Object.<string, Array.<Change>>}} A promise to a Record Object. The keys are strings, unlicensed, unresolved and forbidden. The values are a list of changes
|
||||
*/
|
||||
export async function getDeniedLicenseChanges(
|
||||
export async function getInvalidLicenseChanges(
|
||||
changes: Change[],
|
||||
licenses: {
|
||||
allow?: string[]
|
||||
deny?: string[]
|
||||
}
|
||||
): Promise<[Change[], Change[]]> {
|
||||
): Promise<Record<string, Changes>> {
|
||||
const {allow, deny} = licenses
|
||||
|
||||
const groupedChanges = await groupChanges(changes)
|
||||
const unlicensedChanges: Changes = groupedChanges.unlicensed
|
||||
const licensedChanges: Changes = groupedChanges.licensed
|
||||
|
||||
const forbiddenLicenseChanges: Changes = []
|
||||
const invalidLicenseChanges: Record<string, Changes> = {
|
||||
unlicensed: groupedChanges.unlicensed,
|
||||
unresolved: [],
|
||||
forbidden: []
|
||||
}
|
||||
|
||||
const validityCache = new Map<string, boolean>()
|
||||
|
||||
for (const change of licensedChanges) {
|
||||
// should never happen since licensedChanges have licenses. Look into Intersection Types
|
||||
const license = change.license
|
||||
|
||||
// should never happen since licensedChanges always have licenses but license is nullable in changes schema
|
||||
if (license === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (validityCache.get(license) === undefined) {
|
||||
if (license === 'NOASSERTION') {
|
||||
invalidLicenseChanges.unlicensed.push(change)
|
||||
} else if (validityCache.get(license) === undefined) {
|
||||
try {
|
||||
if (allow !== undefined) {
|
||||
const found = allow.find(spdxExpression =>
|
||||
@@ -53,22 +60,17 @@ export async function getDeniedLicenseChanges(
|
||||
)
|
||||
validityCache.set(license, found === undefined)
|
||||
}
|
||||
} catch (_) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Invalid spdx license ${license} for ${change.name}`)
|
||||
} catch (err) {
|
||||
invalidLicenseChanges.unresolved.push(change)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Verify spdxSatisfies is working as expected as currently:
|
||||
// spdxSatisfies("MIT", "MIT AND (GPL-2.0 OR ISC)") => true
|
||||
// spdxSatisfies("MIT AND (GPL-2.0 OR ISC)", "MIT") => false
|
||||
|
||||
if (validityCache.get(license) === false) {
|
||||
forbiddenLicenseChanges.push(change)
|
||||
invalidLicenseChanges.forbidden.push(change)
|
||||
}
|
||||
}
|
||||
|
||||
return [forbiddenLicenseChanges, unlicensedChanges]
|
||||
return invalidLicenseChanges
|
||||
}
|
||||
|
||||
const fetchGHLicense = async (
|
||||
|
||||
+22
-18
@@ -10,7 +10,7 @@ import {
|
||||
filterChangesByScopes,
|
||||
filterAllowedAdvisories
|
||||
} from '../src/filter'
|
||||
import {getDeniedLicenseChanges} from './licenses'
|
||||
import {getInvalidLicenseChanges} from './licenses'
|
||||
import * as summary from './summary'
|
||||
import {getRefs} from './git-refs'
|
||||
|
||||
@@ -45,7 +45,7 @@ async function run(): Promise<void> {
|
||||
change.vulnerabilities.length > 0
|
||||
)
|
||||
|
||||
const [licenseErrors, unknownLicenses] = await getDeniedLicenseChanges(
|
||||
const invalidLicenseChanges = await getInvalidLicenseChanges(
|
||||
filteredChanges,
|
||||
{
|
||||
allow: config.allow_licenses,
|
||||
@@ -53,13 +53,13 @@ async function run(): Promise<void> {
|
||||
}
|
||||
)
|
||||
|
||||
summary.addSummaryToSummary(addedChanges, licenseErrors, unknownLicenses)
|
||||
summary.addSummaryToSummary(addedChanges, invalidLicenseChanges)
|
||||
summary.addChangeVulnerabilitiesToSummary(addedChanges, minSeverity)
|
||||
summary.addLicensesToSummary(licenseErrors, unknownLicenses, config)
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config)
|
||||
summary.addScannedDependencies(changes)
|
||||
|
||||
printVulnerabilitiesBlock(addedChanges, minSeverity)
|
||||
printLicensesBlock(licenseErrors, unknownLicenses)
|
||||
printLicensesBlock(invalidLicenseChanges)
|
||||
printScannedDependencies(changes)
|
||||
} catch (error) {
|
||||
if (error instanceof RequestError && error.status === 404) {
|
||||
@@ -83,7 +83,7 @@ async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
function printVulnerabilitiesBlock(
|
||||
addedChanges: Change[],
|
||||
addedChanges: Changes,
|
||||
minSeverity: Severity
|
||||
): void {
|
||||
let failed = false
|
||||
@@ -119,24 +119,28 @@ function printChangeVulnerabilities(change: Change): void {
|
||||
}
|
||||
|
||||
function printLicensesBlock(
|
||||
licenseErrors: Change[],
|
||||
unknownLicenses: Change[]
|
||||
invalidLicenseChanges: Record<string, Changes>
|
||||
): void {
|
||||
core.group('Licenses', async () => {
|
||||
if (licenseErrors.length > 0) {
|
||||
printLicensesError(licenseErrors)
|
||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
||||
core.info('\nThe following dependencies have incompatible licenses:\n')
|
||||
printLicensesError(invalidLicenseChanges.forbidden)
|
||||
core.setFailed('Dependency review detected incompatible licenses.')
|
||||
}
|
||||
printNullLicenses(unknownLicenses)
|
||||
if (invalidLicenseChanges.unresolved.length > 0) {
|
||||
core.warning(
|
||||
'\nThe validity of the licenses of the dependecies below could not be determine. Ensure that they are valid spdx licenses:\n'
|
||||
)
|
||||
printLicensesError(invalidLicenseChanges.unresolved)
|
||||
core.setFailed(
|
||||
'Dependency review could not detect the validity of all licenses.'
|
||||
)
|
||||
}
|
||||
printNullLicenses(invalidLicenseChanges.unlicensed)
|
||||
})
|
||||
}
|
||||
|
||||
function printLicensesError(changes: Change[]): void {
|
||||
if (changes.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
core.info('\nThe following dependencies have incompatible licenses:\n')
|
||||
function printLicensesError(changes: Changes): void {
|
||||
for (const change of changes) {
|
||||
core.info(
|
||||
`${styles.bold.open}${change.manifest} » ${change.name}@${change.version}${styles.bold.close} – License: ${styles.color.red.open}${change.license}${styles.color.red.close}`
|
||||
@@ -144,7 +148,7 @@ function printLicensesError(changes: Change[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
function printNullLicenses(changes: Change[]): void {
|
||||
function printNullLicenses(changes: Changes): void {
|
||||
if (changes.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
+23
-18
@@ -1,18 +1,19 @@
|
||||
import * as core from '@actions/core'
|
||||
import {ConfigurationOptions, Change, Changes} from './schemas'
|
||||
import {ConfigurationOptions, Changes} from './schemas'
|
||||
import {SummaryTableRow} from '@actions/core/lib/summary'
|
||||
import {groupDependenciesByManifest, getManifestsSet, renderUrl} from './utils'
|
||||
|
||||
export function addSummaryToSummary(
|
||||
addedPackages: Changes,
|
||||
licenseErrors: Change[],
|
||||
unknownLicenses: Change[]
|
||||
invalidLicenseChanges: Record<string, Changes>
|
||||
): void {
|
||||
core.summary
|
||||
.addHeading('Dependency Review')
|
||||
.addRaw(
|
||||
`We found ${addedPackages.length} vulnerable package(s), ${licenseErrors.length} package(s) with incompatible licenses, and ${unknownLicenses.length} package(s) with unknown licenses.`
|
||||
)
|
||||
core.summary.addHeading('Dependency Review').addRaw(
|
||||
`We found:
|
||||
- ${addedPackages.length} vulnerable package(s),
|
||||
- ${invalidLicenseChanges.unresolved.length} package(s) with unprocessable licenses,
|
||||
- ${invalidLicenseChanges.forbidden.length} package(s) with incompatible licenses, and
|
||||
- ${invalidLicenseChanges.unlicensed.length} package(s) with unknown licenses.`
|
||||
)
|
||||
}
|
||||
|
||||
export function addChangeVulnerabilitiesToSummary(
|
||||
@@ -76,8 +77,7 @@ export function addChangeVulnerabilitiesToSummary(
|
||||
}
|
||||
|
||||
export function addLicensesToSummary(
|
||||
licenseErrors: Change[],
|
||||
unknownLicenses: Change[],
|
||||
invalidLicenseChanges: Record<string, Changes>,
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.summary.addHeading('Licenses')
|
||||
@@ -93,21 +93,24 @@ export function addLicensesToSummary(
|
||||
)
|
||||
}
|
||||
|
||||
if (licenseErrors.length === 0 && unknownLicenses.length === 0) {
|
||||
if (
|
||||
invalidLicenseChanges.forbidden.length === 0 &&
|
||||
invalidLicenseChanges.unlicensed.length === 0
|
||||
) {
|
||||
core.summary.addQuote('No license violations detected.')
|
||||
return
|
||||
}
|
||||
|
||||
if (licenseErrors.length > 0) {
|
||||
if (invalidLicenseChanges.forbidden.length > 0) {
|
||||
const rows: SummaryTableRow[] = []
|
||||
const manifests = getManifestsSet(licenseErrors)
|
||||
const manifests = getManifestsSet(invalidLicenseChanges.forbidden)
|
||||
|
||||
core.summary.addHeading('Incompatible Licenses', 3).addSeparator()
|
||||
|
||||
for (const manifest of manifests) {
|
||||
core.summary.addHeading(`<em>${manifest}</em>`, 4)
|
||||
|
||||
for (const change of licenseErrors.filter(
|
||||
for (const change of invalidLicenseChanges.forbidden.filter(
|
||||
pkg => pkg.manifest === manifest
|
||||
)) {
|
||||
rows.push([
|
||||
@@ -122,11 +125,13 @@ export function addLicensesToSummary(
|
||||
core.summary.addQuote('No license violations detected.')
|
||||
}
|
||||
|
||||
core.debug(`found ${unknownLicenses.length} unknown licenses`)
|
||||
core.debug(
|
||||
`found ${invalidLicenseChanges.unlicensed.length} unknown licenses`
|
||||
)
|
||||
|
||||
if (unknownLicenses.length > 0) {
|
||||
if (invalidLicenseChanges.unlicensed.length > 0) {
|
||||
const rows: SummaryTableRow[] = []
|
||||
const manifests = getManifestsSet(unknownLicenses)
|
||||
const manifests = getManifestsSet(invalidLicenseChanges.unlicensed)
|
||||
|
||||
core.debug(
|
||||
`found ${manifests.entries.length} manifests for unknown licenses`
|
||||
@@ -137,7 +142,7 @@ export function addLicensesToSummary(
|
||||
for (const manifest of manifests) {
|
||||
core.summary.addHeading(`<em>${manifest}</em>`, 4)
|
||||
|
||||
for (const change of unknownLicenses.filter(
|
||||
for (const change of invalidLicenseChanges.unlicensed.filter(
|
||||
pkg => pkg.manifest === manifest
|
||||
)) {
|
||||
rows.push([
|
||||
|
||||
Reference in New Issue
Block a user