Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ The SCANOSS scan engine supports [scan tuning parameters](https://github.com/sca
The SCANOSS Code Scan Action includes three configurable policies:

1. **Copyleft** (`copyleft or cpl`): This policy checks if any component or code snippet is associated with a copyleft license. If such a
license is detected, the pull request (PR) is rejected. The default list of Copyleft licenses is defined in the following [file](https://github.com/scanoss/gha-code-scan/blob/main/src/utils/license.utils.ts).
license is detected, the pull request (PR) is rejected. The copyleft determination is performed by [scanoss.py](https://github.com/scanoss/scanoss.py) (`scanoss-py inspect copyleft`), which uses the [OSADL copyleft checklist](https://www.osadl.org/fileadmin/checklists/copyleft.json) as the default list. Both strong (`Yes`) and weak/restricted (`Yes (restricted)`) copyleft entries are treated as copyleft, and this includes `-or-later` variants (e.g. `GPL-2.0-or-later`, `AGPL-3.0-or-later`). The default list can be customized with the `licenses.copyleft.include`, `licenses.copyleft.exclude`, and `licenses.copyleft.explicit` inputs.

2. **Undeclared** (`undeclared or und`): This policy compares the components detected in the repository against those declared in scanoss.json
file (customizable through the settingsFilepath parameter). If there are undeclared components, the PR is rejected.
Expand Down
84 changes: 1 addition & 83 deletions __tests__/license.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,91 +23,9 @@

import { LicenseUtil } from '../src/utils/license.utils';

// Mock external dependencies
jest.mock('@actions/core');

describe('License Utils', () => {
beforeEach(() => {
jest.clearAllMocks();
// Clear module cache to ensure fresh imports
jest.resetModules();
});

describe('LicenseUtil class initialization', () => {
it('should use explicit licenses when COPYLEFT_LICENSE_EXPLICIT is set', () => {
jest.doMock('../src/app.input', () => ({
COPYLEFT_LICENSE_EXPLICIT: 'GPL-2.0-only,LGPL-3.0-only',
COPYLEFT_LICENSE_INCLUDE: '',
COPYLEFT_LICENSE_EXCLUDE: ''
}));

const { LicenseUtil: MockedLicenseUtil } = require('../src/utils/license.utils');
const util = new MockedLicenseUtil();

// Test that the utility is instantiated correctly with explicit configuration
expect(util).toBeDefined();
expect(util).toBeInstanceOf(MockedLicenseUtil);
});

it('should use default licenses with include when COPYLEFT_LICENSE_INCLUDE is set', () => {
jest.doMock('../src/app.input', () => ({
COPYLEFT_LICENSE_EXPLICIT: '',
COPYLEFT_LICENSE_INCLUDE: 'Custom-License-1.0',
COPYLEFT_LICENSE_EXCLUDE: ''
}));

const { LicenseUtil: MockedLicenseUtil } = require('../src/utils/license.utils');
const util = new MockedLicenseUtil();

// Test that the utility is instantiated correctly with include configuration
expect(util).toBeDefined();
expect(util).toBeInstanceOf(MockedLicenseUtil);
});

it('should use default licenses with exclude when COPYLEFT_LICENSE_EXCLUDE is set', () => {
jest.doMock('../src/app.input', () => ({
COPYLEFT_LICENSE_EXPLICIT: '',
COPYLEFT_LICENSE_INCLUDE: '',
COPYLEFT_LICENSE_EXCLUDE: 'GPL-2.0-only'
}));

const { LicenseUtil: MockedLicenseUtil } = require('../src/utils/license.utils');
const util = new MockedLicenseUtil();

// Test that the utility is instantiated correctly with exclude configuration
expect(util).toBeDefined();
expect(util).toBeInstanceOf(MockedLicenseUtil);
});

it('should be instantiated correctly with default configuration', () => {
jest.doMock('../src/app.input', () => ({
COPYLEFT_LICENSE_EXPLICIT: '',
COPYLEFT_LICENSE_INCLUDE: '',
COPYLEFT_LICENSE_EXCLUDE: ''
}));

const { LicenseUtil: MockedLicenseUtil } = require('../src/utils/license.utils');
const util = new MockedLicenseUtil();

// Test that the utility is instantiated correctly with default configuration
expect(util).toBeDefined();
expect(util).toBeInstanceOf(MockedLicenseUtil);
});
});

describe('LicenseUtil.getOSADL', () => {
let util: LicenseUtil;

beforeEach(() => {
// Create fresh instance with default configuration for each test
jest.doMock('../src/app.input', () => ({
COPYLEFT_LICENSE_EXPLICIT: '',
COPYLEFT_LICENSE_INCLUDE: '',
COPYLEFT_LICENSE_EXCLUDE: ''
}));
const { LicenseUtil: MockedLicenseUtil } = require('../src/utils/license.utils');
util = new MockedLicenseUtil();
});
const util = new LicenseUtil();

it('should return SPDX URL for valid SPDX licenses', () => {
const result = util.getOSADL('MIT');
Expand Down
55 changes: 0 additions & 55 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 5 additions & 65 deletions src/utils/license.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,76 +21,16 @@
THE SOFTWARE.
*/

import * as inputs from '../app.input';
import * as core from '@actions/core';

/**
* Utility class for handling license operations and copyleft license detection.
* Utility class for handling license operations.
*
* Note: copyleft determination is NOT performed here. It is delegated to
* scanoss.py (`scanoss-py inspect copyleft`), which uses the OSADL copyleft
* checklist. This class only builds SPDX license reference URLs for reporting.
*/
export class LicenseUtil {
private BASE_OSADL_URL = 'https://spdx.org/licenses';
private HTML = 'html';
/**
* Initializes the license utility with copyleft license configurations.
*/
constructor() {
this.init();
}

private defaultCopyleftLicenses = new Set<string>(
[
'GPL-1.0-only',
'GPL-2.0-only',
'GPL-3.0-only',
'AGPL-3.0-only',
'Sleepycat',
'Watcom-1.0',
'GFDL-1.1-only',
'GFDL-1.2-only',
'GFDL-1.3-only',
'LGPL-2.1-only',
'LGPL-3.0-only',
'MPL-1.1',
'MPL-2.0',
'EPL-1.0',
'EPL-2.0',
'CDDL-1.0',
'CDDL-1.1',
'CECILL-2.1',
'Artistic-1.0',
'Artistic-2.0',
'CC-BY-SA-4.0'
].map(l => l.toLowerCase())
);

private copyLeftLicenses = new Set<string>();

/**
* Initializes copyleft license sets based on configuration.
*/
private init(): void {
if (inputs.COPYLEFT_LICENSE_EXPLICIT) {
const explicitCopyleftLicenses = inputs.COPYLEFT_LICENSE_EXPLICIT.split(',').map(pn => pn.trim().toLowerCase());
core.debug(`Explicit licenses: ${explicitCopyleftLicenses}`);
this.copyLeftLicenses = new Set<string>(explicitCopyleftLicenses);
return;
}

core.debug(`Explicit licenses not defined, setting default licenses...`);
this.copyLeftLicenses = this.defaultCopyleftLicenses;

if (inputs.COPYLEFT_LICENSE_INCLUDE) {
const includedCopyleftLicenses = inputs.COPYLEFT_LICENSE_INCLUDE.split(',').map(pn => pn.trim());
core.debug(`Included copyleft licenses: ${includedCopyleftLicenses}`);
includedCopyleftLicenses.forEach(l => this.copyLeftLicenses.add(l.toLowerCase()));
}

if (inputs.COPYLEFT_LICENSE_EXCLUDE) {
const excludedCopyleftLicenses = inputs.COPYLEFT_LICENSE_EXCLUDE.split(',').map(pn => pn.trim());
core.debug(`Excluded copyleft licenses: ${excludedCopyleftLicenses}`);
excludedCopyleftLicenses.forEach(l => this.copyLeftLicenses.delete(l.toLowerCase()));
}
}

/**
* Generates SPDX license URL for the given license identifier.
Expand Down
Loading