Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
157 changes: 144 additions & 13 deletions __tests__/summary.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import {expect, jest, test} from '@jest/globals'
import {expect, jest, test, beforeEach} from '@jest/globals'
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'
import {createTestVulnerability} from './fixtures/create-test-vulnerability'
import * as utils from '../src/utils'

const mockOctokitRequest = jest.fn<any>()

beforeEach(() => {
jest.spyOn(utils, 'octokitClient').mockReturnValue({
request: mockOctokitRequest
} as any)

mockOctokitRequest.mockResolvedValue({
data: {vulnerabilities: []}
})
})

afterEach(() => {
jest.clearAllMocks()
Expand Down Expand Up @@ -315,27 +328,27 @@ test('uses checkmarks for vulnerabilities if only license issues were found', ()
expect(text).toContain('✅ 0 package(s) with unknown licenses')
})

test('addChangeVulnerabilitiesToSummary() - only includes section if any vulnerabilities found', () => {
summary.addChangeVulnerabilitiesToSummary(emptyChanges, 'low')
test('addChangeVulnerabilitiesToSummary() - only includes section if any vulnerabilities found', async () => {
await summary.addChangeVulnerabilitiesToSummary(emptyChanges, 'low')
const text = core.summary.stringify()
expect(text).toEqual('')
})

test('addChangeVulnerabilitiesToSummary() - includes all vulnerabilities', () => {
test('addChangeVulnerabilitiesToSummary() - includes all vulnerabilities', async () => {
const changes = [
createTestChange({name: 'lodash'}),
createTestChange({name: 'underscore', package_url: 'test-url'})
]

summary.addChangeVulnerabilitiesToSummary(changes, 'low')
await summary.addChangeVulnerabilitiesToSummary(changes, 'low')

const text = core.summary.stringify()
expect(text).toContain('<h2>Vulnerabilities</h2>')
expect(text).toContain('lodash')
expect(text).toContain('underscore')
})

test('addChangeVulnerabilitiesToSummary() - includes advisory url if available', () => {
test('addChangeVulnerabilitiesToSummary() - includes advisory url if available', async () => {
const changes = [
createTestChange({
name: 'underscore',
Expand All @@ -348,14 +361,14 @@ test('addChangeVulnerabilitiesToSummary() - includes advisory url if available',
})
]

summary.addChangeVulnerabilitiesToSummary(changes, 'low')
await summary.addChangeVulnerabilitiesToSummary(changes, 'low')

const text = core.summary.stringify()
expect(text).toContain('lodash')
expect(text).toContain('<a href="test-url">test-summary</a>')
})

test('addChangeVulnerabilitiesToSummary() - groups vulnerabilities of a single package', () => {
test('addChangeVulnerabilitiesToSummary() - groups vulnerabilities of a single package', async () => {
const changes = [
createTestChange({
name: 'package-with-multiple-vulnerabilities',
Expand All @@ -366,34 +379,43 @@ test('addChangeVulnerabilitiesToSummary() - groups vulnerabilities of a single p
})
]

summary.addChangeVulnerabilitiesToSummary(changes, 'low')
await summary.addChangeVulnerabilitiesToSummary(changes, 'low')

const text = core.summary.stringify()
expect(text.match('package-with-multiple-vulnerabilities')).toHaveLength(1)
expect(text).toContain('test-summary-1')
expect(text).toContain('test-summary-2')
})

test('addChangeVulnerabilitiesToSummary() - prints severity statement if above low', () => {
test('addChangeVulnerabilitiesToSummary() - prints severity statement if above low', async () => {
const changes = [createTestChange()]

summary.addChangeVulnerabilitiesToSummary(changes, 'medium')
await summary.addChangeVulnerabilitiesToSummary(changes, 'medium')

const text = core.summary.stringify()
expect(text).toContain(
'Only included vulnerabilities with severity <strong>medium</strong> or higher.'
)
})

test('addChangeVulnerabilitiesToSummary() - does not print severity statement if it is set to "low"', () => {
test('addChangeVulnerabilitiesToSummary() - does not print severity statement if it is set to "low"', async () => {
const changes = [createTestChange()]

summary.addChangeVulnerabilitiesToSummary(changes, 'low')
await summary.addChangeVulnerabilitiesToSummary(changes, 'low')

const text = core.summary.stringify()
expect(text).not.toContain('Only included vulnerabilities')
})

test('addChangeVulnerabilitiesToSummary() - includes patched version column', async () => {
const changes = [createTestChange()]

await summary.addChangeVulnerabilitiesToSummary(changes, 'low')

const text = core.summary.stringify()
expect(text).toContain('Patched Version')
})

test('addLicensesToSummary() - does not include entire section if no license issues found', () => {
summary.addLicensesToSummary(emptyInvalidLicenseChanges, defaultConfig)
const text = core.summary.stringify()
Expand Down Expand Up @@ -508,3 +530,112 @@ test('addLicensesToSummary() - includes allowed dependency licences', () => {
'<details><summary><strong>Excluded from license check</strong>:</summary> MIT, Apache-2.0</details>'
)
})

test('addChangeVulnerabilitiesToSummary() - handles multiple version ranges for same package', async () => {
// Simulates GHSA-gwq6-fmvp-qp68 scenario with multiple version ranges
const pkg8 = createTestChange({
ecosystem: 'nuget',
name: 'Microsoft.NetCore.App.Runtime.linux-arm',
version: '8.0.1',
vulnerabilities: [
createTestVulnerability({
advisory_ghsa_id: 'GHSA-test-multi',
advisory_summary: 'Test Multi-Range Advisory',
severity: 'high'
})
]
})

const pkg9 = createTestChange({
ecosystem: 'nuget',
name: 'Microsoft.NetCore.App.Runtime.linux-arm',
version: '9.0.1',
vulnerabilities: [
createTestVulnerability({
advisory_ghsa_id: 'GHSA-test-multi',
advisory_summary: 'Test Multi-Range Advisory',
severity: 'high'
})
]
})

// Mock API response with multiple version ranges for same package
mockOctokitRequest.mockResolvedValueOnce({
data: {
vulnerabilities: [
{
package: {
ecosystem: 'NuGet',
name: 'Microsoft.NetCore.App.Runtime.linux-arm'
},
vulnerable_version_range: '>= 8.0.0, <= 8.0.20',
first_patched_version: '8.0.21'
},
{
package: {
ecosystem: 'NuGet',
name: 'Microsoft.NetCore.App.Runtime.linux-arm'
},
vulnerable_version_range: '>= 9.0.0, <= 9.0.9',
first_patched_version: '9.0.10'
}
]
}
})

const changes = [pkg8, pkg9]
await summary.addChangeVulnerabilitiesToSummary(changes, 'low')

const text = core.summary.stringify()

// Both packages should have correct patched versions based on their version ranges
expect(text).toContain('8.0.21')
expect(text).toContain('9.0.10')
expect(mockOctokitRequest).toHaveBeenCalledWith('GET /advisories/{ghsa_id}', {
ghsa_id: 'GHSA-test-multi'
})
})

test('addChangeVulnerabilitiesToSummary() - handles RestSharp GHSA-4rr6-2v9v-wcpc case', async () => {
const pkg = createTestChange({
ecosystem: 'nuget',
name: 'RestSharp',
version: '111.4.1',
vulnerabilities: [
createTestVulnerability({
advisory_ghsa_id: 'GHSA-4rr6-2v9v-wcpc',
advisory_summary:
"CRLF Injection in RestSharp's `RestRequest.AddHeader` method",
severity: 'moderate'
})
]
})

// Mock API response matching actual GitHub Advisory Database response
mockOctokitRequest.mockResolvedValueOnce({
data: {
vulnerabilities: [
{
package: {
ecosystem: 'nuget',
name: 'RestSharp'
},
vulnerable_version_range: '>= 107.0.0-preview.1, < 112.0.0',
first_patched_version: '112.0.0'
}
]
}
})

const changes = [pkg]
await summary.addChangeVulnerabilitiesToSummary(changes, 'low')

const text = core.summary.stringify()

// Should show the correct patched version
expect(text).toContain('112.0.0')
expect(text).not.toContain('N/A')
expect(mockOctokitRequest).toHaveBeenCalledWith('GET /advisories/{ghsa_id}', {
ghsa_id: 'GHSA-4rr6-2v9v-wcpc'
})
})
Loading