diff --git a/src/main/scanLogic/scanRunners/analyzerManager.ts b/src/main/scanLogic/scanRunners/analyzerManager.ts index 1e2e08b4..059a4a3d 100644 --- a/src/main/scanLogic/scanRunners/analyzerManager.ts +++ b/src/main/scanLogic/scanRunners/analyzerManager.ts @@ -17,7 +17,7 @@ import { LogUtils } from '../../log/logUtils'; export class AnalyzerManager { private static readonly RELATIVE_DOWNLOAD_URL: string = '/xsc-gen-exe-analyzer-manager-local/v1'; private static readonly BINARY_NAME: string = 'analyzerManager'; - public static readonly ANALYZER_MANAGER_VERSION: string = '1.11.2'; + public static readonly ANALYZER_MANAGER_VERSION: string = '1.9.11'; public static readonly ANALYZER_MANAGER_PATH: string = Utils.addWinSuffixIfNeeded( path.join(ScanUtils.getIssuesPath(), AnalyzerManager.BINARY_NAME, AnalyzerManager.BINARY_NAME) ); diff --git a/src/main/scanLogic/scanRunners/applicabilityScan.ts b/src/main/scanLogic/scanRunners/applicabilityScan.ts index f46ee5dc..37160fed 100644 --- a/src/main/scanLogic/scanRunners/applicabilityScan.ts +++ b/src/main/scanLogic/scanRunners/applicabilityScan.ts @@ -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[]; + notApplicableCve: { [cve_id: string]: CveApplicableDetails }; } /** @@ -268,8 +268,12 @@ export class ApplicabilityRunner extends JasRunner { let applicableCvesIdToDetails: Map = new Map( Object.entries(scanResponse.applicableCve) ); + let notApplicableCvesIdToDetails: Map = new Map( + Object.entries(scanResponse.notApplicableCve) + ); let relevantScannedCve: string[] = []; let relevantApplicableCve: Map = new Map(); + let relevantNotApplicableCve: Map = new Map(); for (let scannedCve of scanResponse.scannedCve) { if (relevantCve.has(scannedCve)) { @@ -277,12 +281,18 @@ export class ApplicabilityRunner extends JasRunner { let potential: CveApplicableDetails | undefined = applicableCvesIdToDetails.get(scannedCve); if (potential) { relevantApplicableCve.set(scannedCve, potential); + continue + } + potential = notApplicableCvesIdToDetails.get(scannedCve); + if (potential) { + relevantNotApplicableCve.set(scannedCve, potential); } } } return { scannedCve: Array.from(relevantScannedCve), - applicableCve: Object.fromEntries(relevantApplicableCve.entries()) + applicableCve: Object.fromEntries(relevantApplicableCve.entries()), + notApplicableCve: Object.fromEntries(relevantNotApplicableCve.entries()) } as ApplicabilityScanResponse; } @@ -298,10 +308,11 @@ export class ApplicabilityRunner extends JasRunner { // Prepare const analyzerScanRun: AnalyzerScanRun = response.runs[0]; const applicable: Map = new Map(); - const nonapplicable: string[] = []; + const notApplicable: Map = new Map(); const scanned: Set = new Set(); const rulesFullDescription: Map = new Map(); const applicabilityStatues: Map = new Map(); + for (const rule of analyzerScanRun.tool.driver.rules) { if (rule.fullDescription) { rulesFullDescription.set(rule.id, rule.fullDescription.text); @@ -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, notApplicable, 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 + notApplicableCve: Object.fromEntries(notApplicable.entries()) } as ApplicabilityScanResponse; } diff --git a/src/main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode.ts b/src/main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode.ts index c12e8a66..74eecc03 100644 --- a/src/main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode.ts +++ b/src/main/treeDataProviders/issuesTree/descriptorTree/projectDependencyTreeNode.ts @@ -19,6 +19,7 @@ export class ProjectDependencyTreeNode extends FileTreeNode { private _scannedCve?: Set | undefined; // Is applicable if key in here private _applicableCve?: Map | undefined; + private _notApplicableCve?: Map | undefined; protected _dependencyScanTimeStamp?: number; protected _applicableScanTimeStamp?: number; @@ -133,6 +134,14 @@ export class ProjectDependencyTreeNode extends FileTreeNode { this._applicableCve = value; } + public get notApplicableCve(): Map | undefined { + return this._notApplicableCve; + } + + public set notApplicableCve(value: Map | undefined) { + this._notApplicableCve = value; + } + public get applicableScanTimeStamp(): number | undefined { return this._applicableScanTimeStamp; } diff --git a/src/main/treeDataProviders/utils/analyzerUtils.ts b/src/main/treeDataProviders/utils/analyzerUtils.ts index fdf693c4..d830dce5 100644 --- a/src/main/treeDataProviders/utils/analyzerUtils.ts +++ b/src/main/treeDataProviders/utils/analyzerUtils.ts @@ -228,6 +228,9 @@ export class AnalyzerUtils { descriptorNode.applicableCve = new Map( dependencyScanResults.applicableIssues ? Object.entries(dependencyScanResults.applicableIssues.applicableCve) : [] ); + descriptorNode.notApplicableCve = new Map( + dependencyScanResults.applicableIssues ? Object.entries(dependencyScanResults.applicableIssues.notApplicableCve) : [] + ); descriptorNode.applicableScanTimeStamp = dependencyScanResults.applicableScanTimestamp; // Populate related CodeFile nodes with issues and update the descriptor CVE applicability details @@ -240,10 +243,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); @@ -256,9 +259,20 @@ export class AnalyzerUtils { evidence: evidences } as IApplicableDetails; } else { - // Not applicable + // Not Applicable + let notApplicableApplicableDetails: CveApplicableDetails | undefined = descriptorNode.notApplicableCve?.get(node.labelId); + if (!notApplicableApplicableDetails) { + continue + } + evidences.push({ + reason: notApplicableApplicableDetails.fixReason + } as IEvidence); node.severity = SeverityUtils.notApplicable(node.severity); - node.applicableDetails = { isApplicable: false } as IApplicableDetails; + node.applicableDetails = { + isApplicable: false, + searchTarget: notApplicableApplicableDetails.fullDescription, + evidence: evidences + } as IApplicableDetails; } } } diff --git a/src/test/resources/applicableScan/npm/expectedScanResponse.json b/src/test/resources/applicableScan/npm/expectedScanResponse.json index 65d462d4..b2fac104 100644 --- a/src/test/resources/applicableScan/npm/expectedScanResponse.json +++ b/src/test/resources/applicableScan/npm/expectedScanResponse.json @@ -38,9 +38,18 @@ "fullDescription": "The scanner checks whether any of the following vulnerable functions are called:\n\n* `util.setProperty` with external input to its 2nd (`path`) or 3rd (`value`) arguments.\n* `ReflectionObject.setParsedOption` with external input to its 2nd (`name`) or 3rd (`value`) arguments.\n* `parse` with external input to its 1st (`source`) argument.\n* `load`\n* `loadSync`\n\nThe scanner also checks whether the `Object.freeze()` remediation is not present." } }, - "nonapplicableCve": [ - "CVE-2021-3807", - "CVE-2021-3918", - "CVE-2021-44228" - ] + "notApplicableCve": { + "CVE-2021-3807": { + "fixReason": "The scanner checks whether the vulnerable function `ansi-regex` is called.", + "fullDescription": "The scanner checks whether the vulnerable function `ansi-regex` is called." + }, + "CVE-2021-3918":{ + "fixReason": "The scanner checks whether any of the following vulnerable functions are called:\n\n* `json-schema.validate` with external input to its 1st (`instance`) argument.\n* `json-schema.checkPropertyChange` with external input to its 2nd (`schema`) argument.\n\nThe scanner also checks whether the `Object.freeze()` remediation is not present.", + "fullDescription": "The scanner checks whether any of the following vulnerable functions are called:\n\n* `json-schema.validate` with external input to its 1st (`instance`) argument.\n* `json-schema.checkPropertyChange` with external input to its 2nd (`schema`) argument.\n\nThe scanner also checks whether the `Object.freeze()` remediation is not present." + }, + "CVE-2021-44228":{ + "fixReason": "The scanner checks whether any of the following vulnerable functions are called:\n\n* `info` with external input to any of its arguments.\n* `fatal` with external input to any of its arguments.\n* `log` with external input to any of its arguments.\n* `warn` with external input to any of its arguments.\n* `trace` with external input to any of its arguments.\n* `error` with external input to any of its arguments.\n* `debug` with external input to any of its arguments.", + "fullDescription": "The scanner checks whether any of the following vulnerable functions are called:\n\n* `info` with external input to any of its arguments.\n* `fatal` with external input to any of its arguments.\n* `log` with external input to any of its arguments.\n* `warn` with external input to any of its arguments.\n* `trace` with external input to any of its arguments.\n* `error` with external input to any of its arguments.\n* `debug` with external input to any of its arguments." + } + } } \ No newline at end of file diff --git a/src/test/resources/applicableScan/python/expectedScanResponse.json b/src/test/resources/applicableScan/python/expectedScanResponse.json index 1b195c94..1c6e30c6 100644 --- a/src/test/resources/applicableScan/python/expectedScanResponse.json +++ b/src/test/resources/applicableScan/python/expectedScanResponse.json @@ -27,8 +27,14 @@ "fullDescription": "The scanner checks whether the vulnerable function `open` is called with external input to its 1st (`name`) argument." } }, - "nonapplicableCve": [ - "CVE-2021-3918", - "CVE-2019-15605" - ] + "notApplicableCve": { + "CVE-2021-3918": { + "fixReason": "The scanner checks whether any of the following vulnerable functions are called:\n\n* `json-schema.validate` with external input to its 1st (`instance`) argument.\n* `json-schema.checkPropertyChange` with external input to its 2nd (`schema`) argument.\n\nThe scanner also checks whether the `Object.freeze()` remediation is not present.", + "fullDescription": "The scanner checks whether any of the following vulnerable functions are called:\n\n* `json-schema.validate` with external input to its 1st (`instance`) argument.\n* `json-schema.checkPropertyChange` with external input to its 2nd (`schema`) argument.\n\nThe scanner also checks whether the `Object.freeze()` remediation is not present." + }, + "CVE-2019-15605": { + "fixReason": "The scanner checks whether Express.js, which is the vulnerability's main remote attack vector, is running.", + "fullDescription": "The scanner checks whether Express.js, which is the vulnerability's main remote attack vector, is running." + } + } } \ No newline at end of file diff --git a/src/test/tests/integration/applicability.test.ts b/src/test/tests/integration/applicability.test.ts index 347f7312..43bc739c 100644 --- a/src/test/tests/integration/applicability.test.ts +++ b/src/test/tests/integration/applicability.test.ts @@ -76,8 +76,8 @@ describe('Applicability Integration Tests', async () => { assert.includeDeepMembers(Object.keys(response.applicableCve), Object.keys(expectedContent.applicableCve)); }); - it('Check all expected nonapplicable CVE detected', () => { - assert.sameDeepMembers(response.nonapplicableCve, expectedContent.nonapplicableCve); + it('Check all expected notApplicableCve CVE detected', () => { + assert.includeDeepMembers(Object.keys(response.notApplicableCve), Object.keys(expectedContent.notApplicableCve)); }); describe('Applicable details data validations', () => {