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

token validation feature in ide #496

Merged
merged 10 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "jfrog-vscode-extension",
"displayName": "JFrog",
"description": "Security scanning for your Go, npm, Pypi, Maven and NuGet projects.",
"version": "2.11.7",
"version": "2.11.6",
eyalk007 marked this conversation as resolved.
Show resolved Hide resolved
"license": "Apache-2.0",
"icon": "resources/extensionIcon.png",
"repository": {
Expand Down Expand Up @@ -107,6 +107,11 @@
"scope": "application",
"markdownDescription": "Exclude development dependencies during the scan. Currently, only npm is supported."
},
"jfrog.tokenValidation": {
"type": "boolean",
"scope": "application",
"markdownDescription": "Enable token validation on secret scanning."
},
eyalk007 marked this conversation as resolved.
Show resolved Hide resolved
"jfrog.externalResourcesRepository": {
"type": "string",
"scope": "application",
Expand Down Expand Up @@ -327,7 +332,7 @@
"dependencies": {
"adm-zip": "~0.5.9",
"fs-extra": "~10.1.0",
"jfrog-client-js": "^2.8.0",
"jfrog-client-js": "^2.9.0",
"jfrog-ide-webview": "https://releases.jfrog.io/artifactory/ide-webview-npm/jfrog-ide-webview/-/jfrog-ide-webview-0.2.14.tgz",
"js-yaml": "^4.1.0",
"json2csv": "~5.0.7",
Expand Down
15 changes: 15 additions & 0 deletions src/main/connect/connectionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { LogLevel, LogManager } from '../log/logManager';
import { ScanUtils } from '../utils/scanUtils';
import { ConnectionUtils } from './connectionUtils';
import { XrayScanClient } from 'jfrog-client-js/dist/src/Xray/XrayScanClient';
import { IJasConfig } from 'jfrog-client-js/dist/model/Xray/JasConfig/JasConfig';
attiasas marked this conversation as resolved.
Show resolved Hide resolved

export enum LoginStatus {
Success = 'SUCCESS',
Expand Down Expand Up @@ -959,4 +960,18 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
}
this._logManager.logMessage(usagePrefix + 'Usage report sent successfully.', 'DEBUG');
}

public async isTokenValidationPlatformEnabled(): Promise<boolean> {
try {
let response: IJasConfig = await this.createJfrogClient()
.xray()
.jasconfig()
.getJasConfig();
this._logManager.logMessage('Got token validation value:' + response.enable_token_validation_scanning +' from platform', 'DEBUG');
eyalk007 marked this conversation as resolved.
Show resolved Hide resolved
return response.enable_token_validation_scanning;
} catch (error) {
this._logManager.logMessage('Failed getting token validation from platform', 'DEBUG');
return false;
}
}
}
7 changes: 7 additions & 0 deletions src/main/scanLogic/scanManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ export class ScanManager implements ExtensionComponent {
if (scanDetails.multiScanId) {
params = { msi: scanDetails.multiScanId };
}
if (scanDetails.jasRunnerFactory.supportedScans.tokenValidation) {
if (params) {
params.tokenValidation = scanDetails.jasRunnerFactory.supportedScans.tokenValidation
} else {
params = {tokenValidation: scanDetails.jasRunnerFactory.supportedScans.tokenValidation}
}
}
for (const runner of jasRunners) {
if (runner.shouldRun()) {
scansPromises.push(
Expand Down
8 changes: 7 additions & 1 deletion src/main/scanLogic/scanRunners/analyzerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class AnalyzerManager {

private static readonly JFROG_RELEASES_URL: string = 'https://releases.jfrog.io';
public static readonly JF_RELEASES_REPO: string = 'JF_RELEASES_REPO';
public static readonly JF_VALIDATE_SECRETS: string = 'JF_VALIDATE_SECRETS';

public static readonly ENV_PLATFORM_URL: string = 'JF_PLATFORM_URL';
public static readonly ENV_TOKEN: string = 'JF_TOKEN';
Expand Down Expand Up @@ -148,7 +149,8 @@ export class AnalyzerManager {
};
}

private populateOptionalInformation(binaryVars: NodeJS.ProcessEnv, params?: BinaryEnvParams) {

private async populateOptionalInformation(binaryVars: NodeJS.ProcessEnv, params?: BinaryEnvParams) {
// Optional proxy information - environment variable
let proxyHttpUrl: string | undefined = process.env['HTTP_PROXY'];
let proxyHttpsUrl: string | undefined = process.env['HTTPS_PROXY'];
Expand All @@ -160,6 +162,10 @@ export class AnalyzerManager {
proxyHttpUrl = 'http://' + proxyUrl;
proxyHttpsUrl = 'https://' + proxyUrl;
}

if (params?.tokenValidation && params.tokenValidation == true) {
eyalk007 marked this conversation as resolved.
Show resolved Hide resolved
binaryVars[AnalyzerManager.JF_VALIDATE_SECRETS] = "true"
}
if (proxyHttpUrl) {
binaryVars[AnalyzerManager.ENV_HTTP_PROXY] = this.addOptionalProxyAuthInformation(proxyHttpUrl);
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/scanLogic/scanRunners/analyzerModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export interface AnalyzeIssue {
level?: AnalyzerManagerSeverityLevel;
suppressions?: AnalyzeSuppression[];
codeFlows?: CodeFlow[];
properties?: { [key: string]: string };
}

export interface AnalyzeSuppression {
Expand Down Expand Up @@ -96,6 +97,7 @@ export interface FileRegion {
startColumn: number;
endColumn: number;
snippet?: ResultContent;
properties: { [key: string]: string };
}

export interface ResultContent {
Expand Down
1 change: 1 addition & 0 deletions src/main/scanLogic/scanRunners/jasRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ interface RunRequest {
export interface BinaryEnvParams {
executionLogDirectory?: string;
msi?: string;
tokenValidation?: boolean;
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/main/scanLogic/scanRunners/secretsScan.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as semver from 'semver';
import { ConnectionManager } from '../../connect/connectionManager';
import { LogManager } from '../../log/logManager';
import { IssuesRootTreeNode } from '../../treeDataProviders/issuesTree/issuesRootTreeNode';
Expand All @@ -9,6 +10,8 @@ import { AnalyzerManager } from './analyzerManager';
import { AnalyzeScanRequest, AnalyzerScanResponse, AnalyzerScanRun, ScanType } from './analyzerModels';
import { BinaryEnvParams, JasRunner, RunArgs } from './jasRunner';

export const DYNAMIC_TOKEN_VALIDATION_MIN_XRAY_VERSION: any = semver.coerce('3.101.0');

export interface SecretsScanResponse {
filesWithIssues: FileWithSecurityIssues[];
ignoreCount?: number;
Expand Down
44 changes: 44 additions & 0 deletions src/main/scanLogic/sourceCodeScan/supportedScans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,27 @@ import { ConnectionManager } from '../../connect/connectionManager';
import { ConnectionUtils, EntitlementScanFeature } from '../../connect/connectionUtils';
import { LogManager } from '../../log/logManager';
import { ScanUtils } from '../../utils/scanUtils';
import * as semver from 'semver';
import { DYNAMIC_TOKEN_VALIDATION_MIN_XRAY_VERSION } from '../scanRunners/secretsScan';
import { Configuration } from '../../utils/configuration';

export class SupportedScans {
private _applicability?: boolean;
private _sast?: boolean;
private _iac?: boolean;
private _secrets?: boolean;
private _tokenValidation?: boolean;
constructor(private _connectionManager: ConnectionManager, protected _logManager: LogManager) {}

get tokenValidation(): boolean | undefined {
return this._tokenValidation
}

public setTokenValidation(value: boolean| undefined): SupportedScans {
this._tokenValidation = value;
return this;
}

get applicability(): boolean | undefined {
return this._applicability;
}
Expand Down Expand Up @@ -72,6 +85,11 @@ export class SupportedScans {
.then(res => this.setSast(res))
.catch(err => ScanUtils.onScanError(err, this._logManager, true))
);
requests.push(
this.isTokenValidationEnabled()
.then(res => this.setTokenValidation(res))
.catch(err => ScanUtils.onScanError(err, this._logManager, true))
);
await Promise.all(requests);
return this;
}
Expand Down Expand Up @@ -102,4 +120,30 @@ export class SupportedScans {
public async isSastSupported(): Promise<boolean> {
return await ConnectionUtils.testXrayEntitlementForFeature(this._connectionManager.createJfrogClient(), EntitlementScanFeature.Sast);
}

/**
* Check if token validation scan is enabled
*/
public async isTokenValidationEnabled(): Promise<boolean> {
let xraySemver: semver.SemVer = new semver.SemVer(this._connectionManager.xrayVersion);
if (xraySemver.compare(DYNAMIC_TOKEN_VALIDATION_MIN_XRAY_VERSION) < 0) {
this._logManager.logMessage(
'You cannot use dynamic token validation feature on xray version ' +
this._connectionManager.xrayVersion +
' as it requires xray version ' +
DYNAMIC_TOKEN_VALIDATION_MIN_XRAY_VERSION,
'INFO'
);
return false;
}
if (Configuration.enableTokenValidation()) {
return true;
}
let tokenValidation: boolean = await this._connectionManager.isTokenValidationPlatformEnabled();
if (tokenValidation || process.env.JF_VALIDATE_SECRETS) {
return true;
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { SecurityIssue } from '../../utils/analyzerUtils';
export class SecretTreeNode extends CodeIssueTreeNode {
private _fullDescription?: string;
private _snippet?: string;
private _tokenValidation?: string;
private _metadata?: string;

constructor(issue: SecurityIssue, location: FileRegion, parent: CodeFileTreeNode) {
super(
Expand All @@ -25,6 +27,8 @@ export class SecretTreeNode extends CodeIssueTreeNode {
issue.severity,
issue.ruleName
);
this._tokenValidation = location.properties?.tokenValidation;
this._metadata = location.properties?.metadata;
this._snippet = location.snippet?.text;
this._fullDescription = issue.fullDescription;
}
Expand All @@ -33,6 +37,14 @@ export class SecretTreeNode extends CodeIssueTreeNode {
return this._snippet;
}

public get tokenValidation(): string | undefined {
return this._tokenValidation;
}

public get metadata(): string | undefined {
return this._metadata;
}

public get fullDescription(): string | undefined {
return this._fullDescription;
}
Expand All @@ -50,7 +62,9 @@ export class SecretTreeNode extends CodeIssueTreeNode {
endRow: this.regionWithIssue.end.line + 1,
endColumn: this.regionWithIssue.end.character + 1
} as IAnalysisStep,
description: this._fullDescription
description: this._fullDescription,
tokenValidation: this._tokenValidation,
metadata: this._metadata
} as ISecretsPage;
}
}
10 changes: 9 additions & 1 deletion src/main/treeDataProviders/utils/analyzerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ProjectDependencyTreeNode } from '../issuesTree/descriptorTree/projectD
import { FileTreeNode } from '../issuesTree/fileTreeNode';
import { IssueTreeNode } from '../issuesTree/issueTreeNode';
import { IssuesRootTreeNode } from '../issuesTree/issuesRootTreeNode';
import { TokenStatus } from '../../types/tokenStatus';

export interface FileWithSecurityIssues {
full_path: string;
Expand Down Expand Up @@ -94,7 +95,14 @@ export class AnalyzerUtils {
location.physicalLocation.artifactLocation.uri
);
let fileIssue: SecurityIssue = AnalyzerUtils.getOrCreateSecurityIssue(fileWithIssues, analyzeIssue, fullDescription);
fileIssue.locations.push(location.physicalLocation.region);
let newLocation: FileRegion = location.physicalLocation.region;
let properties: {[key: string]: string} = {
"tokenValidation": analyzeIssue.properties?.tokenValidation
? (analyzeIssue.properties.tokenValidation.trim() as keyof typeof TokenStatus) : '',
"metadata": analyzeIssue.properties?.metadata ? analyzeIssue.properties.metadata.trim() : ''
}
newLocation.properties = properties
fileIssue.locations.push(newLocation);
});
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/types/tokenStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum TokenStatus {
'Active',
'Unsupported',
'Unavailable',
'Inactive',
'Not a token',
''
}
7 changes: 7 additions & 0 deletions src/main/utils/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export class Configuration {
return vscode.workspace.getConfiguration(this.jfrogSectionConfigurationKey).get('xray.watchers');
}

/**
* Returns true to scan secrets with token validation enabled
*/
public static enableTokenValidation(): boolean | undefined {
return vscode.workspace.getConfiguration(this.jfrogSectionConfigurationKey).get('tokenValidation');
}

/**
* Return true if exclude dev dependencies option is checked on the jfrog extension configuration page.
*/
Expand Down
27 changes: 26 additions & 1 deletion src/test/resources/secretsScan/analyzerResponse.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,35 @@
}
],
"ruleId": "generic"
},
{
"message": {
"text": "Secret keys were found"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "file:///examples/secrets-demo/../applicable_secret.py"
},
"region": {
"endColumn": 132,
"endLine": 1,
"snippet": {
"text": "sometoken"
},
"startColumn": 12,
"startLine": 1
}
}
}
],
"ruleId": "generic",
"properties": {"tokenValidation": "Active", "metadata": "somemetadata"}
}
]
}
],
"version": "2.1.0",
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json"
}
}
5 changes: 4 additions & 1 deletion src/test/resources/secretsScan/applicable_base64.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
const api_key = "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe"
// eslint-disable-next-line @typescript-eslint/typedef
const api_key = "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe"
// eslint-disable-next-line @typescript-eslint/typedef
const token_key = "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"
Loading
Loading