Files
dependency-review-action/__tests__/licenses.test.ts
T

267 lines
7.4 KiB
TypeScript
Raw Normal View History

2022-10-13 11:57:38 +00:00
import {expect, jest, test} from '@jest/globals'
2022-06-06 17:07:26 +02:00
import {Change, Changes} from '../src/schemas'
2024-06-05 22:57:06 -07:00
import {getInvalidLicenseChanges} from '../src/licenses'
2022-06-06 17:07:26 +02:00
const npmChange: Change = {
2022-06-06 17:07:26 +02:00
manifest: 'package.json',
change_type: 'added',
ecosystem: 'npm',
name: 'Reeuhq',
version: '1.0.2',
2022-06-13 20:19:01 +02:00
package_url: 'pkg:npm/[email protected]',
2022-06-06 17:07:26 +02:00
license: 'MIT',
source_repository_url: 'github.com/some-repo',
2022-09-15 17:53:34 +00:00
scope: 'runtime',
2022-06-06 17:07:26 +02:00
vulnerabilities: [
{
severity: 'critical',
advisory_ghsa_id: 'first-random_string',
2022-07-05 18:25:21 +09:00
advisory_summary: 'very dangerous',
2022-06-06 17:07:26 +02:00
advisory_url: 'github.com/future-funk'
}
]
}
const rubyChange: Change = {
2022-06-06 17:07:26 +02:00
change_type: 'added',
manifest: 'Gemfile.lock',
ecosystem: 'rubygems',
name: 'actionsomething',
version: '3.2.0',
2022-06-13 20:19:01 +02:00
package_url: 'pkg:gem/[email protected]',
2024-06-05 22:57:06 -07:00
license: 'BSD-3-Clause',
2022-06-06 17:07:26 +02:00
source_repository_url: 'github.com/some-repo',
2022-09-15 17:53:34 +00:00
scope: 'runtime',
2022-06-06 17:07:26 +02:00
vulnerabilities: [
{
severity: 'moderate',
advisory_ghsa_id: 'second-random_string',
2022-07-05 18:25:21 +09:00
advisory_summary: 'not so dangerous',
2022-06-06 17:07:26 +02:00
advisory_url: 'github.com/future-funk'
},
{
severity: 'low',
advisory_ghsa_id: 'third-random_string',
advisory_summary: 'dont page me',
advisory_url: 'github.com/future-funk'
}
]
}
2023-05-19 10:47:59 +02:00
const pipChange: Change = {
change_type: 'added',
manifest: 'requirements.txt',
ecosystem: 'pip',
name: 'package-1',
version: '1.1.1',
2023-12-11 17:23:19 +01:00
package_url: 'pkg:pypi/[email protected]',
2023-05-19 10:47:59 +02:00
license: 'MIT',
source_repository_url: 'github.com/some-repo',
scope: 'runtime',
vulnerabilities: [
{
severity: 'moderate',
advisory_ghsa_id: 'second-random_string',
advisory_summary: 'not so dangerous',
advisory_url: 'github.com/future-funk'
},
{
severity: 'low',
advisory_ghsa_id: 'third-random_string',
advisory_summary: 'dont page me',
advisory_url: 'github.com/future-funk'
}
]
}
2022-10-13 11:57:38 +00:00
jest.mock('@actions/core')
const mockOctokit = {
rest: {
licenses: {
getForRepo: jest
.fn()
.mockReturnValue({data: {license: {spdx_id: 'AGPL'}}})
}
}
}
jest.mock('octokit', () => {
return {
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
2022-10-13 11:57:38 +00:00
Octokit: class {
constructor() {
return mockOctokit
}
}
}
})
2022-10-27 13:09:37 +00:00
beforeEach(async () => {
jest.resetModules()
2022-06-06 17:07:26 +02:00
})
2022-06-06 18:06:00 +02:00
2022-10-27 13:09:37 +00:00
test('it adds license outside the allow list to forbidden changes', async () => {
2024-06-05 22:57:06 -07:00
const changes: Changes = [
npmChange, // MIT license
rubyChange // BSD license
]
2022-10-27 13:09:37 +00:00
const {forbidden} = await getInvalidLicenseChanges(changes, {
2024-06-05 22:57:06 -07:00
allow: ['BSD-3-Clause']
2022-10-27 13:09:37 +00:00
})
2024-06-05 22:57:06 -07:00
2022-10-27 13:09:37 +00:00
expect(forbidden[0]).toBe(npmChange)
expect(forbidden.length).toEqual(1)
})
test('it adds license inside the deny list to forbidden changes', async () => {
2024-06-05 22:57:06 -07:00
const changes: Changes = [
npmChange, // MIT license
rubyChange // BSD license
]
2022-10-27 13:09:37 +00:00
const {forbidden} = await getInvalidLicenseChanges(changes, {
2024-06-05 22:57:06 -07:00
deny: ['BSD-3-Clause']
2022-10-13 11:06:40 +00:00
})
2024-06-05 22:57:06 -07:00
2022-10-27 13:09:37 +00:00
expect(forbidden[0]).toBe(rubyChange)
expect(forbidden.length).toEqual(1)
2022-06-06 18:06:00 +02:00
})
2022-10-27 13:09:37 +00:00
test('it does not add license outside the allow list to forbidden changes if it is in removed changes', async () => {
2022-07-13 18:11:10 +09:00
const changes: Changes = [
{...npmChange, change_type: 'removed'},
{...rubyChange, change_type: 'removed'}
]
2022-10-27 13:09:37 +00:00
const {forbidden} = await getInvalidLicenseChanges(changes, {
2024-06-05 22:57:06 -07:00
allow: ['BSD-3-Clause']
2022-10-13 11:06:40 +00:00
})
2022-10-27 13:09:37 +00:00
expect(forbidden).toStrictEqual([])
2022-07-13 18:11:10 +09:00
})
2022-10-27 13:09:37 +00:00
test('it does not add license inside the deny list to forbidden changes if it is in removed changes', async () => {
2022-07-13 18:11:10 +09:00
const changes: Changes = [
{...npmChange, change_type: 'removed'},
{...rubyChange, change_type: 'removed'}
]
2022-10-27 13:09:37 +00:00
const {forbidden} = await getInvalidLicenseChanges(changes, {
2024-06-05 22:57:06 -07:00
deny: ['BSD-3-Clause']
2022-10-13 11:06:40 +00:00
})
2022-10-27 13:09:37 +00:00
expect(forbidden).toStrictEqual([])
2022-07-13 18:11:10 +09:00
})
2022-07-13 19:07:12 +09:00
2022-10-27 13:09:37 +00:00
test('it adds license outside the allow list to forbidden changes if it is in both added and removed changes', async () => {
2022-07-13 19:07:12 +09:00
const changes: Changes = [
{...npmChange, change_type: 'removed'},
npmChange,
{...rubyChange, change_type: 'removed'}
]
2022-10-27 13:09:37 +00:00
const {forbidden} = await getInvalidLicenseChanges(changes, {
2024-06-05 22:57:06 -07:00
allow: ['BSD-3-Clause']
2022-10-13 11:06:40 +00:00
})
2022-10-27 13:09:37 +00:00
expect(forbidden).toStrictEqual([npmChange])
})
test('it adds all licenses to unresolved if it is unable to determine the validity', async () => {
2024-06-05 22:57:06 -07:00
const changes: Changes = [
{...npmChange, license: 'Foo'},
{...rubyChange, license: 'Bar'}
]
2022-10-27 13:09:37 +00:00
const invalidLicenses = await getInvalidLicenseChanges(changes, {
2024-06-05 22:57:06 -07:00
allow: ['Apache-2.0']
2022-10-27 13:09:37 +00:00
})
expect(invalidLicenses.forbidden.length).toEqual(0)
expect(invalidLicenses.unlicensed.length).toEqual(0)
expect(invalidLicenses.unresolved.length).toEqual(2)
2022-07-13 19:07:12 +09:00
})
2022-10-13 11:57:38 +00:00
2023-05-19 10:47:59 +02:00
test('it does not filter out changes that are on the exclusions list', async () => {
const changes: Changes = [pipChange, npmChange, rubyChange]
const licensesConfig = {
2024-06-05 22:57:06 -07:00
allow: ['BSD-3-Clause'],
2023-12-11 17:23:19 +01:00
licenseExclusions: ['pkg:pypi/[email protected]', 'pkg:npm/[email protected]']
2023-05-19 10:47:59 +02:00
}
const invalidLicenses = await getInvalidLicenseChanges(
changes,
licensesConfig
)
expect(invalidLicenses.forbidden.length).toEqual(0)
})
2023-05-31 16:24:19 +02:00
test('it does not fail when the packages dont have a valid PURL', async () => {
const emptyPurlChange = pipChange
emptyPurlChange.package_url = ''
const changes: Changes = [emptyPurlChange, npmChange, rubyChange]
const licensesConfig = {
2024-06-05 22:57:06 -07:00
allow: ['BSD-3-Clause'],
2023-12-11 17:23:19 +01:00
licenseExclusions: ['pkg:pypi/[email protected]', 'pkg:npm/[email protected]']
2023-05-31 16:24:19 +02:00
}
const invalidLicenses = await getInvalidLicenseChanges(
changes,
licensesConfig
)
expect(invalidLicenses.forbidden.length).toEqual(1)
})
2023-05-19 10:47:59 +02:00
test('it does filters out changes if they are not on the exclusions list', async () => {
const changes: Changes = [pipChange, npmChange, rubyChange]
const licensesConfig = {
2024-06-05 22:57:06 -07:00
allow: ['BSD-3-Clause'],
2023-12-11 17:23:19 +01:00
licenseExclusions: [
'pkg:pypi/[email protected]',
'pkg:npm/[email protected]'
]
2023-05-19 10:47:59 +02:00
}
2024-06-05 22:57:06 -07:00
2023-05-19 10:47:59 +02:00
const invalidLicenses = await getInvalidLicenseChanges(
changes,
licensesConfig
)
2024-06-05 22:57:06 -07:00
2023-05-19 10:47:59 +02:00
expect(invalidLicenses.forbidden.length).toEqual(2)
expect(invalidLicenses.forbidden[0]).toBe(pipChange)
expect(invalidLicenses.forbidden[1]).toBe(npmChange)
})
2022-10-13 11:57:38 +00:00
describe('GH License API fallback', () => {
test('it calls licenses endpoint if atleast one of the changes has null license and valid source_repository_url', async () => {
const nullLicenseChange = {
...npmChange,
license: null,
source_repository_url: 'http://github.com/some-owner/some-repo'
}
2022-10-27 13:09:37 +00:00
const {unlicensed} = await getInvalidLicenseChanges(
2022-10-13 11:57:38 +00:00
[nullLicenseChange, rubyChange],
{}
)
expect(mockOctokit.rest.licenses.getForRepo).toHaveBeenNthCalledWith(1, {
owner: 'some-owner',
repo: 'some-repo'
})
2022-10-27 13:09:37 +00:00
expect(unlicensed.length).toEqual(0)
2022-10-13 11:57:38 +00:00
})
test('it does not call licenses API endpoint for change with null license and invalid source_repository_url ', async () => {
2022-10-27 13:09:37 +00:00
const {unlicensed} = await getInvalidLicenseChanges(
2022-10-13 11:57:38 +00:00
[{...npmChange, license: null}],
{}
)
expect(mockOctokit.rest.licenses.getForRepo).not.toHaveBeenCalled()
2022-10-27 13:09:37 +00:00
expect(unlicensed.length).toEqual(1)
2022-10-13 11:57:38 +00:00
})
test('it does not call licenses API endpoint if licenses for all changes are present', async () => {
2022-10-27 13:09:37 +00:00
const {unlicensed} = await getInvalidLicenseChanges(
2022-10-13 11:57:38 +00:00
[npmChange, rubyChange],
{}
)
expect(mockOctokit.rest.licenses.getForRepo).not.toHaveBeenCalled()
2022-10-27 13:09:37 +00:00
expect(unlicensed.length).toEqual(0)
2022-10-13 11:57:38 +00:00
})
})