Skip to content

Commit

Permalink
Add XSC analytics (#470)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas committed Apr 7, 2024
1 parent 3e581d0 commit ff3766c
Show file tree
Hide file tree
Showing 31 changed files with 2,149 additions and 1,784 deletions.
2,921 changes: 1,360 additions & 1,561 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 14 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@
"scope": "application",
"markdownDescription": "Add one or more Xray Watches, to reflect the security policies required by your organization.\n\nIf both “Project Key” and “Watches” are configured, VS Code will use the configured Watches, and not the Project Key to determine the policy for the security scanning."
},
"jfrog.reportAnalytics": {
"type": "boolean",
"default": true,
"scope": "application",
"markdownDescription": "Help us improve the JFrog extension by sending analytic data and errors to JFrog."
},
"jfrog.showAdvanceScanLog": {
"type": "boolean",
"default": false,
"scope": "application",
"markdownDescription": "Show the detailed advance scan logs at debug level."
},
"jfrog.logLevel": {
"type": "string",
"default": "info",
Expand Down Expand Up @@ -304,7 +316,7 @@
"dependencies": {
"adm-zip": "~0.5.9",
"fs-extra": "~10.1.0",
"jfrog-client-js": "^2.7.1",
"jfrog-client-js": "^2.8.0",
"jfrog-ide-webview": "https://releases.jfrog.io/artifactory/ide-webview-npm/jfrog-ide-webview/-/jfrog-ide-webview-0.2.13.tgz",
"js-yaml": "^4.1.0",
"json2csv": "~5.0.7",
Expand Down Expand Up @@ -332,6 +344,7 @@
"@types/vscode": "1.64.0",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"@vscode/vsce": "^2.21.0",
"chai": "^4.3.6",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.5.0",
Expand All @@ -344,7 +357,6 @@
"tmp": "^0.2.1",
"ts-loader": "^9.3.0",
"typescript": "^4.7.2",
"@vscode/vsce": "^2.21.0",
"vscode-test": "^1.6.1",
"webpack": "^5.76.0",
"webpack-cli": "^4.9.2"
Expand Down
10 changes: 9 additions & 1 deletion src/main/commands/commandManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,12 @@ export class CommandManager implements ExtensionComponent {
private async showConnectionStatus() {
if (await this._connectionManager.isSignedIn()) {
vscode.window.showInformationMessage(this.xrayConnectionDetails());
if (this._connectionManager.xscUrl && this._connectionManager.xscVersion !== '') {
// Optional for compatible platforms
vscode.window.showInformationMessage(this.xscConnectionDetails());
}
if (this._connectionManager.rtUrl) {
return vscode.window.showInformationMessage(this.artifactoryConnectionDetails());
vscode.window.showInformationMessage(this.artifactoryConnectionDetails());
}
return;
}
Expand All @@ -288,6 +292,10 @@ export class CommandManager implements ExtensionComponent {
return this.createServerDetailsMessage('Xray', this._connectionManager.xrayUrl, this._connectionManager.xrayVersion);
}

private xscConnectionDetails(): string {
return this.createServerDetailsMessage('Xray Source Control', this._connectionManager.xscUrl, this._connectionManager.xscVersion);
}

private artifactoryConnectionDetails(): string {
return this.createServerDetailsMessage('Artifactory', this._connectionManager.rtUrl, this._connectionManager.artifactoryVersion);
}
Expand Down
138 changes: 126 additions & 12 deletions src/main/connect/connectionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ import {
IGraphResponse,
IUsageFeature,
JfrogClient,
XrayScanProgress
ScanEvent,
StartScanRequest,
XrayScanProgress,
XscLogLevel
} from 'jfrog-client-js';
import * as semver from 'semver';
import * as vscode from 'vscode';
import { ContextKeys, SessionStatus } from '../constants/contextKeys';
import { ExtensionComponent } from '../extensionComponent';
import { LogManager } from '../log/logManager';
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';

export enum LoginStatus {
Success = 'SUCCESS',
Expand All @@ -35,6 +39,7 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
private static readonly XRAY_USERNAME_KEY: string = 'jfrog.xray.username';
private static readonly PLATFORM_URL_KEY: string = 'jfrog.xray.platformUrl';
private static readonly XRAY_URL_KEY: string = 'jfrog.xray.url';
private static readonly XSC_URL_KEY: string = 'jfrog.xsc.url';
private static readonly RT_URL_KEY: string = 'jfrog.rt.url';

// Service ID in the OS KeyStore to store and retrieve the password / access token
Expand All @@ -61,9 +66,11 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
private _username: string = '';
private _password: string = '';
private _xrayUrl: string = '';
private _xscUrl: string = '';
private _rtUrl: string = '';
private _url: string = '';
private _xrayVersion: string = '';
private _xscVersion: string = '';
private _artifactoryVersion: string = '';

constructor(protected _logManager: LogManager) {
Expand Down Expand Up @@ -206,6 +213,9 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
) {
return false;
}
if (this._url !== '') {
await ConnectionUtils.checkXscConnection(this._url, this._username, this._password, this._accessToken, prompt, this._logManager);
}
if (this.areCompleteCredentialsSet()) {
return await ConnectionUtils.checkArtifactoryConnection(
this._rtUrl,
Expand Down Expand Up @@ -329,6 +339,10 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
return this._xrayUrl;
}

public get xscUrl() {
return this._xscUrl;
}

public get rtUrl() {
return this._rtUrl;
}
Expand All @@ -349,10 +363,18 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
return this._xrayVersion;
}

public get xscVersion() {
return this._xscVersion;
}

public get artifactoryVersion() {
return this._artifactoryVersion;
}

public get logManager() {
return this._logManager;
}

/**
* Resolve Xray and JFrog platform URLs from the input url.
* If URL is <platform-url>, the derived Xray URL is <platform-url/xray>, and the Artifactory URL is <platform-url/artifactory>.
Expand All @@ -363,13 +385,15 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
if (await ConnectionUtils.isPlatformUrl(this._url, this._username, this._password, this._accessToken, this._logManager)) {
// _url is a platform URL
this._xrayUrl = this.getXrayUrlFromPlatform();
this._xscUrl = this.getXscUrlFromPlatform();
this._rtUrl = this.getRtUrlFromPlatform();
} else if (this._url) {
// _url is an Xray URL
this._xrayUrl = this._url;
if (this._url.endsWith('/xray') || this._url.endsWith('/xray/')) {
// Assuming platform URL was extracted. Checking against Artifactory.
this._url = this._url.substring(0, this._url.lastIndexOf('/xray'));
this._xscUrl = this.getXscUrlFromPlatform();
this._rtUrl = this.getRtUrlFromPlatform();
if (
!(await ConnectionUtils.validateArtifactoryConnection(
Expand All @@ -385,11 +409,21 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
}
} else {
this._url = '';
this._xscUrl = '';
this._rtUrl = '';
}
}
if (
this._url &&
!(await ConnectionUtils.validateXscConnection(this._url, this._username, this._password, this._accessToken, this._logManager))
) {
this._xscUrl = '';
}
this._logManager.logMessage('Resolved JFrog platform URL: ' + this._url, 'DEBUG');
this._logManager.logMessage('Resolved Xray URL: ' + this._xrayUrl, 'DEBUG');
if (this._xscUrl) {
this._logManager.logMessage('Resolved Xray Source Control URL: ' + this._xscUrl, 'DEBUG');
}
this._logManager.logMessage('Resolved Artifactory URL: ' + this._rtUrl, 'DEBUG');
}

Expand All @@ -406,6 +440,10 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
return this.getServiceUrlFromPlatform(this._url, 'xray');
}

private getXscUrlFromPlatform(): string {
return this.getServiceUrlFromPlatform(this._url, 'xsc');
}

public async tryCredentialsFromEnv(): Promise<LoginStatus> {
if (!(await this.getCredentialsFromEnv())) {
this.deleteCredentialsFromMemory();
Expand Down Expand Up @@ -564,6 +602,7 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
private async setUrlsFromFilesystem(): Promise<boolean> {
this._url = (await this._context.globalState.get(ConnectionManager.PLATFORM_URL_KEY)) || '';
this._xrayUrl = (await this._context.globalState.get(ConnectionManager.XRAY_URL_KEY)) || '';
this._xscUrl = (await this._context.globalState.get(ConnectionManager.XSC_URL_KEY)) || '';
this._rtUrl = (await this._context.globalState.get(ConnectionManager.RT_URL_KEY)) || '';
return !!this._url || !!this._xrayUrl;
}
Expand All @@ -576,6 +615,10 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
await this._context.globalState.update(ConnectionManager.XRAY_URL_KEY, this._xrayUrl);
}

private async storeXscUrl() {
await this._context.globalState.update(ConnectionManager.XSC_URL_KEY, this._xscUrl);
}

private async storeRtUrl() {
await this._context.globalState.update(ConnectionManager.RT_URL_KEY, this._rtUrl);
}
Expand Down Expand Up @@ -703,6 +746,7 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
private async storeConnection(): Promise<boolean> {
try {
await this.storeXrayUrl();
await this.storeXscUrl();
await this.storeRtUrl();
await this.storePlatformUrl();
await this.storeUsername();
Expand Down Expand Up @@ -730,6 +774,7 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
await Promise.all([await this.deletePasswordFromSecretStorage(), await this.deleteAccessTokenFromSecretStorage()]);
await Promise.all([
this._context.globalState.update(ConnectionManager.XRAY_URL_KEY, undefined),
this._context.globalState.update(ConnectionManager.XSC_URL_KEY, undefined),
this._context.globalState.update(ConnectionManager.RT_URL_KEY, undefined),
this._context.globalState.update(ConnectionManager.PLATFORM_URL_KEY, undefined),
this._context.globalState.update(ConnectionManager.XRAY_USERNAME_KEY, undefined)
Expand All @@ -748,19 +793,81 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
progress: XrayScanProgress,
checkCanceled: () => void,
project: string,
watches: string[]
watches: string[],
msi?: string,
packageType?: string
): Promise<IGraphResponse> {
let policyMessage: string = '';
let client: JfrogClient = this.createJfrogClient();
let scanService: XrayScanClient = client.xray().scan();
let technology: string[] | undefined;
if (msi && msi !== '' && packageType) {
scanService = client.xsc().scan();
technology = [packageType];
}
this._logManager.logMessage(this.getScanLog(graphRequest.component_id, project, watches, msi, packageType), 'DEBUG');
return await scanService.graph(graphRequest, progress, checkCanceled, project, watches, msi, technology);
}

private getScanLog(componentId: string, project: string, watches: string[], msi?: string, packageType?: string): string {
let service: string = 'Xray';
let message: string = '';
if (msi && msi !== '') {
service += ' Source Control (XSC)';
message += ` Scan MSI: ${msi}`;
if (packageType) {
message += ` Scan Technology: ${packageType}`;
}
}
if (watches.length > 0) {
policyMessage += ` Using Watches: [${watches.join(', ')}]`;
message += ` Using Watches: [${watches.join(', ')}]`;
} else if (project && project !== '') {
policyMessage += ` Using Project key: ${project}`;
message += ` Using Project key: ${project}`;
}
this._logManager.logMessage('Sending dependency graph "' + graphRequest.component_id + '" to Xray for analyzing.' + policyMessage, 'DEBUG');
return await this.createJfrogClient()
.xray()
.scan()
.graph(graphRequest, progress, checkCanceled, project, watches);
return `Sending dependency graph "` + componentId + `" to ` + service + ` for analyzing.` + message;
}

public async startScan(request: StartScanRequest): Promise<ScanEvent> {
if (this._xscUrl === '') {
this._logManager.logMessage('Xsc is not configured. Skipping analytics...', 'DEBUG');
return {} as ScanEvent;
}
return this.createJfrogClient()
.xsc()
.event()
.startScan(request);
}

public async endScan(event: ScanEvent): Promise<void> {
if (this._xscUrl === '') {
return;
}
this.createJfrogClient()
.xsc()
.event()
.endScan(event);
}

public async logWithAnalytics(message: string, level: LogLevel): Promise<void> {
if (this._xscUrl === '') {
return;
}
this.createJfrogClient()
.xsc()
.event()
.log({
message: message,
source: 'jfrog-vscode-extension',
log_level: this.toXscLogLevel(level)
});
}

private toXscLogLevel(level: LogLevel): XscLogLevel {
if (level === 'WARN') {
return 'warning';
} else if (level === 'ERR') {
return 'error';
}
return level.toLowerCase() as XscLogLevel;
}

public async searchArtifactsByAql(aql: string): Promise<IAqlSearchResult> {
Expand Down Expand Up @@ -818,13 +925,20 @@ export class ConnectionManager implements ExtensionComponent, vscode.Disposable
}

private async updateJfrogVersions() {
await Promise.all([this.updateArtifactoryVersion(), this.updateXrayVersion()]);
await Promise.all([this.updateArtifactoryVersion(), this.updateXrayVersion(), this.updateXscVersion()]);
}

private async updateXrayVersion() {
this._xrayVersion = await ConnectionUtils.getXrayVersion(this.createJfrogClient());
}

public async updateXscVersion() {
this._xscVersion = await ConnectionUtils.getXscVersion(this.createJfrogClient());
if (this._xscVersion === '') {
this._logManager.logMessage('Xray source control is not enabled or not supported by the connected server.', 'DEBUG');
}
}

private async updateArtifactoryVersion() {
this._artifactoryVersion = await ConnectionUtils.getArtifactoryVersion(this.createJfrogClient());
}
Expand Down
Loading

0 comments on commit ff3766c

Please sign in to comment.