From 9c3bca105dc17ec405a319cc7558f43cb03f1bd9 Mon Sep 17 00:00:00 2001 From: barv Date: Tue, 10 Sep 2024 15:37:48 +0300 Subject: [PATCH 1/7] token validation feature in ide --- package.json | 5 +++ src/main/connect/connectionManager.ts | 15 +++++++++ .../scanLogic/scanRunners/analyzerManager.ts | 31 +++++++++++++++++++ .../scanLogic/scanRunners/analyzerModels.ts | 3 ++ src/main/scanLogic/scanRunners/secretsScan.ts | 3 ++ .../codeFileTree/secretsTreeNode.ts | 16 +++++++++- .../treeDataProviders/utils/analyzerUtils.ts | 8 ++++- src/main/types/tokenStatus.ts | 8 +++++ src/main/utils/configuration.ts | 7 +++++ .../secretsScan/analyzerResponse.json | 27 +++++++++++++++- .../secretsScan/applicable_base64.js | 5 ++- .../secretsScan/expectedScanResponse.json | 19 ++++++++++++ src/test/tests/integration/secrets.test.ts | 4 +++ src/test/tests/secretsScan.test.ts | 4 ++- src/test/tests/utils/testAnalyzer.test.ts | 16 ++++++++++ src/test/tests/utils/testIntegration.test.ts | 31 +++++++++++++++++++ 16 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 src/main/types/tokenStatus.ts diff --git a/package.json b/package.json index 0316b4ee5..3b8feb32a 100644 --- a/package.json +++ b/package.json @@ -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." + }, "jfrog.externalResourcesRepository": { "type": "string", "scope": "application", diff --git a/src/main/connect/connectionManager.ts b/src/main/connect/connectionManager.ts index 079849b3b..dfad4ffb9 100644 --- a/src/main/connect/connectionManager.ts +++ b/src/main/connect/connectionManager.ts @@ -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'; export enum LoginStatus { Success = 'SUCCESS', @@ -959,4 +960,18 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable } this._logManager.logMessage(usagePrefix + 'Usage report sent successfully.', 'DEBUG'); } + + public async isTokenValidationPlatformEnabled(): Promise { + try { + let response: IJasConfig = await this.createJfrogClient() + .xray() + .jasconfig() + .getJasConfig(); + this._logManager.logMessage('Successfully got token validation from platform', 'DEBUG'); + return response.enable_token_validation_scanning; + } catch (error) { + this._logManager.logMessage('Failed getting token validation from platform', 'DEBUG'); + return false; + } + } } diff --git a/src/main/scanLogic/scanRunners/analyzerManager.ts b/src/main/scanLogic/scanRunners/analyzerManager.ts index 5135b8333..9108d7c08 100644 --- a/src/main/scanLogic/scanRunners/analyzerManager.ts +++ b/src/main/scanLogic/scanRunners/analyzerManager.ts @@ -10,6 +10,8 @@ import { Configuration } from '../../utils/configuration'; import { Translators } from '../../utils/translators'; import { BinaryEnvParams } from './jasRunner'; import { LogUtils } from '../../log/logUtils'; +import { DYNAMIC_TOKEN_VALIDATION_MIN_XRAY_VERSION } from './secretsScan'; +import * as semver from 'semver'; /** * Analyzer manager is responsible for running the analyzer on the workspace. @@ -24,6 +26,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'; @@ -148,6 +151,33 @@ export class AnalyzerManager { }; } + private isTokenValidationEnabled(): string { + 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 response: Promise = this._connectionManager.isTokenValidationPlatformEnabled(); + let tokenValidation: boolean = false; + response.then(res => { + tokenValidation = res; + }); + if (tokenValidation || process.env.JF_VALIDATE_SECRETS) { + return 'true'; + } + + return 'false'; + } + private populateOptionalInformation(binaryVars: NodeJS.ProcessEnv, params?: BinaryEnvParams) { // Optional proxy information - environment variable let proxyHttpUrl: string | undefined = process.env['HTTP_PROXY']; @@ -160,6 +190,7 @@ export class AnalyzerManager { proxyHttpUrl = 'http://' + proxyUrl; proxyHttpsUrl = 'https://' + proxyUrl; } + binaryVars[AnalyzerManager.JF_VALIDATE_SECRETS] = this.isTokenValidationEnabled(); if (proxyHttpUrl) { binaryVars[AnalyzerManager.ENV_HTTP_PROXY] = this.addOptionalProxyAuthInformation(proxyHttpUrl); } diff --git a/src/main/scanLogic/scanRunners/analyzerModels.ts b/src/main/scanLogic/scanRunners/analyzerModels.ts index b326d9f9d..c3881f08f 100644 --- a/src/main/scanLogic/scanRunners/analyzerModels.ts +++ b/src/main/scanLogic/scanRunners/analyzerModels.ts @@ -54,6 +54,7 @@ export interface AnalyzeIssue { level?: AnalyzerManagerSeverityLevel; suppressions?: AnalyzeSuppression[]; codeFlows?: CodeFlow[]; + properties?: { [key: string]: string }; } export interface AnalyzeSuppression { @@ -96,6 +97,8 @@ export interface FileRegion { startColumn: number; endColumn: number; snippet?: ResultContent; + tokenValidation?: string; + metadata?: string; } export interface ResultContent { diff --git a/src/main/scanLogic/scanRunners/secretsScan.ts b/src/main/scanLogic/scanRunners/secretsScan.ts index 9c44b6349..1c104f86e 100644 --- a/src/main/scanLogic/scanRunners/secretsScan.ts +++ b/src/main/scanLogic/scanRunners/secretsScan.ts @@ -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'; @@ -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; diff --git a/src/main/treeDataProviders/issuesTree/codeFileTree/secretsTreeNode.ts b/src/main/treeDataProviders/issuesTree/codeFileTree/secretsTreeNode.ts index 5a28cb212..8b84d7cfd 100644 --- a/src/main/treeDataProviders/issuesTree/codeFileTree/secretsTreeNode.ts +++ b/src/main/treeDataProviders/issuesTree/codeFileTree/secretsTreeNode.ts @@ -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( @@ -25,6 +27,8 @@ export class SecretTreeNode extends CodeIssueTreeNode { issue.severity, issue.ruleName ); + this._tokenValidation = location.tokenValidation; + this._metadata = location.metadata; this._snippet = location.snippet?.text; this._fullDescription = issue.fullDescription; } @@ -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; } @@ -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; } } diff --git a/src/main/treeDataProviders/utils/analyzerUtils.ts b/src/main/treeDataProviders/utils/analyzerUtils.ts index c95f5b8d0..af4c2f711 100644 --- a/src/main/treeDataProviders/utils/analyzerUtils.ts +++ b/src/main/treeDataProviders/utils/analyzerUtils.ts @@ -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; @@ -94,7 +95,12 @@ 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; + newLocation.tokenValidation = analyzeIssue.properties?.tokenValidation + ? (analyzeIssue.properties.tokenValidation.trim() as keyof typeof TokenStatus) + : ''; + newLocation.metadata = analyzeIssue.properties?.metadata ? analyzeIssue.properties.metadata.trim() : ''; + fileIssue.locations.push(newLocation); }); } diff --git a/src/main/types/tokenStatus.ts b/src/main/types/tokenStatus.ts new file mode 100644 index 000000000..acbff31d2 --- /dev/null +++ b/src/main/types/tokenStatus.ts @@ -0,0 +1,8 @@ +export enum TokenStatus { + 'Active', + 'Unsupported', + 'Unavailable', + 'Inactive', + 'Not a token', + '' +} diff --git a/src/main/utils/configuration.ts b/src/main/utils/configuration.ts index 53b401c96..68b0aedd9 100644 --- a/src/main/utils/configuration.ts +++ b/src/main/utils/configuration.ts @@ -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. */ diff --git a/src/test/resources/secretsScan/analyzerResponse.json b/src/test/resources/secretsScan/analyzerResponse.json index ec6d193de..b05c4ec29 100644 --- a/src/test/resources/secretsScan/analyzerResponse.json +++ b/src/test/resources/secretsScan/analyzerResponse.json @@ -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" -} \ No newline at end of file +} diff --git a/src/test/resources/secretsScan/applicable_base64.js b/src/test/resources/secretsScan/applicable_base64.js index 4ec6fa06e..9cb9c888d 100644 --- a/src/test/resources/secretsScan/applicable_base64.js +++ b/src/test/resources/secretsScan/applicable_base64.js @@ -1 +1,4 @@ -const api_key = "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe" \ No newline at end of file +// 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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" \ No newline at end of file diff --git a/src/test/resources/secretsScan/expectedScanResponse.json b/src/test/resources/secretsScan/expectedScanResponse.json index 792dc40ed..238dd6624 100644 --- a/src/test/resources/secretsScan/expectedScanResponse.json +++ b/src/test/resources/secretsScan/expectedScanResponse.json @@ -19,6 +19,25 @@ "startLine": 1 } ] + }, + { + "ruleId": "REQ.SECRET.KEYS", + "severity": 10, + "ruleName": "Secret keys were found", + "fullDescription": "Storing hardcoded secrets in your source code or binary artifact could lead to several risks.\n\nIf the secret is associated with a wide scope of privileges, attackers could extract it from the source code or binary artifact and use it maliciously to attack many targets. For example, if the hardcoded password gives high-privilege access to an AWS account, the attackers may be able to query/modify company-wide sensitive data without per-user authentication.\n\n## Best practices\n\nUse safe storage when storing high-privilege secrets such as passwords and tokens, for example -\n\n* ### Environment Variables\n\nEnvironment variables are set outside of the application code, and can be dynamically passed to the application only when needed, for example -\n`SECRET_VAR=MySecret ./my_application`\nThis way, `MySecret` does not have to be hardcoded into `my_application`.\n\nNote that if your entire binary artifact is published (ex. a Docker container published to Docker Hub), the value for the environment variable must not be stored in the artifact itself (ex. inside the `Dockerfile` or one of the container's files) but rather must be passed dynamically, for example in the `docker run` call as an argument.\n\n* ### Secret management services\n\nExternal vendors offer cloud-based secret management services, that provide proper access control to each secret. The given access to each secret can be dynamically modified or even revoked. Some examples include -\n\n* [Hashicorp Vault](https://www.vaultproject.io)\n* [AWS KMS](https://aws.amazon.com/kms) (Key Management Service)\n* [Google Cloud KMS](https://cloud.google.com/security-key-management)\n\n## Least-privilege principle\n\nStoring a secret in a hardcoded manner can be made safer, by making sure the secret grants the least amount of privilege as needed by the application.\nFor example - if the application needs to read a specific table from a specific database, and the secret grants access to perform this operation **only** (meaning - no access to other tables, no write access at all) then the damage from any secret leaks is mitigated.\nThat being said, it is still not recommended to store secrets in a hardcoded manner, since this type of storage does not offer any way to revoke or moderate the usage of the secret.\n", + "locations": [ + { + "endColumn": 60, + "endLine": 2, + "snippet": { + "text": "token_key = \"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\"" + }, + "startColumn": 20, + "startLine": 2, + "tokenValidation": "Inactive", + "metadata": "" + } + ] } ] } diff --git a/src/test/tests/integration/secrets.test.ts b/src/test/tests/integration/secrets.test.ts index a1444a539..69065392d 100644 --- a/src/test/tests/integration/secrets.test.ts +++ b/src/test/tests/integration/secrets.test.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import { AnalyzeScanRequest } from '../../../main/scanLogic/scanRunners/analyzerModels'; import { SecretsRunner, SecretsScanResponse } from '../../../main/scanLogic/scanRunners/secretsScan'; import { + assertIssuesTokenValidationExist, AnalyzerManagerIntegrationEnv, assertFileIssuesExist, assertIssuesExist, @@ -87,6 +88,9 @@ describe('Secrets Scan Integration Tests', async () => { it('Check severity', () => assertIssuesSeverityExist(directoryToScan, response.filesWithIssues, expectedContent.filesWithIssues)); + it('Check token validation', () => + assertIssuesTokenValidationExist(directoryToScan, response.filesWithIssues, expectedContent.filesWithIssues)); + it('Check snippet', () => assertIssuesLocationSnippetsExist(directoryToScan, response.filesWithIssues, expectedContent.filesWithIssues)); }); diff --git a/src/test/tests/secretsScan.test.ts b/src/test/tests/secretsScan.test.ts index 463cf6a02..3173f05a4 100644 --- a/src/test/tests/secretsScan.test.ts +++ b/src/test/tests/secretsScan.test.ts @@ -13,6 +13,7 @@ import { AnalyzerUtils, FileWithSecurityIssues } from '../../main/treeDataProvid import { ScanResults } from '../../main/types/workspaceIssuesDetails'; import { AppsConfigModule } from '../../main/utils/jfrogAppsConfig/jfrogAppsConfig'; import { + assertTokenValidationResult, assertFileNodesCreated, assertIssueNodesCreated, assertIssuesFullDescription, @@ -85,7 +86,7 @@ describe('Secrets Scan Tests', () => { }); it('Check issue count returned from method', () => { - assert.equal(populatedIssues, 3); + assert.equal(populatedIssues, 4); }); it('Check timestamp transferred from data to node', () => { @@ -106,6 +107,7 @@ describe('Secrets Scan Tests', () => { it('Check number of file nodes populated as root children', () => assertSameNumberOfFileNodes(testRoot, expectedFilesWithIssues)); describe('Issues populated as nodes', () => { + it('Check token validation', () => assertTokenValidationResult(testRoot, expectedFilesWithIssues, getTestIssueNode)); it('Check number of issues populated in file', () => assertSameNumberOfIssueNodes(testRoot, expectedFilesWithIssues)); it('Check issue nodes created in the file node', () => assertIssueNodesCreated(testRoot, expectedFilesWithIssues, getTestIssueNode)); diff --git a/src/test/tests/utils/testAnalyzer.test.ts b/src/test/tests/utils/testAnalyzer.test.ts index 9d67b5828..63f2e653a 100644 --- a/src/test/tests/utils/testAnalyzer.test.ts +++ b/src/test/tests/utils/testAnalyzer.test.ts @@ -69,6 +69,22 @@ export function findLocationNode(location: FileRegion, fileNode: CodeFileTreeNod ); } +export function assertTokenValidationResult( + testRoot: IssuesRootTreeNode, + expectedFilesWithIssues: FileWithSecurityIssues[], + getTestIssueNode: (fileNode: CodeFileTreeNode, location: FileRegion) => SecretTreeNode +) { + expectedFilesWithIssues.forEach((expectedFileIssues: FileWithSecurityIssues) => { + let fileNode: CodeFileTreeNode = getTestCodeFileNode(testRoot, expectedFileIssues.full_path); + expectedFileIssues.issues.forEach((expectedIssues: SecurityIssue) => { + expectedIssues.locations.forEach((expectedLocation: FileRegion) => { + assert.deepEqual(getTestIssueNode(fileNode, expectedLocation).metadata, expectedLocation.metadata); + assert.deepEqual(getTestIssueNode(fileNode, expectedLocation).tokenValidation, expectedLocation.tokenValidation); + }); + }); + }); +} + export function assertFileNodesCreated(testRoot: IssuesRootTreeNode, expectedFilesWithIssues: FileWithSecurityIssues[]) { expectedFilesWithIssues.forEach((fileIssues: FileWithSecurityIssues) => { assert.isDefined(getTestCodeFileNode(testRoot, fileIssues.full_path)); diff --git a/src/test/tests/utils/testIntegration.test.ts b/src/test/tests/utils/testIntegration.test.ts index 0f51d4033..b8fc60c82 100644 --- a/src/test/tests/utils/testIntegration.test.ts +++ b/src/test/tests/utils/testIntegration.test.ts @@ -214,6 +214,37 @@ export function assertIssuesSeverityExist( }); } +export function assertIssuesTokenValidationExist( + testDataRoot: string, + responseFilesWithIssues: FileWithSecurityIssues[], + expectedFilesWithIssues: FileWithSecurityIssues[] +) { + expectedFilesWithIssues.forEach((expectedFileWithIssues: FileWithSecurityIssues) => { + expectedFileWithIssues.issues.forEach((expectedIssues: SecurityIssue) => { + expectedIssues.locations.forEach((expectedLocation: FileRegion) => { + assert.deepEqual( + getTestLocation( + path.join(testDataRoot, expectedFileWithIssues.full_path), + responseFilesWithIssues, + expectedIssues.ruleId, + expectedLocation + ).tokenValidation, + expectedLocation.tokenValidation + ); + assert.deepEqual( + getTestLocation( + path.join(testDataRoot, expectedFileWithIssues.full_path), + responseFilesWithIssues, + expectedIssues.ruleId, + expectedLocation + ).metadata, + expectedLocation.metadata + ); + }); + }); + }); +} + export function assertIssuesLocationSnippetsExist( testDataRoot: string, responseFilesWithIssues: FileWithSecurityIssues[], From 357af5caf4e9bfefa9bedf36bdb1047b6a8fc340 Mon Sep 17 00:00:00 2001 From: barv Date: Wed, 18 Sep 2024 12:29:35 +0300 Subject: [PATCH 2/7] pr fixes --- src/main/connect/connectionManager.ts | 2 +- src/main/scanLogic/scanRunners/analyzerManager.ts | 2 +- src/main/scanLogic/scanRunners/analyzerModels.ts | 3 +-- .../issuesTree/codeFileTree/secretsTreeNode.ts | 4 ++-- src/main/treeDataProviders/utils/analyzerUtils.ts | 10 ++++++---- src/test/tests/utils/testAnalyzer.test.ts | 4 ++-- src/test/tests/utils/testIntegration.test.ts | 8 ++++---- 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/connect/connectionManager.ts b/src/main/connect/connectionManager.ts index dfad4ffb9..f1e6476b4 100644 --- a/src/main/connect/connectionManager.ts +++ b/src/main/connect/connectionManager.ts @@ -967,7 +967,7 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable .xray() .jasconfig() .getJasConfig(); - this._logManager.logMessage('Successfully got token validation from platform', 'DEBUG'); + this._logManager.logMessage('Got token validation value:' + response.enable_token_validation_scanning +' from platform', 'DEBUG'); return response.enable_token_validation_scanning; } catch (error) { this._logManager.logMessage('Failed getting token validation from platform', 'DEBUG'); diff --git a/src/main/scanLogic/scanRunners/analyzerManager.ts b/src/main/scanLogic/scanRunners/analyzerManager.ts index 9108d7c08..12d5e1678 100644 --- a/src/main/scanLogic/scanRunners/analyzerManager.ts +++ b/src/main/scanLogic/scanRunners/analyzerManager.ts @@ -19,7 +19,7 @@ import * as semver from 'semver'; 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.8.14'; + public static readonly ANALYZER_MANAGER_VERSION: string = '1.8.15'; 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/analyzerModels.ts b/src/main/scanLogic/scanRunners/analyzerModels.ts index c3881f08f..26a3eec87 100644 --- a/src/main/scanLogic/scanRunners/analyzerModels.ts +++ b/src/main/scanLogic/scanRunners/analyzerModels.ts @@ -97,8 +97,7 @@ export interface FileRegion { startColumn: number; endColumn: number; snippet?: ResultContent; - tokenValidation?: string; - metadata?: string; + properties: { [key: string]: string }; } export interface ResultContent { diff --git a/src/main/treeDataProviders/issuesTree/codeFileTree/secretsTreeNode.ts b/src/main/treeDataProviders/issuesTree/codeFileTree/secretsTreeNode.ts index 8b84d7cfd..3481c556d 100644 --- a/src/main/treeDataProviders/issuesTree/codeFileTree/secretsTreeNode.ts +++ b/src/main/treeDataProviders/issuesTree/codeFileTree/secretsTreeNode.ts @@ -27,8 +27,8 @@ export class SecretTreeNode extends CodeIssueTreeNode { issue.severity, issue.ruleName ); - this._tokenValidation = location.tokenValidation; - this._metadata = location.metadata; + this._tokenValidation = location.properties?.tokenValidation; + this._metadata = location.properties?.metadata; this._snippet = location.snippet?.text; this._fullDescription = issue.fullDescription; } diff --git a/src/main/treeDataProviders/utils/analyzerUtils.ts b/src/main/treeDataProviders/utils/analyzerUtils.ts index af4c2f711..fdf693c4d 100644 --- a/src/main/treeDataProviders/utils/analyzerUtils.ts +++ b/src/main/treeDataProviders/utils/analyzerUtils.ts @@ -96,10 +96,12 @@ export class AnalyzerUtils { ); let fileIssue: SecurityIssue = AnalyzerUtils.getOrCreateSecurityIssue(fileWithIssues, analyzeIssue, fullDescription); let newLocation: FileRegion = location.physicalLocation.region; - newLocation.tokenValidation = analyzeIssue.properties?.tokenValidation - ? (analyzeIssue.properties.tokenValidation.trim() as keyof typeof TokenStatus) - : ''; - newLocation.metadata = analyzeIssue.properties?.metadata ? analyzeIssue.properties.metadata.trim() : ''; + 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); }); } diff --git a/src/test/tests/utils/testAnalyzer.test.ts b/src/test/tests/utils/testAnalyzer.test.ts index 63f2e653a..eae78a4fc 100644 --- a/src/test/tests/utils/testAnalyzer.test.ts +++ b/src/test/tests/utils/testAnalyzer.test.ts @@ -78,8 +78,8 @@ export function assertTokenValidationResult( let fileNode: CodeFileTreeNode = getTestCodeFileNode(testRoot, expectedFileIssues.full_path); expectedFileIssues.issues.forEach((expectedIssues: SecurityIssue) => { expectedIssues.locations.forEach((expectedLocation: FileRegion) => { - assert.deepEqual(getTestIssueNode(fileNode, expectedLocation).metadata, expectedLocation.metadata); - assert.deepEqual(getTestIssueNode(fileNode, expectedLocation).tokenValidation, expectedLocation.tokenValidation); + assert.deepEqual(getTestIssueNode(fileNode, expectedLocation).metadata, expectedLocation.properties?.metadata); + assert.deepEqual(getTestIssueNode(fileNode, expectedLocation).tokenValidation, expectedLocation.properties?.tokenValidation); }); }); }); diff --git a/src/test/tests/utils/testIntegration.test.ts b/src/test/tests/utils/testIntegration.test.ts index b8fc60c82..3ac88ffa4 100644 --- a/src/test/tests/utils/testIntegration.test.ts +++ b/src/test/tests/utils/testIntegration.test.ts @@ -228,8 +228,8 @@ export function assertIssuesTokenValidationExist( responseFilesWithIssues, expectedIssues.ruleId, expectedLocation - ).tokenValidation, - expectedLocation.tokenValidation + ).properties?.tokenValidation, + expectedLocation.properties?.tokenValidation ); assert.deepEqual( getTestLocation( @@ -237,8 +237,8 @@ export function assertIssuesTokenValidationExist( responseFilesWithIssues, expectedIssues.ruleId, expectedLocation - ).metadata, - expectedLocation.metadata + ).properties?.metadata, + expectedLocation.properties?.metadata ); }); }); From d8d06037c5f9df4da346047ec855a3e072735901 Mon Sep 17 00:00:00 2001 From: barv Date: Mon, 23 Sep 2024 17:23:26 +0300 Subject: [PATCH 3/7] cr fixes --- src/main/scanLogic/scanManager.ts | 7 +++ .../scanLogic/scanRunners/analyzerManager.ts | 35 +++------------ src/main/scanLogic/scanRunners/jasRunner.ts | 1 + .../sourceCodeScan/supportedScans.ts | 44 +++++++++++++++++++ 4 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/main/scanLogic/scanManager.ts b/src/main/scanLogic/scanManager.ts index 172ce568a..c6c3de61b 100644 --- a/src/main/scanLogic/scanManager.ts +++ b/src/main/scanLogic/scanManager.ts @@ -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( diff --git a/src/main/scanLogic/scanRunners/analyzerManager.ts b/src/main/scanLogic/scanRunners/analyzerManager.ts index 12d5e1678..1bf383daf 100644 --- a/src/main/scanLogic/scanRunners/analyzerManager.ts +++ b/src/main/scanLogic/scanRunners/analyzerManager.ts @@ -10,8 +10,6 @@ import { Configuration } from '../../utils/configuration'; import { Translators } from '../../utils/translators'; import { BinaryEnvParams } from './jasRunner'; import { LogUtils } from '../../log/logUtils'; -import { DYNAMIC_TOKEN_VALIDATION_MIN_XRAY_VERSION } from './secretsScan'; -import * as semver from 'semver'; /** * Analyzer manager is responsible for running the analyzer on the workspace. @@ -151,34 +149,8 @@ export class AnalyzerManager { }; } - private isTokenValidationEnabled(): string { - 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 response: Promise = this._connectionManager.isTokenValidationPlatformEnabled(); - let tokenValidation: boolean = false; - response.then(res => { - tokenValidation = res; - }); - if (tokenValidation || process.env.JF_VALIDATE_SECRETS) { - return 'true'; - } - return 'false'; - } - - 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']; @@ -190,7 +162,10 @@ export class AnalyzerManager { proxyHttpUrl = 'http://' + proxyUrl; proxyHttpsUrl = 'https://' + proxyUrl; } - binaryVars[AnalyzerManager.JF_VALIDATE_SECRETS] = this.isTokenValidationEnabled(); + + if (params?.tokenValidation && params.tokenValidation == true) { + binaryVars[AnalyzerManager.JF_VALIDATE_SECRETS] = "true" + } if (proxyHttpUrl) { binaryVars[AnalyzerManager.ENV_HTTP_PROXY] = this.addOptionalProxyAuthInformation(proxyHttpUrl); } diff --git a/src/main/scanLogic/scanRunners/jasRunner.ts b/src/main/scanLogic/scanRunners/jasRunner.ts index fb2f6d6c7..f873c03ba 100644 --- a/src/main/scanLogic/scanRunners/jasRunner.ts +++ b/src/main/scanLogic/scanRunners/jasRunner.ts @@ -40,6 +40,7 @@ interface RunRequest { export interface BinaryEnvParams { executionLogDirectory?: string; msi?: string; + tokenValidation?: boolean; } /** diff --git a/src/main/scanLogic/sourceCodeScan/supportedScans.ts b/src/main/scanLogic/sourceCodeScan/supportedScans.ts index 49dd6070c..99e6f810d 100644 --- a/src/main/scanLogic/sourceCodeScan/supportedScans.ts +++ b/src/main/scanLogic/sourceCodeScan/supportedScans.ts @@ -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; } @@ -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; } @@ -102,4 +120,30 @@ export class SupportedScans { public async isSastSupported(): Promise { return await ConnectionUtils.testXrayEntitlementForFeature(this._connectionManager.createJfrogClient(), EntitlementScanFeature.Sast); } + + /** + * Check if token validation scan is enabled + */ + public async isTokenValidationEnabled(): Promise { + 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; + } } From 76802c107c5addad13fc4bffbf8c2108117fd9fd Mon Sep 17 00:00:00 2001 From: barv Date: Sun, 10 Nov 2024 11:43:49 +0200 Subject: [PATCH 4/7] jfrog-client-js update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3b8feb32a..9713514ad 100644 --- a/package.json +++ b/package.json @@ -332,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", From d157c21c762f63793768d2503cf3a8ee7fbfd2b3 Mon Sep 17 00:00:00 2001 From: barv Date: Sun, 10 Nov 2024 12:18:20 +0200 Subject: [PATCH 5/7] fix package-lock --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 28e2203aa..ca477af16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,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", @@ -4333,9 +4333,9 @@ } }, "node_modules/jfrog-client-js": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jfrog-client-js/-/jfrog-client-js-2.8.0.tgz", - "integrity": "sha512-Q2gYG6vKKeKi6bu8crIRBTFz3AABzROg+CtxFqH5Z9u5yyD24bReUvjujfwxu3aYTq8VUdojVbHVg9A6jBw6mg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/jfrog-client-js/-/jfrog-client-js-2.9.0.tgz", + "integrity": "sha512-95344WnqWIlUQalcu+XxCazN3IGwGmTHVMggx1IzOkoxsI1FxszZiFCc+QZFRmJD6HmzOxajhiCG/CUspZsD0A==", "dependencies": { "axios": "~0.27.2", "axios-retry": "~3.2.5", From 9a4fb3a61adb947d300040bb9fd8bb87cac52143 Mon Sep 17 00:00:00 2001 From: barv Date: Tue, 12 Nov 2024 10:40:37 +0200 Subject: [PATCH 6/7] cr changes --- package-lock.json | 4 ++-- package.json | 2 +- src/main/connect/connectionManager.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index ca477af16..5fdf3c36a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jfrog-vscode-extension", - "version": "2.11.6", + "version": "2.11.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jfrog-vscode-extension", - "version": "2.11.6", + "version": "2.11.7", "license": "Apache-2.0", "dependencies": { "adm-zip": "~0.5.9", diff --git a/package.json b/package.json index 9713514ad..dd41286ec 100644 --- a/package.json +++ b/package.json @@ -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.6", + "version": "2.11.7", "license": "Apache-2.0", "icon": "resources/extensionIcon.png", "repository": { diff --git a/src/main/connect/connectionManager.ts b/src/main/connect/connectionManager.ts index f1e6476b4..252abc732 100644 --- a/src/main/connect/connectionManager.ts +++ b/src/main/connect/connectionManager.ts @@ -967,7 +967,7 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable .xray() .jasconfig() .getJasConfig(); - this._logManager.logMessage('Got token validation value:' + response.enable_token_validation_scanning +' from platform', 'DEBUG'); + this._logManager.logMessage(`Got token validation value: ${response.enable_token_validation_scanning} from platform`, 'DEBUG'); return response.enable_token_validation_scanning; } catch (error) { this._logManager.logMessage('Failed getting token validation from platform', 'DEBUG'); From adfb87a4ea247e878357886b021267d153f1451a Mon Sep 17 00:00:00 2001 From: barv Date: Tue, 12 Nov 2024 11:16:50 +0200 Subject: [PATCH 7/7] cr changes --- src/main/scanLogic/scanRunners/analyzerManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scanLogic/scanRunners/analyzerManager.ts b/src/main/scanLogic/scanRunners/analyzerManager.ts index 2c89fc7f2..1bb5b7fc8 100644 --- a/src/main/scanLogic/scanRunners/analyzerManager.ts +++ b/src/main/scanLogic/scanRunners/analyzerManager.ts @@ -163,7 +163,7 @@ export class AnalyzerManager { proxyHttpsUrl = 'https://' + proxyUrl; } - if (params?.tokenValidation && params.tokenValidation == true) { + if (params?.tokenValidation && params.tokenValidation === true) { binaryVars[AnalyzerManager.JF_VALIDATE_SECRETS] = "true" } if (proxyHttpUrl) {