Skip to content

Commit 99a1525

Browse files
authored
Merge pull request #619 from nexB/fix/missing-license-expressions_spdx
Fixed compund SPDX expression resolution in detection & clue matches
2 parents 51c96eb + e07a060 commit 99a1525

File tree

10 files changed

+500
-222
lines changed

10 files changed

+500
-222
lines changed

src/components/LicenseEntity/LicenseEntity.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
} from "./FileRegionTableCols";
1010
import { ScanOptionKeys } from "../../utils/parsers";
1111
import LicenseMatchesTable from "./LicenseMatchesTable";
12-
import { LicenseTypes } from "../../services/workbenchDB.types";
1312
import { useWorkbenchDB } from "../../contexts/dbContext";
1413
import { TodoAttributes } from "../../services/models/todo";
1514

@@ -105,13 +104,7 @@ const LicenseEntity = (props: LicenseDetectionEntityProps) => {
105104
</div>
106105
<b>Matches</b>
107106
<LicenseMatchesTable
108-
matchesInfo={{
109-
licenseType:
110-
activeLicenseEntity.type === "detection"
111-
? LicenseTypes.DETECTION
112-
: LicenseTypes.CLUE,
113-
matches: matches,
114-
}}
107+
matches={matches}
115108
showLIcenseText={
116109
Boolean(scanInfo.optionsMap.get(ScanOptionKeys.LICENSE_TEXT)) ||
117110
matches[0]?.matched_text?.length > 0 ||

src/components/LicenseEntity/LicenseMatchCells/MatchLicenseExpression.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import CoreLink from "../../CoreLink/CoreLink";
33
import {
44
LICENSE_EXPRESSIONS_CONJUNCTIONS,
55
parseTokensFromExpression,
6-
} from "../../../services/models/databaseUtils";
6+
} from "../../../utils/expressions";
77
import {
88
LicenseClueMatch,
99
LicenseDetectionMatch,

src/components/LicenseEntity/LicenseMatchesTable.tsx

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
import { Button, OverlayTrigger, Popover, Table } from "react-bootstrap";
77
import MatchedTextRenderer from "./LicenseMatchCells/MatchedText";
88
import MatchLicenseExpressionRenderer from "./LicenseMatchCells/MatchLicenseExpression";
9-
import { LicenseTypes } from "../../services/workbenchDB.types";
109
import { MatchedTextProvider } from "./MatchedTextContext";
1110
import MatchRuleDetails from "./MatchRuleDetails";
1211
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -15,23 +14,15 @@ import CoreLink from "../CoreLink/CoreLink";
1514

1615
interface LicenseMatchProps {
1716
showLIcenseText?: boolean;
18-
matchesInfo:
19-
| {
20-
licenseType: LicenseTypes.DETECTION;
21-
matches: LicenseDetectionMatch[];
22-
}
23-
| {
24-
licenseType: LicenseTypes.CLUE;
25-
matches: LicenseClueMatch[];
26-
};
17+
matches: LicenseClueMatch[] | LicenseDetectionMatch[];
2718
}
2819
const LicenseMatchesTable = (props: LicenseMatchProps) => {
29-
const { matchesInfo, showLIcenseText } = props;
20+
const { matches, showLIcenseText } = props;
3021

3122
return (
3223
<MatchedTextProvider>
3324
<div>
34-
{matchesInfo.matches.map((match, idx) => (
25+
{matches.map((match, idx) => (
3526
<div
3627
className="matches-table-container"
3728
key={match.license_expression + "_" + idx}
@@ -49,20 +40,19 @@ const LicenseMatchesTable = (props: LicenseMatchProps) => {
4940
/>
5041
</td>
5142
</tr>
52-
{matchesInfo.licenseType === LicenseTypes.DETECTION &&
53-
(match as LicenseDetectionMatch)?.license_expression_spdx && (
54-
<tr>
55-
<td colSpan={2}>License Expression SPDX</td>
56-
<td colSpan={7}>
57-
<MatchLicenseExpressionRenderer
58-
matchInfo={{
59-
match: match,
60-
spdxLicense: true,
61-
}}
62-
/>
63-
</td>
64-
</tr>
65-
)}
43+
{match.license_expression_spdx && (
44+
<tr>
45+
<td colSpan={2}>License Expression SPDX</td>
46+
<td colSpan={7}>
47+
<MatchLicenseExpressionRenderer
48+
matchInfo={{
49+
match: match,
50+
spdxLicense: true,
51+
}}
52+
/>
53+
</td>
54+
</tr>
55+
)}
6656
{showLIcenseText && (
6757
<tr className="matched-text-row">
6858
<td colSpan={2}>Matched Text</td>

src/services/importedJsonTypes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,17 @@ export interface LicenseMatch {
1818
match_coverage: number;
1919
matcher: string;
2020
license_expression: string;
21+
license_expression_spdx?: string;
2122
rule_identifier: string;
2223
rule_relevance: number;
2324
rule_url: string;
2425

2526
// Parser-added fields
2627
path?: string;
2728
license_expression_keys?: LicenseExpressionKey[];
28-
}
29-
export interface LicenseDetectionMatch extends LicenseMatch {
30-
license_expression_spdx?: string;
3129
license_expression_spdx_keys?: LicenseExpressionSpdxKey[];
3230
}
31+
export type LicenseDetectionMatch = LicenseMatch;
3332
export type LicenseClueMatch = LicenseMatch;
3433

3534
export interface LicenseFileRegion {
@@ -57,6 +56,7 @@ export interface LicenseClue {
5756
fileClueIdx: number;
5857
matches?: LicenseClueMatch[];
5958
file_regions?: LicenseFileRegion[];
59+
license_expression_spdx?: string;
6060
}
6161
export interface TopLevelLicenseDetection {
6262
identifier: string;

src/services/models/databaseUtils.ts

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -37,76 +37,3 @@ export function parentPath(path: string) {
3737
const splits = path.split("/");
3838
return splits.length === 1 ? "#" : splits.slice(0, -1).join("/");
3939
}
40-
41-
export const LICENSE_EXPRESSIONS_CONJUNCTIONS = ["AND", "OR", "WITH"];
42-
43-
// @TODO - Needs more testing
44-
export const parseSubExpressions = (expression: string) => {
45-
if (!expression || !expression.length) return [];
46-
const tokens = expression.split(/( |\(|\))/);
47-
const result = [];
48-
let currSubExpression = "";
49-
let popTokens = 0;
50-
for (const token of tokens) {
51-
if (token === "(") {
52-
if (popTokens) currSubExpression += "(";
53-
popTokens++;
54-
} else if (token === ")") {
55-
popTokens--;
56-
if (popTokens) {
57-
currSubExpression += ")";
58-
} else {
59-
result.push(currSubExpression);
60-
currSubExpression = "";
61-
}
62-
} else {
63-
if (popTokens) currSubExpression += token;
64-
else {
65-
if (token.trim().length) result.push(token);
66-
}
67-
}
68-
}
69-
70-
return result.filter(
71-
(subExpression) =>
72-
subExpression.trim().length &&
73-
!LICENSE_EXPRESSIONS_CONJUNCTIONS.includes(subExpression.trim())
74-
);
75-
};
76-
77-
// Test parseSubExpressions
78-
// [
79-
// 'apache-2.0 AND (mit AND json) AND (apache-2.0 AND bsd-simplified AND bsd-new AND cc0-1.0 AND cddl-1.0) AND (cddl-1.0 AND bsd-new) AND (bsd-new AND epl-2.0 AND elastic-license-v2) AND (bsd-new AND json AND lgpl-2.0 AND mit AND gpl-2.0 AND universal-foss-exception-1.0)',
80-
// 'apache-2.0 AND cc0-1.0 AND mit AND (lgpl-2.1 AND bsd-new AND unknown-license-reference) AND bsd-new AND (mit AND apache-2.0 AND bsd-new) AND (ofl-1.1 AND mit AND cc-by-3.0) AND (mit AND cc0-1.0) AND (mit AND apache-2.0) AND (mit AND gpl-3.0) AND ((mit OR gpl-3.0) AND mit AND gpl-3.0) AND ofl-1.1 AND (ofl-1.1 AND proprietary-license) AND isc AND (bsd-new AND bsd-simplified) AND unknown AND (apache-2.0 AND isc) AND (apache-2.0 AND mit) AND ((gpl-2.0 WITH font-exception-gpl OR ofl-1.1) AND apache-2.0) AND (apache-2.0 AND bsd-new AND bsd-simplified AND cc-by-3.0 AND cc0-1.0 AND gpl-3.0 AND isc AND lgpl-2.1 AND mit AND ofl-1.1 AND unknown-license-reference AND other-copyleft AND other-permissive AND unknown)',
81-
// '(gpl-2.0 WITH font-exception-gpl OR ofl-1.1) AND apache-2.0',
82-
// 'apache OR apache-2.0',
83-
// '(mit OR gpl-3.0) AND mit AND gpl-3.0',
84-
// 'apache-2.0 AND (mit AND json)'
85-
// ].map(key => {
86-
// console.log(key, parseSubExpressions(key), "\n");
87-
// })
88-
89-
export function parseTokensFromExpression(expression: string) {
90-
if (!expression) expression = "";
91-
const tokens = expression.split(/( |\(|\))/);
92-
return tokens;
93-
}
94-
95-
export function parseTokenKeysFromExpression(expression: string) {
96-
if (!expression) expression = "";
97-
const AVOID_KEYWORDS = new Set(["WITH", "OR", "AND", "(", ")"]);
98-
const tokens = parseTokensFromExpression(expression);
99-
return tokens.filter(
100-
(token) =>
101-
token.trim().length && token.length && !AVOID_KEYWORDS.has(token.trim())
102-
);
103-
}
104-
export function filterSpdxKeys(keys: string[]) {
105-
const ignoredPrefixes = ["License-scancode-", "LicenseRef-scancode-"];
106-
return keys.filter((key) => {
107-
for (const prefix of ignoredPrefixes) {
108-
if (key.includes(prefix)) return false;
109-
}
110-
return true;
111-
});
112-
}

0 commit comments

Comments
 (0)