import {expect, jest, test} from '@jest/globals' import {Changes, ConfigurationOptions} 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' afterEach(() => { jest.clearAllMocks() core.summary.emptyBuffer() }) const emptyChanges: Changes = [] const emptyInvalidLicenseChanges = { forbidden: [], unresolved: [], unlicensed: [] } const defaultConfig: ConfigurationOptions = { vulnerability_check: true, license_check: true, fail_on_severity: 'high', fail_on_scopes: ['runtime'], allow_ghsas: [], allow_licenses: [], deny_licenses: [], comment_summary_in_pr: true } const changesWithEmptyManifests: Changes = [ { change_type: 'added', manifest: '', ecosystem: 'unknown', name: 'castore', version: '0.1.17', package_url: 'pkg:hex/castore@0.1.17', license: null, source_repository_url: null, scope: 'runtime', vulnerabilities: [] }, { change_type: 'added', manifest: '', ecosystem: 'unknown', name: 'connection', version: '1.1.0', package_url: 'pkg:hex/connection@1.1.0', license: null, source_repository_url: null, scope: 'runtime', vulnerabilities: [] }, { change_type: 'added', manifest: 'python/dist-info/METADATA', ecosystem: 'pip', name: 'pygments', version: '2.6.1', package_url: 'pkg:pypi/pygments@2.6.1', license: 'BSD-2-Clause', source_repository_url: 'https://github.com/pygments/pygments', scope: 'runtime', vulnerabilities: [] } ] test('prints headline as h1', () => { summary.addSummaryToSummary( emptyChanges, emptyInvalidLicenseChanges, defaultConfig ) const text = core.summary.stringify() expect(text).toContain('

Dependency Review

') }) test('only includes "No vulnerabilities or license issues found"-message if both are configured and nothing was found', () => { summary.addSummaryToSummary( emptyChanges, emptyInvalidLicenseChanges, defaultConfig ) const text = core.summary.stringify() expect(text).toContain('✅ No vulnerabilities or license issues found.') }) test('only includes "No vulnerabilities found"-message if "license_check" is set to false and nothing was found', () => { const config = {...defaultConfig, license_check: false} summary.addSummaryToSummary(emptyChanges, emptyInvalidLicenseChanges, config) const text = core.summary.stringify() expect(text).toContain('✅ No vulnerabilities found.') }) test('only includes "No license issues found"-message if "vulnerability_check" is set to false and nothing was found', () => { const config = {...defaultConfig, vulnerability_check: false} summary.addSummaryToSummary(emptyChanges, emptyInvalidLicenseChanges, config) const text = core.summary.stringify() expect(text).toContain('✅ No license issues found.') }) test('groups dependencies with empty manifest paths together', () => { summary.addSummaryToSummary( changesWithEmptyManifests, emptyInvalidLicenseChanges, defaultConfig ) summary.addScannedDependencies(changesWithEmptyManifests) const text = core.summary.stringify() expect(text).toContain('Unnamed Manifest') expect(text).toContain('castore') expect(text).toContain('connection') expect(text).toContain('python/dist-info/METADATA') expect(text).toContain('pygments') }) test('does not include status section if nothing was found', () => { summary.addSummaryToSummary( emptyChanges, emptyInvalidLicenseChanges, defaultConfig ) const text = core.summary.stringify() expect(text).not.toContain('The following issues were found:') }) test('includes count and status icons for all findings', () => { const vulnerabilities = [ createTestChange({name: 'lodash'}), createTestChange({name: 'underscore', package_url: 'test-url'}) ] const licenseIssues = { forbidden: [createTestChange()], unresolved: [createTestChange(), createTestChange()], unlicensed: [createTestChange(), createTestChange(), createTestChange()] } summary.addSummaryToSummary(vulnerabilities, licenseIssues, defaultConfig) const text = core.summary.stringify() expect(text).toContain('❌ 2 vulnerable package(s)') expect(text).toContain( '❌ 2 package(s) with invalid SPDX license definitions' ) expect(text).toContain('❌ 1 package(s) with incompatible licenses') expect(text).toContain('⚠️ 3 package(s) with unknown licenses') }) test('uses checkmarks for license issues if only vulnerabilities were found', () => { const vulnerabilities = [createTestChange()] summary.addSummaryToSummary( vulnerabilities, emptyInvalidLicenseChanges, defaultConfig ) const text = core.summary.stringify() expect(text).toContain('❌ 1 vulnerable package(s)') expect(text).toContain( '✅ 0 package(s) with invalid SPDX license definitions' ) expect(text).toContain('✅ 0 package(s) with incompatible licenses') expect(text).toContain('✅ 0 package(s) with unknown licenses') }) test('uses checkmarks for vulnerabilities if only license issues were found', () => { const licenseIssues = { forbidden: [createTestChange()], unresolved: [], unlicensed: [] } summary.addSummaryToSummary(emptyChanges, licenseIssues, defaultConfig) const text = core.summary.stringify() expect(text).toContain('✅ 0 vulnerable package(s)') expect(text).toContain( '✅ 0 package(s) with invalid SPDX license definitions' ) expect(text).toContain('❌ 1 package(s) with incompatible licenses') expect(text).toContain('✅ 0 package(s) with unknown licenses') }) test('addChangeVulnerabilitiesToSummary() - only includes section if any vulnerabilites found', () => { summary.addChangeVulnerabilitiesToSummary(emptyChanges, 'low') const text = core.summary.stringify() expect(text).toEqual('') }) test('addChangeVulnerabilitiesToSummary() - includes all vulnerabilities', () => { const changes = [ createTestChange({name: 'lodash'}), createTestChange({name: 'underscore', package_url: 'test-url'}) ] summary.addChangeVulnerabilitiesToSummary(changes, 'low') const text = core.summary.stringify() expect(text).toContain('

Vulnerabilities

') expect(text).toContain('lodash') expect(text).toContain('underscore') }) test('addChangeVulnerabilitiesToSummary() - includes advisory url if available', () => { const changes = [ createTestChange({ name: 'underscore', vulnerabilities: [ createTestVulnerability({ advisory_summary: 'test-summary', advisory_url: 'test-url' }) ] }) ] summary.addChangeVulnerabilitiesToSummary(changes, 'low') const text = core.summary.stringify() expect(text).toContain('lodash') expect(text).toContain('test-summary') }) test('addChangeVulnerabilitiesToSummary() - groups vulnerabilities of a single package', () => { const changes = [ createTestChange({ name: 'package-with-multiple-vulnerabilities', vulnerabilities: [ createTestVulnerability({advisory_summary: 'test-summary-1'}), createTestVulnerability({advisory_summary: 'test-summary-2'}) ] }) ] 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', () => { const changes = [createTestChange()] summary.addChangeVulnerabilitiesToSummary(changes, 'medium') const text = core.summary.stringify() expect(text).toContain( 'Only included vulnerabilities with severity medium or higher.' ) }) test('addChangeVulnerabilitiesToSummary() - does not print severity statment if it is set to "low"', () => { const changes = [createTestChange()] summary.addChangeVulnerabilitiesToSummary(changes, 'low') const text = core.summary.stringify() expect(text).not.toContain('Only included vulnerabilities') }) test('addLicensesToSummary() - does not include entire section if no license issues found', () => { summary.addLicensesToSummary(emptyInvalidLicenseChanges, defaultConfig) const text = core.summary.stringify() expect(text).toEqual('') }) test('addLicensesToSummary() - includes all license issues in table', () => { const licenseIssues = { forbidden: [createTestChange()], unresolved: [createTestChange(), createTestChange()], unlicensed: [createTestChange(), createTestChange(), createTestChange()] } summary.addLicensesToSummary(licenseIssues, defaultConfig) const text = core.summary.stringify() expect(text).toContain('

License Issues

') expect(text).toContain('Incompatible License') expect(text).toContain('Invalid SPDX License') expect(text).toContain('Unknown License') }) test('addLicenseToSummary() - adds one table per manifest', () => { const licenseIssues = { forbidden: [ createTestChange({manifest: 'package.json'}), createTestChange({manifest: '.github/workflows/test.yml'}) ], unresolved: [], unlicensed: [] } summary.addLicensesToSummary(licenseIssues, defaultConfig) const text = core.summary.stringify() expect(text).toContain('

package.json

') expect(text).toContain('

.github/workflows/test.yml

') }) test('addLicensesToSummary() - does not include specific license type sub-section if nothing is found', () => { const licenseIssues = { forbidden: [], unlicensed: [], unresolved: [createTestChange()] } summary.addLicensesToSummary(licenseIssues, defaultConfig) const text = core.summary.stringify() expect(text).not.toContain('Incompatible License') expect(text).not.toContain('Unknown License') expect(text).toContain('Invalid SPDX License') }) test('addLicensesToSummary() - includes list of configured allowed licenses', () => { const licenseIssues = { forbidden: [createTestChange()], unresolved: [], unlicensed: [] } const config: ConfigurationOptions = { ...defaultConfig, allow_licenses: ['MIT', 'Apache-2.0'] } summary.addLicensesToSummary(licenseIssues, config) const text = core.summary.stringify() expect(text).toContain('Allowed Licenses: MIT, Apache-2.0') }) test('addLicensesToSummary() - includes configured denied license', () => { const licenseIssues = { forbidden: [createTestChange()], unresolved: [], unlicensed: [] } const config: ConfigurationOptions = { ...defaultConfig, deny_licenses: ['MIT'] } summary.addLicensesToSummary(licenseIssues, config) const text = core.summary.stringify() expect(text).toContain('Denied Licenses: MIT') })