Skip to content

Commit

Permalink
Only run advance security scanners on supported tech (#347)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas authored May 17, 2023
1 parent 34ae54d commit 504d694
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 44 deletions.
5 changes: 5 additions & 0 deletions src/main/scanLogic/scanRunners/applicabilityScan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AnalyzeIssue, AnalyzeLocation, AnalyzerScanRun, ScanType, AnalyzeScanRe
import { ConnectionManager } from '../../connect/connectionManager';
import { Resource } from '../../utils/resource';
import { ScanUtils } from '../../utils/scanUtils';
import { PackageType } from '../../types/projectType';

/**
* The request that is sent to the binary to scan applicability
Expand Down Expand Up @@ -49,6 +50,10 @@ export class ApplicabilityRunner extends BinaryRunner {
super(connectionManager, timeout, ScanType.ContextualAnalysis, logManager, binary);
}

public static supportedPackageTypes(): PackageType[] {
return [PackageType.Npm, PackageType.Yarn, PackageType.Python];
}

/** @override */
protected async runBinary(yamlConfigPath: string, executionLogDirectory: string, checkCancel: () => void): Promise<void> {
await this.executeBinary(checkCancel, ['ca', yamlConfigPath], executionLogDirectory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { ScanResults, DependencyScanResults } from '../../types/workspaceIssuesD
import { AnalyzerUtils } from '../utils/analyzerUtils';
import { IacTreeNode } from './codeFileTree/iacTreeNode';
import { SecretTreeNode } from './codeFileTree/secretsTreeNode';
import { UsageUtils } from '../../utils/usageUtils';

/**
* Describes Xray issues data provider for the 'Issues' tree view and provides API to get issues data for files.
Expand Down Expand Up @@ -201,7 +202,7 @@ export class IssuesTreeDataProvider implements vscode.TreeDataProvider<IssuesRoo
let workspaceDescriptors: Map<PackageType, vscode.Uri[]> = await ScanUtils.locatePackageDescriptors([root.workSpace], this._logManager);
let subStepsCount: number = IssuesTreeDataProvider.getNumberOfTasksInRepopulate(this._scanManager.supportedScans, workspaceDescriptors);
checkCanceled();
DependencyUtils.sendUsageReport(this._scanManager.supportedScans, workspaceDescriptors, this._treesManager.connectionManager);
UsageUtils.sendUsageReport(this._scanManager.supportedScans, workspaceDescriptors, this._treesManager.connectionManager);
// Scan workspace
let scansPromises: Promise<any>[] = [];
progressManager.startStep('🔎 Scanning for issues', subStepsCount);
Expand Down
50 changes: 7 additions & 43 deletions src/main/treeDataProviders/utils/dependencyUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as vscode from 'vscode';
import { IComponent, IGraphResponse, IUsageFeature, IViolation, IVulnerability } from 'jfrog-client-js';
import { IComponent, IGraphResponse, IViolation, IVulnerability } from 'jfrog-client-js';
import { RootNode } from '../dependenciesTree/dependenciesRoot/rootTree';
import { DependenciesTreeNode } from '../dependenciesTree/dependenciesTreeNode';
import { Severity, SeverityUtils } from '../../types/severity';
Expand All @@ -19,18 +19,17 @@ import { DependencyScanResults, ScanResults } from '../../types/workspaceIssuesD
import { EnvironmentTreeNode } from '../issuesTree/descriptorTree/environmentTreeNode';
import { ProjectDependencyTreeNode } from '../issuesTree/descriptorTree/projectDependencyTreeNode';
import { NugetUtils } from '../../utils/nugetUtils';
import { ConnectionManager } from '../../connect/connectionManager';
import { IssuesRootTreeNode } from '../issuesTree/issuesRootTreeNode';
import { GraphScanProgress, StepProgress } from './stepProgress';
import { AnalyzerUtils } from './analyzerUtils';
import { DescriptorTreeNode } from '../issuesTree/descriptorTree/descriptorTreeNode';
import { VirtualEnvPypiTree } from '../dependenciesTree/dependenciesRoot/virtualEnvPypiTree';
import { ScanManager, SupportedScans } from '../../scanLogic/scanManager';
import { ScanManager } from '../../scanLogic/scanManager';
import { FileScanBundle, FileScanError, ScanUtils } from '../../utils/scanUtils';
import { LogManager } from '../../log/logManager';
import { GeneralInfo } from '../../types/generalInfo';
import { FileTreeNode } from '../issuesTree/fileTreeNode';
import { ScanType } from '../../scanLogic/scanRunners/analyzerModels';
import { ApplicabilityRunner } from '../../scanLogic/scanRunners/applicabilityScan';

export class DependencyUtils {
public static readonly FAIL_TO_SCAN: string = '[Fail to scan]';
Expand Down Expand Up @@ -193,10 +192,11 @@ export class DependencyUtils {
return;
}
let foundIssues: boolean = false;
let dependencyScanResult: DependencyScanResults = <DependencyScanResults>fileScanBundle.data;
// Dependency graph scan task
await DependencyUtils.scanProjectDependencyGraph(
scanManager,
<DependencyScanResults>fileScanBundle.data,
dependencyScanResult,
fileScanBundle.dataNode,
rootGraph,
scanProgress,
Expand All @@ -206,15 +206,15 @@ export class DependencyUtils {
foundIssues = issuesFound > 0;
if (foundIssues) {
// populate data and view
fileScanBundle.workspaceResults.descriptorsIssues.push(<DependencyScanResults>fileScanBundle.data);
fileScanBundle.workspaceResults.descriptorsIssues.push(dependencyScanResult);
fileScanBundle.root.addChildAndApply(fileScanBundle.dataNode);
}
})
.catch(error => DependencyUtils.onFileScanError(error, scanManager.logManager, fileScanBundle))
.finally(() => scanProgress.onProgress());

// Applicable scan task
if (!contextualScan || !foundIssues) {
if (!contextualScan || !foundIssues || !ApplicabilityRunner.supportedPackageTypes().includes(dependencyScanResult.type)) {
return;
}
if (fileScanBundle.dataNode instanceof DescriptorTreeNode) {
Expand Down Expand Up @@ -540,40 +540,4 @@ export class DependencyUtils {
return [];
}
}

/**
* Sends usage report for all techs we found project descriptors of and for each advance scan that was preformed.
* @param supportedScans - the entitlement for each scan
* @param projectDescriptors - map of all project descriptors by their tech.
* @param connectionManager - manager containing Artifactory details if configured.
*/
public static async sendUsageReport(
supportedScans: SupportedScans,
projectDescriptors: Map<PackageType, vscode.Uri[]>,
connectionManager: ConnectionManager
) {
let featureArray: IUsageFeature[] = [];
if (supportedScans.dependencies) {
for (const [techEnum, descriptors] of projectDescriptors.entries()) {
// Only add to usage if found descriptors for tech.
if (!!descriptors) {
const featureName: string = PackageType[techEnum].toLowerCase() + '-deps';
featureArray.push({ featureId: featureName });
}
}
}
if (supportedScans.applicability) {
featureArray.push({ featureId: ScanType.ContextualAnalysis });
}
if (supportedScans.iac) {
featureArray.push({ featureId: ScanType.Iac });
}
if (supportedScans.secrets) {
featureArray.push({ featureId: ScanType.Secrets });
}
if (featureArray.length === 0) {
return;
}
await connectionManager.sendUsageReport(featureArray);
}
}
58 changes: 58 additions & 0 deletions src/main/utils/usageUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as vscode from 'vscode';
import { IUsageFeature } from 'jfrog-client-js';
import { PackageType } from '../types/projectType';
import { SupportedScans } from '../scanLogic/scanManager';
import { ConnectionManager } from '../connect/connectionManager';
import { ApplicabilityRunner } from '../scanLogic/scanRunners/applicabilityScan';

export class UsageUtils {
private static getUsageFeaturesByExistTech(
projectDescriptors: Map<PackageType, vscode.Uri[]>,
scanSuffix: string,
onlyInclude?: PackageType[]
): IUsageFeature[] {
let features: IUsageFeature[] = [];
for (const [techEnum, descriptors] of projectDescriptors.entries()) {
// If we only support subset of the techs, check if tech is supported.
if (onlyInclude && onlyInclude.length > 0 && !onlyInclude.includes(techEnum)) {
continue;
}
// Only add to usage if found descriptors for tech.
if (!!descriptors && descriptors.length > 0) {
const featureName: string = PackageType[techEnum].toLowerCase() + '-' + scanSuffix;
features.push({ featureId: featureName });
}
}
return features;
}

/**
* Sends usage report for all techs we found project descriptors of and for each advance scan that was preformed.
* @param supportedScans - the entitlement for each scan
* @param projectDescriptors - map of all project descriptors by their tech.
* @param connectionManager - manager containing Artifactory details if configured.
*/
public static async sendUsageReport(
supportedScans: SupportedScans,
projectDescriptors: Map<PackageType, vscode.Uri[]>,
connectionManager: ConnectionManager
) {
let features: IUsageFeature[] = [];
if (supportedScans.dependencies) {
features.push(...this.getUsageFeaturesByExistTech(projectDescriptors, 'deps'));
}
if (supportedScans.applicability) {
features.push(...this.getUsageFeaturesByExistTech(projectDescriptors, 'contextual', ApplicabilityRunner.supportedPackageTypes()));
}
if (supportedScans.iac) {
features.push({ featureId: 'iac' });
}
if (supportedScans.secrets) {
features.push({ featureId: 'secrets' });
}
if (features.length === 0) {
return;
}
await connectionManager.sendUsageReport(features);
}
}
78 changes: 78 additions & 0 deletions src/test/tests/usageUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { IUsageFeature } from 'jfrog-client-js';
import { ConnectionManager } from '../../main/connect/connectionManager';
import { assert } from 'chai';
import { LogManager } from '../../main/log/logManager';
import { SupportedScans } from '../../main/scanLogic/scanManager';
import { PackageType } from '../../main/types/projectType';
import { UsageUtils } from '../../main/utils/usageUtils';
import { Uri } from 'vscode';

describe('Usage Utils Tests', async () => {
const logManager: LogManager = new LogManager().activate();

[
{
test: 'Not supported',
supportedScans: {} as SupportedScans,
descriptors: getDummyDescriptors(),
expectedFeatures: [],
expectedReportSent: false
},
{
test: 'With dependencies scan',
supportedScans: { dependencies: true } as SupportedScans,
descriptors: getDummyDescriptors(PackageType.Go, PackageType.Npm),
expectedFeatures: [{ featureId: 'go-deps' }, { featureId: 'npm-deps' }],
expectedReportSent: true
},
{
test: 'With advance scan',
supportedScans: { dependencies: true, applicability: true, iac: true, secrets: true } as SupportedScans,
descriptors: getDummyDescriptors(PackageType.Go, PackageType.Npm),
expectedFeatures: [
{ featureId: 'go-deps' },
{ featureId: 'npm-deps' },
{ featureId: 'npm-contextual' },
{ featureId: 'iac' },
{ featureId: 'secrets' }
],
expectedReportSent: true
}
].forEach(testCase => {
it('Send usage report features - ' + testCase.test, async () => {
const dummyConnectionManager: ConnectionMangerDummy = new ConnectionMangerDummy(logManager);
dummyConnectionManager.expectedFeatures = testCase.expectedFeatures;
await UsageUtils.sendUsageReport(testCase.supportedScans, testCase.descriptors, dummyConnectionManager).then(() =>
assert.equal(dummyConnectionManager.reportSent, testCase.expectedReportSent)
);
});
});

function getDummyDescriptors(...types: PackageType[]): Map<PackageType, Uri[]> {
let descriptors: Map<PackageType, Uri[]> = new Map<PackageType, Uri[]>();
for (let type of types) {
descriptors.set(type, [Uri.parse('/somewhere/file'), Uri.parse('/somewhere/other')]);
}
return descriptors;
}
});

class ConnectionMangerDummy extends ConnectionManager {
private _expectedFeatures: IUsageFeature[] = [];
private _reportSent: boolean = false;

/** @override */
public async sendUsageReport(featureArray: IUsageFeature[]): Promise<void> {
this._reportSent = true;
assert.sameDeepMembers(featureArray, this._expectedFeatures);
}

public set expectedFeatures(value: IUsageFeature[]) {
this._expectedFeatures = value;
this._reportSent = false;
}

public get reportSent(): boolean {
return this._reportSent;
}
}

0 comments on commit 504d694

Please sign in to comment.