Skip to content

Commit

Permalink
Add Applicability evidence to non-applicable vulnerabilities (#497)
Browse files Browse the repository at this point in the history
  • Loading branch information
dortam888 authored Nov 20, 2024
1 parent 75651bf commit d7825a7
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/main/scanLogic/scanRunners/analyzerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
Expand Down
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[];
notApplicableCve: { [cve_id: string]: CveApplicableDetails };
}

/**
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 notApplicableCvesIdToDetails: Map<string, CveApplicableDetails> = new Map<string, CveApplicableDetails>(
Object.entries(scanResponse.notApplicableCve)
);
let relevantScannedCve: string[] = [];
let relevantApplicableCve: Map<string, CveApplicableDetails> = new Map<string, CveApplicableDetails>();
let relevantNotApplicableCve: 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);
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;
}

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 notApplicable: 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, 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;
}

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 _notApplicableCve?: Map<string, CveApplicableDetails> | undefined;
protected _dependencyScanTimeStamp?: number;
protected _applicableScanTimeStamp?: number;

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

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

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

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

// Populate related CodeFile nodes with issues and update the descriptor CVE applicability details
Expand All @@ -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);
Expand All @@ -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;
}
}
}
Expand Down
19 changes: 14 additions & 5 deletions src/test/resources/applicableScan/npm/expectedScanResponse.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
}
14 changes: 10 additions & 4 deletions src/test/resources/applicableScan/python/expectedScanResponse.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
}
4 changes: 2 additions & 2 deletions src/test/tests/integration/applicability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down

0 comments on commit d7825a7

Please sign in to comment.