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

Support JFrog Apps Config file #405

Merged
merged 9 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions src/main/connect/connectionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,6 @@ export class ConnectionUtils {
return Promise.resolve('Successfully connected to Xray version: ' + xrayVersion);
}

public static async testXrayVersionForScanGraph(jfrogClient: JfrogClient, logger: LogManager): Promise<boolean> {
let xrayVersion: string = await this.getXrayVersion(jfrogClient);
if (!(await this.isXrayVersionCompatible(xrayVersion, ConnectionUtils.MINIMAL_XRAY_VERSION_SUPPORTED))) {
logger.logError(new Error('Dependencies scan is supported only on Xray >= 3.29.0'), true);
return false;
}
return true;
}

public static async testXrayEntitlementForFeature(jfrogClient: JfrogClient, feature: EntitlementScanFeature): Promise<boolean> {
return await jfrogClient
.xray()
Expand Down
148 changes: 23 additions & 125 deletions src/main/scanLogic/scanManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,23 @@ import * as vscode from 'vscode';

import { ExtensionComponent } from '../extensionComponent';

import { LogManager } from '../log/logManager';
import { ConnectionManager } from '../connect/connectionManager';
import { ConnectionUtils, EntitlementScanFeature } from '../connect/connectionUtils';
import { LogManager } from '../log/logManager';

import { RootNode } from '../treeDataProviders/dependenciesTree/dependenciesRoot/rootTree';
import { IGraphResponse, XrayScanProgress } from 'jfrog-client-js';
import { GraphScanLogic } from './scanGraphLogic';
import { ApplicabilityRunner, ApplicabilityScanResponse } from './scanRunners/applicabilityScan';
import { EosRunner, EosScanRequest, EosScanResponse } from './scanRunners/eosScan';
import { AnalyzerUtils } from '../treeDataProviders/utils/analyzerUtils';
import { Configuration } from '../utils/configuration';
import { RootNode } from '../treeDataProviders/dependenciesTree/dependenciesRoot/rootTree';
import { StepProgress } from '../treeDataProviders/utils/stepProgress';
import { Resource } from '../utils/resource';
import { BinaryRunner } from './scanRunners/binaryRunner';
import { ScanUtils } from '../utils/scanUtils';
import { StepProgress } from '../treeDataProviders/utils/stepProgress';
import { Utils } from '../utils/utils';
import { IacRunner, IacScanResponse } from './scanRunners/iacScan';
import { SecretsRunner, SecretsScanResponse } from './scanRunners/secretsScan';
import { GraphScanLogic } from './scanGraphLogic';
import { JasRunner } from './scanRunners/jasRunner';

export interface SupportedScans {
export interface EntitledScans {
dependencies: boolean;
applicability: boolean;
eos: boolean;
sast: boolean;
iac: boolean;
secrets: boolean;
}
Expand All @@ -37,7 +31,7 @@ export class ScanManager implements ExtensionComponent {
private static readonly RESOURCE_CHECK_UPDATE_INTERVAL_MILLISECS: number = 1000 * 60 * 60 * 24;

private static lastOutdatedCheck: number;
private _supportedScans: SupportedScans = {} as SupportedScans;
private _entitledScans: EntitledScans = {} as EntitledScans;

constructor(private _connectionManager: ConnectionManager, protected _logManager: LogManager) {}

Expand All @@ -54,16 +48,16 @@ export class ScanManager implements ExtensionComponent {
return this._connectionManager;
}

public get supportedScans(): SupportedScans {
return this._supportedScans;
public get entitledScans(): EntitledScans {
return this._entitledScans;
}

/**
* Updates all the resources that are outdated.
* @param supportedScans - the supported scan to get the needed resources. if default, should call getSupportedScans before calling this method.
* @returns true if all the outdated resources updated successfully, false otherwise
*/
public async updateResources(supportedScans: SupportedScans = this._supportedScans): Promise<boolean> {
public async updateResources(supportedScans: EntitledScans = this._entitledScans): Promise<boolean> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a couple of quistions regarding the param:

  1. Given that its name suggests that ScanManager is responsible for managing all scans, why is it necessary to pass in the supported scan as a parameter? As a manager, one would expect it to handle all aspects of the scan without revealing internal implementation details to other functions. A manager should inherently know what is supported or not.

  2. The only reference I could find, calling this func is at 'IssuesTreeDataProvider'. Why data providers should care about scan manager updates? again, no one can tell a manager what to do regarding internal details that are being used for scans operations.

let result: boolean = true;
await ScanUtils.backgroundTask(async (progress: vscode.Progress<{ message?: string; increment?: number }>) => {
progress.report({ message: 'Checking for updates' });
Expand Down Expand Up @@ -96,7 +90,7 @@ export class ScanManager implements ExtensionComponent {
return result;
}

private async getOutdatedResources(supportedScans: SupportedScans): Promise<Resource[]> {
private async getOutdatedResources(supportedScans: EntitledScans): Promise<Resource[]> {
if (!this.shouldCheckOutdated()) {
return [];
}
Expand Down Expand Up @@ -128,23 +122,16 @@ export class ScanManager implements ExtensionComponent {
return !ScanManager.lastOutdatedCheck || Date.now() - ScanManager.lastOutdatedCheck > ScanManager.RESOURCE_CHECK_UPDATE_INTERVAL_MILLISECS;
}

private getResources(supportedScans: SupportedScans): Resource[] {
private getResources(supportedScans: EntitledScans): Resource[] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'Resource' is a vague name that refers to any artifact that is downloaded from Artifactory. The 'Resource' in 'scanManager' talks about a specific resource which is the JasRunners. I suggest we use the most descriptive name we can and replace 'Resource' with 'JasRunners'

let resources: Resource[] = [];
if (supportedScans.applicability || supportedScans.iac || supportedScans.secrets) {
resources.push(BinaryRunner.getAnalyzerManagerResource(this._logManager));
resources.push(JasRunner.getAnalyzerManagerResource(this._logManager));
} else {
this.logManager.logMessage('You are not entitled to run Advanced Security scans', 'DEBUG');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we identify if we are not entitled before calling getResources at getSupportedScans?

}
return resources;
}

/**
* Validate if the graph-scan is supported in the Xray version
*/
public async validateGraphSupported(): Promise<boolean> {
return await ConnectionUtils.testXrayVersionForScanGraph(this._connectionManager.createJfrogClient(), this._logManager);
}

/**
* Check if Contextual Analysis (Applicability) is supported for the user
*/
Expand All @@ -167,23 +154,19 @@ export class ScanManager implements ExtensionComponent {
}

/**
* Check if Eos scan is supported for the user
* Check if SAST scan is supported for the user
*/
public async isEosSupported(): Promise<boolean> {
return true;
public async isSastSupported(): Promise<boolean> {
// TODO: change to SAST feature when Xray entitlement service support it.
return await ConnectionUtils.testXrayEntitlementForFeature(this._connectionManager.createJfrogClient(), EntitlementScanFeature.Applicability);
}

/**
* Get all the entitlement status for each type of scan the manager offers
*/
public async getSupportedScans(): Promise<SupportedScans> {
let supportedScans: SupportedScans = {} as SupportedScans;
public async getSupportedScans(): Promise<EntitledScans> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that we change it to be private, and each support check will be response to update it own property
For example:

    public async isApplicabilitySupported(): Promise<boolean> {
        return await ConnectionUtils.testXrayEntitlementForFeature(this._connectionManager.createJfrogClient(), EntitlementScanFeature.Applicability);
    }

--->

        public async setApplicabilityEntitled(): Promise<void> {
        ConnectionUtils.testXrayEntitlementForFeature(this._connectionManager.createJfrogClient(), EntitlementScanFeature.Applicability)
        .then(res => (this.entitledScans.applicability = res))
        .catch(err => ScanUtils.onScanError(err, this._logManager, true))
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the suggestion is not related to Apps Config and not to the changes I did in this PR.
This PR has already become too big.

let supportedScans: EntitledScans = {} as EntitledScans;
let requests: Promise<any>[] = [];
requests.push(
this.validateGraphSupported()
.then(res => (supportedScans.dependencies = res))
.catch(err => ScanUtils.onScanError(err, this._logManager, true))
);
requests.push(
this.isApplicabilitySupported()
.then(res => (supportedScans.applicability = res))
Expand All @@ -200,12 +183,12 @@ export class ScanManager implements ExtensionComponent {
.catch(err => ScanUtils.onScanError(err, this._logManager, true))
);
requests.push(
this.isEosSupported()
.then(res => (supportedScans.eos = res))
this.isSastSupported()
.then(res => (supportedScans.sast = res))
.catch(err => ScanUtils.onScanError(err, this._logManager, true))
);
await Promise.all(requests);
this._supportedScans = supportedScans;
this._entitledScans = supportedScans;
return supportedScans;
}

Expand All @@ -222,89 +205,4 @@ export class ScanManager implements ExtensionComponent {
let scanLogic: GraphScanLogic = new GraphScanLogic(this._connectionManager);
return await scanLogic.scan(graphRoot, progress, checkCanceled);
}

/**
* Scan CVE in files for applicability issues.
* @param directory - the directory that will be scan
* @param checkCancel - check if should cancel
* @param cveToRun - the CVE list we want to run applicability scan on
* @returns the applicability scan response
*/
public async scanApplicability(
directory: string,
checkCancel: () => void,
cveToRun: Set<string> = new Set<string>()
): Promise<ApplicabilityScanResponse> {
let applicableRunner: ApplicabilityRunner = new ApplicabilityRunner(this._connectionManager, this._logManager);
if (!applicableRunner.validateSupported()) {
this._logManager.logMessage('Applicability runner could not find binary to run', 'WARN');
return {} as ApplicabilityScanResponse;
}
let skipFiles: string[] = AnalyzerUtils.getAnalyzerManagerExcludePattern(Configuration.getScanExcludePattern());
this._logManager.logMessage(
"Scanning directory '" + directory + "' for CVE issues: " + Array.from(cveToRun.values()) + '. Skipping files: ' + skipFiles,
'DEBUG'
);
return await applicableRunner.scan(directory, checkCancel, cveToRun, skipFiles);
}

/**
* Scan directory for 'Infrastructure As Code' (Iac) issues.
* @param directory - the directory that will be scan
* @param checkCancel - check if should cancel
* @returns the Iac scan response
*/
public async scanIac(directory: string, checkCancel: () => void): Promise<IacScanResponse> {
let iacRunner: IacRunner = new IacRunner(this._connectionManager, this.logManager);
if (!iacRunner.validateSupported()) {
this._logManager.logMessage('Iac runner could not find binary to run', 'WARN');
return {} as IacScanResponse;
}
let skipFiles: string[] = AnalyzerUtils.getAnalyzerManagerExcludePattern(Configuration.getScanExcludePattern());
this._logManager.logMessage("Scanning directory '" + directory + "', for Iac issues. Skipping files: " + skipFiles, 'DEBUG');
return await iacRunner.scan(directory, checkCancel, skipFiles);
}
/**
* Scan directory for secrets issues.
* @param directory - the directory that will be scan
* @param checkCancel - check if should cancel
* @returns the Secrets scan response
*/
public async scanSecrets(directory: string, checkCancel: () => void): Promise<SecretsScanResponse> {
let secretsRunner: SecretsRunner = new SecretsRunner(this._connectionManager, this.logManager);
if (!secretsRunner.validateSupported()) {
this._logManager.logMessage('Secrets runner could not find binary to run', 'WARN');
return {} as SecretsScanResponse;
}
let skipFiles: string[] = AnalyzerUtils.getAnalyzerManagerExcludePattern(Configuration.getScanExcludePattern());
this._logManager.logMessage("Scanning directory '" + directory + "', for Secrets issues. Skipping files: " + skipFiles, 'DEBUG');
return await secretsRunner.scan(directory, checkCancel, skipFiles);
}

/**
* Scan for Eos issues.
* @param checkCancel - check if should cancel
* @param requests - the Eos requests to run
* @returns the scan response
*/
public async scanEos(checkCancel: () => void, runDirectory?: string, ...requests: EosScanRequest[]): Promise<EosScanResponse> {
let eosRunner: EosRunner = new EosRunner(this._connectionManager, this._logManager, undefined, undefined, runDirectory);
if (!eosRunner.validateSupported()) {
this._logManager.logMessage('Eos runner could not find binary to run', 'WARN');
return {} as EosScanResponse;
}
if (requests.length === 0) {
this._logManager.logMessage('Eos runner must receive at least one request to run', 'ERR');
return {} as EosScanResponse;
}
let skipFiles: string[] = AnalyzerUtils.getAnalyzerManagerExcludePattern(Configuration.getScanExcludePattern());
this._logManager.logMessage(
'Scanning for Eos issues in ' +
requests.map(request => `(Language '${request.language}', roots: [${request.roots.join()}])`) +
'. Skipping files: ' +
skipFiles,
'DEBUG'
);
return eosRunner.scan(checkCancel, skipFiles, ...requests);
}
}
2 changes: 1 addition & 1 deletion src/main/scanLogic/scanRunners/analyzerModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface AnalyzerRequest {
export enum ScanType {
ContextualAnalysis = 'analyze-applicability',
yahavi marked this conversation as resolved.
Show resolved Hide resolved
Iac = 'iac-scan-modules',
Eos = 'analyze-codebase',
Sast = 'sast',
Secrets = 'secrets-scan'
}

Expand Down
Loading
Loading