Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Applicability evidence to non-applicable vulnerabilities #497

Merged
merged 12 commits into from
Nov 20, 2024
24 changes: 17 additions & 7 deletions src/main/scanLogic/scanRunners/applicabilityScan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export interface ApplicabilityScanResponse {
// All the cve that have applicable issues
applicableCve: { [cve_id: string]: CveApplicableDetails };
// All the cve that have non-applicable issues
nonapplicableCve: string[];
nonapplicableCve: { [cve_id: string]: CveApplicableDetails };
dortam888 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down Expand Up @@ -268,21 +268,31 @@ export class ApplicabilityRunner extends JasRunner {
let applicableCvesIdToDetails: Map<string, CveApplicableDetails> = new Map<string, CveApplicableDetails>(
Object.entries(scanResponse.applicableCve)
);
let nonapplicableCvesIdToDetails: Map<string, CveApplicableDetails> = new Map<string, CveApplicableDetails>(
dortam888 marked this conversation as resolved.
Show resolved Hide resolved
Object.entries(scanResponse.nonapplicableCve)
);
let relevantScannedCve: string[] = [];
let relevantApplicableCve: Map<string, CveApplicableDetails> = new Map<string, CveApplicableDetails>();
let relevantNonApplicableCve: Map<string, CveApplicableDetails> = new Map<string, CveApplicableDetails>();

for (let scannedCve of scanResponse.scannedCve) {
if (relevantCve.has(scannedCve)) {
relevantScannedCve.push(scannedCve);
let potential: CveApplicableDetails | undefined = applicableCvesIdToDetails.get(scannedCve);
if (potential) {
relevantApplicableCve.set(scannedCve, potential);
} else {
let nonapplicativeCveDetails: CveApplicableDetails | undefined = nonapplicableCvesIdToDetails.get(scannedCve);
if (nonapplicativeCveDetails) {
relevantNonApplicableCve.set(scannedCve, nonapplicativeCveDetails);
}
}
dortam888 marked this conversation as resolved.
Show resolved Hide resolved
}
}
return {
scannedCve: Array.from(relevantScannedCve),
applicableCve: Object.fromEntries(relevantApplicableCve.entries())
applicableCve: Object.fromEntries(relevantApplicableCve.entries()),
nonapplicableCve: Object.fromEntries(relevantNonApplicableCve.entries())
} as ApplicabilityScanResponse;
}

Expand All @@ -298,10 +308,11 @@ export class ApplicabilityRunner extends JasRunner {
// Prepare
const analyzerScanRun: AnalyzerScanRun = response.runs[0];
const applicable: Map<string, CveApplicableDetails> = new Map<string, CveApplicableDetails>();
const nonapplicable: string[] = [];
const nonapplicable: Map<string, CveApplicableDetails> = new Map<string, CveApplicableDetails>();
const scanned: Set<string> = new Set<string>();
const rulesFullDescription: Map<string, string> = new Map<string, string>();
const applicabilityStatues: Map<string, ApplicabilityStatus> = new Map<string, ApplicabilityStatus>();

for (const rule of analyzerScanRun.tool.driver.rules) {
if (rule.fullDescription) {
rulesFullDescription.set(rule.id, rule.fullDescription.text);
Expand All @@ -328,18 +339,17 @@ export class ApplicabilityRunner extends JasRunner {
let fileIssues: FileIssues = this.getOrCreateFileIssues(applicableDetails, location.physicalLocation.artifactLocation.uri);
fileIssues.locations.push(location.physicalLocation.region);
});
scanned.add(this.getCveFromRuleId(analyzeIssue.ruleId));
} else if (status === ApplicabilityStatus.NOT_APPLICABLE) {
nonapplicable.push(this.getCveFromRuleId(analyzeIssue.ruleId));
scanned.add(this.getCveFromRuleId(analyzeIssue.ruleId));
this.getOrCreateApplicableDetails(analyzeIssue, nonapplicable, rulesFullDescription.get(analyzeIssue.ruleId));
}
scanned.add(this.getCveFromRuleId(analyzeIssue.ruleId));
});
}
// Convert data to a response
return {
scannedCve: Array.from(scanned),
applicableCve: Object.fromEntries(applicable.entries()),
nonapplicableCve: nonapplicable
nonapplicableCve: Object.fromEntries(nonapplicable.entries())
} as ApplicabilityScanResponse;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class ProjectDependencyTreeNode extends FileTreeNode {
private _scannedCve?: Set<string> | undefined;
// Is applicable if key in here
private _applicableCve?: Map<string, CveApplicableDetails> | undefined;
private _nonapplicableCve?: Map<string, CveApplicableDetails> | undefined;
dortam888 marked this conversation as resolved.
Show resolved Hide resolved
protected _dependencyScanTimeStamp?: number;
protected _applicableScanTimeStamp?: number;

Expand Down Expand Up @@ -133,6 +134,14 @@ export class ProjectDependencyTreeNode extends FileTreeNode {
this._applicableCve = value;
}

public get nonapplicableCve(): Map<string, CveApplicableDetails> | undefined {
return this._nonapplicableCve;
}

public set nonapplicableCve(value: Map<string, CveApplicableDetails> | undefined) {
this._nonapplicableCve = value;
}

public get applicableScanTimeStamp(): number | undefined {
return this._applicableScanTimeStamp;
}
Expand Down
21 changes: 18 additions & 3 deletions src/main/treeDataProviders/utils/analyzerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ export class AnalyzerUtils {
descriptorNode.applicableCve = new Map<string, CveApplicableDetails>(
dependencyScanResults.applicableIssues ? Object.entries(dependencyScanResults.applicableIssues.applicableCve) : []
);
descriptorNode.nonapplicableCve = new Map<string, CveApplicableDetails>(
dependencyScanResults.applicableIssues ? Object.entries(dependencyScanResults.applicableIssues.nonapplicableCve) : []
);
descriptorNode.applicableScanTimeStamp = dependencyScanResults.applicableScanTimestamp;

// Populate related CodeFile nodes with issues and update the descriptor CVE applicability details
Expand All @@ -232,10 +235,10 @@ export class AnalyzerUtils {
}
for (const node of nodes) {
if (node instanceof CveTreeNode) {
let evidences: IEvidence[] = [];
let potential: CveApplicableDetails | undefined = descriptorNode.applicableCve?.get(node.labelId);
if (potential) {
let details: CveApplicableDetails = potential;
let evidences: IEvidence[] = [];
// Populate code file issues for workspace
details.fileEvidences.forEach((fileEvidence: FileIssues) => {
let fileNode: CodeFileTreeNode = this.getOrCreateCodeFileNode(root, fileEvidence.full_path);
Expand All @@ -249,8 +252,20 @@ export class AnalyzerUtils {
} as IApplicableDetails;
} else {
// Not applicable
node.severity = SeverityUtils.notApplicable(node.severity);
node.applicableDetails = { isApplicable: false } as IApplicableDetails;
let nonapplicableApplicableDetails: CveApplicableDetails | undefined = descriptorNode.nonapplicableCve?.get(node.labelId);
if (nonapplicableApplicableDetails) {
dortam888 marked this conversation as resolved.
Show resolved Hide resolved
evidences.push({
reason: nonapplicableApplicableDetails.fixReason,
filePathEvidence: '',
codeEvidence: ''
dortam888 marked this conversation as resolved.
Show resolved Hide resolved
} as IEvidence);
node.severity = SeverityUtils.notApplicable(node.severity);
node.applicableDetails = {
isApplicable: false,
searchTarget: nonapplicableApplicableDetails.fullDescription,
evidence: evidences
} as IApplicableDetails;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/tests/integration/applicability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('Applicability Integration Tests', async () => {
});

it('Check all expected nonapplicable CVE detected', () => {
assert.sameDeepMembers(response.nonapplicableCve, expectedContent.nonapplicableCve);
assert.includeDeepMembers(Object.keys(response.nonapplicableCve), Object.keys(expectedContent.nonapplicableCve));
});

describe('Applicable details data validations', () => {
Expand Down
Loading