Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ Dependency review action can create [outputs](https://docs.github.com/en/actions
- `vulnerable-changes` holds information about dependency changes with vulnerable dependencies in a JSON format.
- `invalid-license-changes` holds information about invalid or non-compliant license dependency changes in a JSON format.
- `denied-changes` holds information about denied dependency changes in a JSON format.
- `resolved-vulnerabilities` holds information about vulnerabilities that have been resolved by removing or upgrading packages in a JSON format.

> [!NOTE]
> 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).
Expand Down
122 changes: 122 additions & 0 deletions __tests__/resolved-vulnerabilities.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {test, expect} from '@jest/globals'
import {getResolvedVulnerabilities} from '../src/resolved-vulnerabilities'
import {Changes, Change} from '../src/schemas'

const vulnerableRemovedChange: Change = {
change_type: 'removed',
manifest: 'package.json',
ecosystem: 'npm',
name: 'lodash',
version: '4.17.20',
package_url: 'pkg:npm/lodash@4.17.20',
license: 'MIT',
source_repository_url: 'https://github.com/lodash/lodash',
scope: 'runtime',
vulnerabilities: [
{
severity: 'high',
advisory_ghsa_id: 'GHSA-35jh-r3h4-6jhm',
advisory_summary: 'lodash Prototype Pollution vulnerability',
advisory_url: 'https://github.com/advisories/GHSA-35jh-r3h4-6jhm'
},
{
severity: 'critical',
advisory_ghsa_id: 'GHSA-p6mc-m468-83gw',
advisory_summary: 'lodash Command Injection via template',
advisory_url: 'https://github.com/advisories/GHSA-p6mc-m468-83gw'
}
]
}

const nonVulnerableRemovedChange: Change = {
change_type: 'removed',
manifest: 'package.json',
ecosystem: 'npm',
name: 'express',
version: '4.18.0',
package_url: 'pkg:npm/express@4.18.0',
license: 'MIT',
source_repository_url: 'https://github.com/expressjs/express',
scope: 'runtime',
vulnerabilities: []
}

const addedChange: Change = {
change_type: 'added',
manifest: 'package.json',
ecosystem: 'npm',
name: 'react',
version: '18.0.0',
package_url: 'pkg:npm/react@18.0.0',
license: 'MIT',
source_repository_url: 'https://github.com/facebook/react',
scope: 'runtime',
vulnerabilities: [
{
severity: 'moderate',
advisory_ghsa_id: 'GHSA-test-1234',
advisory_summary: 'Test vulnerability',
advisory_url: 'https://github.com/advisories/GHSA-test-1234'
}
]
}

test('extracts resolved vulnerabilities from removed packages', () => {
const changes: Changes = [
vulnerableRemovedChange,
nonVulnerableRemovedChange,
addedChange
]

const resolvedVulns = getResolvedVulnerabilities(changes)

expect(resolvedVulns).toHaveLength(2)

expect(resolvedVulns[0]).toEqual({
severity: 'high',
advisory_ghsa_id: 'GHSA-35jh-r3h4-6jhm',
advisory_summary: 'lodash Prototype Pollution vulnerability',
advisory_url: 'https://github.com/advisories/GHSA-35jh-r3h4-6jhm',
package_name: 'lodash',
package_version: '4.17.20',
package_url: 'pkg:npm/lodash@4.17.20',
manifest: 'package.json',
ecosystem: 'npm'
})

expect(resolvedVulns[1]).toEqual({
severity: 'critical',
advisory_ghsa_id: 'GHSA-p6mc-m468-83gw',
advisory_summary: 'lodash Command Injection via template',
advisory_url: 'https://github.com/advisories/GHSA-p6mc-m468-83gw',
package_name: 'lodash',
package_version: '4.17.20',
package_url: 'pkg:npm/lodash@4.17.20',
manifest: 'package.json',
ecosystem: 'npm'
})
})

test('returns empty array when no removed packages have vulnerabilities', () => {
const changes: Changes = [nonVulnerableRemovedChange, addedChange]

const resolvedVulns = getResolvedVulnerabilities(changes)

expect(resolvedVulns).toHaveLength(0)
})

test('ignores added packages with vulnerabilities', () => {
const changes: Changes = [addedChange]

const resolvedVulns = getResolvedVulnerabilities(changes)

expect(resolvedVulns).toHaveLength(0)
})

test('handles empty changes array', () => {
const changes: Changes = []

const resolvedVulns = getResolvedVulnerabilities(changes)

expect(resolvedVulns).toHaveLength(0)
})
15 changes: 14 additions & 1 deletion __tests__/summary.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expect, jest, test} from '@jest/globals'
import {Changes, ConfigurationOptions, Scorecard} from '../src/schemas'
import {Changes, ConfigurationOptions, Scorecard, ResolvedVulnerabilities} from '../src/schemas'
import * as summary from '../src/summary'
import * as core from '@actions/core'
import {createTestChange} from './fixtures/create-test-change'
Expand All @@ -19,6 +19,7 @@ const emptyInvalidLicenseChanges = {
const emptyScorecard: Scorecard = {
dependencies: []
}
const emptyResolvedVulnerabilities: ResolvedVulnerabilities = []
const defaultConfig: ConfigurationOptions = {
vulnerability_check: true,
license_check: true,
Expand Down Expand Up @@ -102,6 +103,7 @@ test('prints headline as h1', () => {
emptyInvalidLicenseChanges,
emptyChanges,
scorecard,
emptyResolvedVulnerabilities,
defaultConfig
)
const text = core.summary.stringify()
Expand All @@ -115,6 +117,7 @@ test('does not add deprecation warning for deny-licenses option if not set', ()
emptyInvalidLicenseChanges,
emptyChanges,
scorecard,
emptyResolvedVulnerabilities,
defaultConfig
)
const text = core.summary.stringify()
Expand All @@ -130,6 +133,7 @@ test('adds deprecation warning for deny-licenses option if set', () => {
emptyInvalidLicenseChanges,
emptyChanges,
scorecard,
emptyResolvedVulnerabilities,
config
)
const text = core.summary.stringify()
Expand All @@ -155,6 +159,7 @@ test('returns minimal summary formatted for posting as a PR comment', () => {
emptyInvalidLicenseChanges,
emptyChanges,
scorecard,
emptyResolvedVulnerabilities,
defaultConfig
)

Expand All @@ -181,6 +186,7 @@ test('only includes "No vulnerabilities or license issues found"-message if both
emptyInvalidLicenseChanges,
emptyChanges,
emptyScorecard,
emptyResolvedVulnerabilities,
defaultConfig
)
const text = core.summary.stringify()
Expand All @@ -195,6 +201,7 @@ test('only includes "No vulnerabilities found"-message if "license_check" is set
emptyInvalidLicenseChanges,
emptyChanges,
emptyScorecard,
emptyResolvedVulnerabilities,
config
)
const text = core.summary.stringify()
Expand All @@ -209,6 +216,7 @@ test('only includes "No license issues found"-message if "vulnerability_check" i
emptyInvalidLicenseChanges,
emptyChanges,
emptyScorecard,
emptyResolvedVulnerabilities,
config
)
const text = core.summary.stringify()
Expand All @@ -222,6 +230,7 @@ test('groups dependencies with empty manifest paths together', () => {
emptyInvalidLicenseChanges,
emptyChanges,
emptyScorecard,
emptyResolvedVulnerabilities,
defaultConfig
)
summary.addScannedFiles(changesWithEmptyManifests)
Expand All @@ -236,6 +245,7 @@ test('does not include status section if nothing was found', () => {
emptyInvalidLicenseChanges,
emptyChanges,
emptyScorecard,
emptyResolvedVulnerabilities,
defaultConfig
)
const text = core.summary.stringify()
Expand All @@ -259,6 +269,7 @@ test('includes count and status icons for all findings', () => {
licenseIssues,
emptyChanges,
emptyScorecard,
emptyResolvedVulnerabilities,
defaultConfig
)

Expand All @@ -279,6 +290,7 @@ test('uses checkmarks for license issues if only vulnerabilities were found', ()
emptyInvalidLicenseChanges,
emptyChanges,
emptyScorecard,
emptyResolvedVulnerabilities,
defaultConfig
)

Expand All @@ -303,6 +315,7 @@ test('uses checkmarks for vulnerabilities if only license issues were found', ()
licenseIssues,
emptyChanges,
emptyScorecard,
emptyResolvedVulnerabilities,
defaultConfig
)

Expand Down
Loading