Skip to content

Commit

Permalink
SCP-139 Adds undeclared policy report
Browse files Browse the repository at this point in the history
  • Loading branch information
francostramana committed Feb 27, 2024
1 parent 47f1eed commit 33cd66b
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 36 deletions.
12 changes: 0 additions & 12 deletions __tests__/result-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,3 @@ describe('Test Results service', () => {
});
}
});

describe('Test components service', () => {
const t = licenseTableTest[3];
it(`test c`, () => {
const scannerResults = JSON.parse(t.content) as ScannerResults;
const components = getComponents(scannerResults);

const componentsWithCopyleft = components.filter(component =>
component.licenses.some(license => !!license.copyleft)
);
});
});
64 changes: 55 additions & 9 deletions dist/index.js

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

6 changes: 3 additions & 3 deletions src/policies/copyleft-policy-check.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ScannerResults } from '../services/result.interfaces';
import { CHECK_NAME } from '../app.config';
import { PolicyCheck } from './policy-check';
import { Component, getComponents, getLicenses } from '../services/result.service';
import { Component, getComponents } from '../services/result.service';
import { generateTable } from '../utils/markdown.utils';

export class CopyleftPolicyCheck extends PolicyCheck {
Expand Down Expand Up @@ -34,8 +34,8 @@ export class CopyleftPolicyCheck extends PolicyCheck {
: `### :x: Policy Fail \n #### ${components.length} component(s) with copyleft licenses were found. \n See details for more information.`;
}

private getDetails(components: Component[]): string {
if (components.length === 0) return '';
private getDetails(components: Component[]): string | undefined {
if (components.length === 0) return undefined;

const headers = ['Component', 'Version', 'License', 'URL', 'Copyleft'];
const rows: string[][] = [];
Expand Down
6 changes: 3 additions & 3 deletions src/policies/policy-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,18 @@ export abstract class PolicyCheck {
this._status = STATUS.RUNNING;
}

protected async success(summary: string, text: string): Promise<void> {
protected async success(summary: string, text?: string): Promise<void> {
this._conclusion = CONCLUSION.Success;
return await this.finish(summary, text);
}

protected async reject(summary: string, text: string): Promise<void> {
protected async reject(summary: string, text?: string): Promise<void> {
if (inputs.POLICIES_HALT_ON_FAILURE) this._conclusion = CONCLUSION.Failure;
else this._conclusion = CONCLUSION.Neutral;
return await this.finish(summary, text);
}

protected async finish(summary: string, text: string): Promise<void> {
protected async finish(summary: string, text?: string): Promise<void> {
core.debug(`Finish policy check: ${this.checkName}. (conclusion=${this._conclusion})`);
this._status = STATUS.FINISHED;

Expand Down
45 changes: 39 additions & 6 deletions src/policies/undeclared-policy-check.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { PolicyCheck } from './policy-check';
import { CHECK_NAME } from '../app.config';
import { ScannerResults } from '../services/result.interfaces';
import { getComponents } from '../services/result.service';
import { Component, getComponents } from '../services/result.service';
import * as inputs from '../app.input';
import { parseSbom } from '../utils/sbom.utils';
import { generateTable } from '../utils/markdown.utils';

export class UndeclaredPolicyCheck extends PolicyCheck {
constructor() {
Expand All @@ -13,21 +14,53 @@ export class UndeclaredPolicyCheck extends PolicyCheck {
async run(scannerResults: ScannerResults): Promise<void> {
super.run(scannerResults);

const nonDeclaredComponents = new Set<string>();
const nonDeclaredComponents: Component[] = [];

const comps = getComponents(scannerResults);
const sbom = await parseSbom(inputs.SBOM_FILEPATH);

comps.forEach(c => {
if (!sbom.components.some(component => component.purl === c.purl)) {
nonDeclaredComponents.add(c.purl);
nonDeclaredComponents.push(c);
}
});

if (!nonDeclaredComponents.size) {
this.success('Completed succesfully', 'Undeclared components were not found');
const summary = this.getSummary(nonDeclaredComponents);
const details = this.getDetails(nonDeclaredComponents);

if (nonDeclaredComponents.length === 0) {
this.success(summary, details);
} else {
this.reject('Failure', `Undeclared components were found: ${JSON.stringify(Array.from(nonDeclaredComponents))}`);
this.reject(summary, details);
}
}

private getSummary(components: Component[]): string {
return components.length === 0
? '### :white_check_mark: Policy Pass \n #### Not undeclared components were found'
: `### :x: Policy Fail \n #### ${components.length} undeclared component(s) were found. \n See details for more information.`;
}

private getDetails(components: Component[]): string | undefined {
if (components.length === 0) return undefined;

const headers = ['Component', 'Version', 'License'];
const rows: string[][] = [];

components.forEach(component => {
const licenses = component.licenses.map(l => l.spdxid).join(' - ');
rows.push([component.purl, component.version, licenses]);
});

const snippet = JSON.stringify(
components.map(({ purl }) => ({ purl })),
null,
4
);

let content = `### Undeclared components \n ${generateTable(headers, rows)}`;
content += `#### Add the following snippet into your \`sbom.json\` file \n \`\`\`json \n ${snippet} \n \`\`\``;

return content;
}
}
31 changes: 28 additions & 3 deletions src/services/result.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,41 @@ export function getComponents(results: ScannerResults): Component[] {
components.push({
purl: d.purl,
version: d.version,
licenses: d.licenses.map(l => ({ spdxid: l.spdx_id, copyleft: null, url: null, count: 1 }))
licenses: d.licenses
.map(l => ({ spdxid: l.spdx_id, copyleft: null, url: null, count: 1 }))
.filter(l => l.spdxid)
});
}
}
}
}

//Merge duplicates purls
// Merge duplicates
const componentMap = new Map<string, Component>();
components.forEach((component: Component) => {
const key = `${component.purl}-${component.version}`;
const existingComponent = componentMap.get(key);
if (existingComponent) {
const licenses = [...existingComponent.licenses, ...component.licenses];
} else {
componentMap.set(key, component);
}

// Remove duplicates licenses
const spdxidSet = new Set<string>();
const uniqueLicenses: License[] = [];
component.licenses.forEach(license => {
if (!spdxidSet.has(license.spdxid)) {
spdxidSet.add(license.spdxid);
uniqueLicenses.push(license);
}
});
component.licenses = uniqueLicenses;
});

const unqiqueComponents = [...componentMap.values()];

return components;
return unqiqueComponents;
}

export function getLicenses(results: ScannerResults): License[] {
Expand Down

0 comments on commit 33cd66b

Please sign in to comment.