Skip to content

Commit

Permalink
Improved accuracy of license expression parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
rhyskoedijk committed Dec 18, 2024
1 parent c6f8ab4 commit 91dcd21
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 28 deletions.
25 changes: 15 additions & 10 deletions shared/models/spdx/2.3/ILicense.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import * as spdxLicenseList from './licenses.json';
import * as spdxLicenseList from 'spdx-license-list';

export interface ILicense {
reference: string;
isDeprecatedLicenseId: boolean;
detailsUrl: string;
referenceNumber: number;
id: string;
name: string;
licenseId: string;
seeAlso: string[];
isOsiApproved: boolean;
isFsfLibre?: boolean;
url: string;
}

/**
Expand All @@ -19,5 +13,16 @@ export interface ILicense {
* @returns The licenses
*/
export function getLicensesFromExpression(licenseExpression: string): ILicense[] | undefined {
return spdxLicenseList.licenses.filter((x: { licenseId: string }) => licenseExpression.includes(x.licenseId));
return Object.keys(spdxLicenseList)
.filter((id) =>
licenseExpression
.split(/\s+/)
.filter((word) => word.length > 0)
.includes(id),
)
.map((id) => ({
id,
name: spdxLicenseList[id].name,
url: spdxLicenseList[id].url,
}));
}
5 changes: 5 additions & 0 deletions shared/models/spdx/2.3/IPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export function getPackageLicenseExpression(pkg: IPackage): string | undefined {
return undefined;
}

export function getPackageLicenseReferences(pkg: IPackage): string[] {
const expression = getPackageLicenseExpression(pkg) || '';
return expression.split(/\s+/).filter((word) => word.length > 0);
}

export function getPackageSupplierOrganization(pkg: IPackage): string | undefined {
if (pkg.supplier === NOASSERTION) {
return undefined;
Expand Down
43 changes: 41 additions & 2 deletions shared/package-lock.json

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

7 changes: 5 additions & 2 deletions shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
"buffer": "^6.0.3",
"json-as-xlsx": "^2.5.6",
"packageurl-js": "^2.0.1",
"semver": "^7.6.3"
"semver": "^7.6.3",
"spdx-expression-parse": "^4.0.0",
"spdx-license-list": "^6.9.0"
},
"devDependencies": {
"@types/semver": "^7.5.8"
"@types/semver": "^7.5.8",
"@types/spdx-expression-parse": "^3.0.5"
}
}
19 changes: 12 additions & 7 deletions shared/spdx/convertSpdxToXlsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import {
} from '../models/spdx/2.3/IExternalRef';
import { IFile } from '../models/spdx/2.3/IFile';
import { getLicensesFromExpression, ILicense } from '../models/spdx/2.3/ILicense';
import { getPackageLicenseExpression, getPackageSupplierOrganization, IPackage } from '../models/spdx/2.3/IPackage';
import {
getPackageLicenseExpression,
getPackageLicenseReferences,
getPackageSupplierOrganization,
IPackage,
} from '../models/spdx/2.3/IPackage';
import { IRelationship } from '../models/spdx/2.3/IRelationship';
import { parseSpdxSecurityAdvisoriesLegacy } from './parseSpdxSecurityAdvisoriesLegacy';

Expand Down Expand Up @@ -57,7 +62,7 @@ export async function convertSpdxToXlsxAsync(spdx: IDocument): Promise<Buffer> {
.filter((licenseExpression): licenseExpression is string => !!licenseExpression)
.flatMap((licenseExpression: string) => getLicensesFromExpression(licenseExpression))
.filter((license): license is ILicense => !!license)
.distinctBy((license: ILicense) => license.licenseId);
.distinctBy((license: ILicense) => license.id);
const suppliers = packages
.map((pkg) => getPackageSupplierOrganization(pkg))
.filter((supplier): supplier is string => !!supplier)
Expand Down Expand Up @@ -254,20 +259,20 @@ export async function convertSpdxToXlsxAsync(spdx: IDocument): Promise<Buffer> {
{ label: 'Risk Reason', value: 'riskReasons' },
],
content: licenses
.orderBy((license: ILicense) => license.licenseId)
.orderBy((license: ILicense) => license.id)
.map((license: ILicense) => {
const packagesWithLicense = packages
.filter((p) => getPackageLicenseExpression(p)?.includes(license.licenseId))
.filter((p) => getPackageLicenseReferences(p).includes(license.id))
.map((p) => `${p.name || ''}@${p.versionInfo || ''}`)
.distinct();
const licenseRisk = getLicenseRiskAssessment(license.licenseId);
const licenseRisk = getLicenseRiskAssessment(license.id);
return {
id: license.licenseId,
id: license.id,
name: license.name,
packages: packagesWithLicense.length,
riskSeverity: (licenseRisk?.severity || LicenseRiskSeverity.Low).toPascalCase(),
riskReasons: licenseRisk?.reasons?.join('; ') || '',
url: license.reference,
url: license.url || '',
};
}),
};
Expand Down
2 changes: 1 addition & 1 deletion ui/components/sbom/SbomDocumentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class SbomDocumentPage extends React.Component<Props, State> {
.filter((licenseExpression): licenseExpression is string => !!licenseExpression)
.flatMap((licenseExpression: string) => getLicensesFromExpression(licenseExpression))
.filter((license): license is ILicense => !!license)
.distinctBy((license: ILicense) => license.licenseId);
.distinctBy((license: ILicense) => license.id);
const suppliers = packages
.map((pkg) => getPackageSupplierOrganization(pkg))
.filter((supplier): supplier is string => !!supplier)
Expand Down
12 changes: 6 additions & 6 deletions ui/components/sbom/SpdxLicenseTableCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { getSeverityByName } from '../../../shared/models/severity/Severities';
import { IDocument } from '../../../shared/models/spdx/2.3/IDocument';
import { getExternalRefPackageManagerUrl } from '../../../shared/models/spdx/2.3/IExternalRef';
import { ILicense } from '../../../shared/models/spdx/2.3/ILicense';
import { getPackageLicenseExpression } from '../../../shared/models/spdx/2.3/IPackage';
import { getPackageLicenseReferences } from '../../../shared/models/spdx/2.3/IPackage';

const MAX_PACKAGES_VISIBLE = 3;

Expand Down Expand Up @@ -77,27 +77,27 @@ export class SpdxLicenseTableCard extends React.Component<Props, State> {
);
const rawTableItems: ILicenseTableItem[] =
props.licenses
?.orderBy((license: ILicense) => license.licenseId)
?.orderBy((license: ILicense) => license.id)
?.map((license: ILicense) => {
const packagesWithLicense = props.document.packages
?.filter((p) => getPackageLicenseExpression(p)?.includes(license.licenseId))
?.filter((p) => getPackageLicenseReferences(p)?.includes(license.id))
?.map((p) => {
return {
name: p.name || '',
version: p.versionInfo || '',
url: getExternalRefPackageManagerUrl(p.externalRefs),
};
});
const licenseRisk = getLicenseRiskAssessment(license.licenseId);
const licenseRisk = getLicenseRiskAssessment(license.id);
return {
id: license.licenseId,
id: license.id,
name: license.name,
packagesTotal: packagesWithLicense.length,
packagesVisible: new ObservableValue<number>(MAX_PACKAGES_VISIBLE),
packages: packagesWithLicense,
riskSeverity: getSeverityByName(licenseRisk?.severity || LicenseRiskSeverity.Low),
riskReasons: licenseRisk?.reasons || [],
url: license.reference,
url: license.url || '',
};
}) || [];

Expand Down

0 comments on commit 91dcd21

Please sign in to comment.