Skip to content

Commit

Permalink
Add Xsc API (#95)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas committed Apr 7, 2024
1 parent 067af87 commit 8930428
Show file tree
Hide file tree
Showing 24 changed files with 633 additions and 39 deletions.
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ Add jfrog-client-js as a dependency to your package.json file:
- [Platform](#platform)
- [Register For Web Login](#register-for-web-login)
- [Get Access Token From Web Login](#get-access-token-from-web-login)
- [Xray Source Control](#xray-source-control)
- [Getting Xsc Version](#getting-xsc-version)
- [Sending Log Message Event](#sending-log-mesage-event)
- [Sending Start Scan Event](#sending-start-scan-event)
- [Sending End Scan Event](#sending-end-scan-event)
- [Getting Scan Event Details](#getting-scan-event-details)

### Setting up JFrog client

Expand Down Expand Up @@ -343,3 +349,85 @@ jfrogClient
```

Please note that you need to replace 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' with the actual session ID that you've generated for `registerSessionId`.

### Xray Source Control

#### System

##### Getting Xsc Version

```javascript
jfrogClient
.xsc()
.system()
.version()
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
```

#### Event

##### Sending Log Message Event

```javascript
jfrogClient
.xsc()
.event()
.log({log_level: 'error', source: 'js-client', message: 'error message to report as an event'})
.catch((error) => {
console.error(error);
});
```

##### Sending Start Scan Event

```javascript
jfrogClient
.xsc()
.event()
.startScan({product: 'product', os_platform: 'windows', jfrog_user: 'user-name'})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
```

##### Sending End Scan Event

```javascript
const scanEvent = {multi_scan_id: 'some-scan-id', event_status: 'completed'}
jfrogClient
.xsc()
.event()
.endScan({product: 'product', os_platform: 'windows', jfrog_user: 'user-name'})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
```

##### Getting Scan Event Details

```javascript
const multiScanId = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX // UUID
jfrogClient
.xsc()
.event()
.getScanEvent(multiScanId)
.then((result) => {
...
})
.catch((error) => {
...
});
```

Please note that you need to replace 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' with the actual multi scan ID that you've generated with `startScan`.
12 changes: 12 additions & 0 deletions model/Xsc/Event/ScanEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ScanEventStatus } from './index';

export interface ScanEvent extends ScanEventEndData {
multi_scan_id: string;
}

export interface ScanEventEndData {
event_status?: ScanEventStatus;
total_findings?: number;
total_ignored_findings?: number;
total_scan_duration?: string;
}
4 changes: 4 additions & 0 deletions model/Xsc/Event/ScanEventResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ScanEventEndData } from './ScanEvent';
import { StartScanRequest } from './StartScanRequest';

export interface ScanEventResponse extends StartScanRequest, ScanEventEndData {}
15 changes: 15 additions & 0 deletions model/Xsc/Event/StartScanRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ScanEventType, ScanEventStatus } from './index';

export interface StartScanRequest {
event_type?: ScanEventType;
event_status?: ScanEventStatus;
product?: string;
product_version?: string;
jpd_version?: string;
jfrog_user?: string;
os_platform?: string;
os_architecture?: string;
machine_id?: string;
analyzer_manager_version?: string;
is_default_config?: boolean;
}
7 changes: 7 additions & 0 deletions model/Xsc/Event/XscLog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { XscLogLevel } from './index';

export interface XscLog {
log_level: XscLogLevel;
source: string;
message: string;
}
17 changes: 17 additions & 0 deletions model/Xsc/Event/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export { ScanEvent } from './ScanEvent';
export { StartScanRequest } from './StartScanRequest';
export { ScanEventResponse } from './ScanEventResponse';
export { XscLog } from './XscLog';

export enum ScanEventStatus {
Started = 'started',
Completed = 'completed',
Cancelled = 'cancelled',
Failed = 'failed',
}

export enum ScanEventType {
SourceCode = 1,
}

export type XscLogLevel = 'debug' | 'info' | 'warning' | 'error';
4 changes: 4 additions & 0 deletions model/Xsc/System/Version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IXscVersion {
xray_version: string;
xsc_version: string;
}
4 changes: 3 additions & 1 deletion model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ export { Severity } from './Xray/Severity';
export * from './Xray/Summary';
export { IXrayVersion } from './Xray/System/Version';
export { AccessTokenResponse } from './Platform/AccessTokenResponse';
export { RetryOnStatusCode } from './ClientConfig'
export { RetryOnStatusCode } from './ClientConfig';
export * from './Xsc/Event';
export { IXscVersion } from './Xsc/System/Version';
2 changes: 1 addition & 1 deletion src/HttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export interface IHttpConfig {
retryOnStatusCode?: RetryOnStatusCode;
}

export type method = 'GET' | 'POST' | 'HEAD';
export type method = 'GET' | 'POST' | 'HEAD' | 'PUT';
export type responseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream';

export interface IRequestParams {
Expand Down
11 changes: 7 additions & 4 deletions src/JfrogClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IJfrogClientConfig } from '../model/JfrogClientConfig';
import { XrayClient } from './Xray/XrayClient';
import { ArtifactoryClient } from './Artifactory/ArtifactoryClient';
import { XscClient } from './Xsc/XscClient';
import { IClientSpecificConfig } from '../model/ClientSpecificConfig';

import * as os from 'os';
Expand All @@ -11,6 +12,7 @@ import { ClientUtils } from './ClientUtils';
export class JfrogClient {
private static readonly ARTIFACTORY_SUFFIX: string = 'artifactory';
private static readonly XRAY_SUFFIX: string = 'xray';
private static readonly XSC_SUFFIX: string = 'xsc';

public readonly clientId?: string;

Expand Down Expand Up @@ -39,16 +41,17 @@ export class JfrogClient {
return new PlatformClient({ serverUrl: this._jfrogConfig.platformUrl, ...this._jfrogConfig });
}

public xsc(): XscClient {
return new XscClient(this.getSpecificClientConfig(JfrogClient.XSC_SUFFIX));
}

/**
* Creates a server specific config from the provided JFrog config.
* @param serverSuffix - server specific suffix.
* @param providedCustomUrl - custom server URL, if provided.
* @private
*/
private getSpecificClientConfig(
serverSuffix: string,
providedCustomUrl: string | undefined
): IClientSpecificConfig {
private getSpecificClientConfig(serverSuffix: string, providedCustomUrl?: string): IClientSpecificConfig {
return { serverUrl: this.getServerUrl(serverSuffix, providedCustomUrl), ...this._jfrogConfig };
}

Expand Down
4 changes: 3 additions & 1 deletion src/Xray/XrayClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { XrayGraphClient as XrayScanClient } from '..';
import { XrayEntitlementsClient } from './XrayEntitlementsClient';

export class XrayClient {
static readonly scanGraphEndpoint: string = 'api/v1/scan/graph';

private readonly httpClient: HttpClient;
private logger: ILogger;

Expand Down Expand Up @@ -60,7 +62,7 @@ export class XrayClient {
}

public scan(): XrayScanClient {
return new XrayScanClient(this.httpClient, this.logger);
return new XrayScanClient(this.httpClient, XrayClient.scanGraphEndpoint, this.logger);
}

public entitlements(): XrayEntitlementsClient {
Expand Down
62 changes: 46 additions & 16 deletions src/Xray/XrayScanClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,37 @@ import { IRequestParams } from '../HttpClient';
import { XrayScanProgress } from './XrayScanProgress';

export class XrayScanClient {
static readonly scanGraphEndpoint: string = 'api/v1/scan/graph';
private static readonly SLEEP_INTERVAL_MILLISECONDS: number = 5000;
private static readonly MAX_ATTEMPTS: number = 60;

constructor(private readonly httpClient: HttpClient, private readonly logger: ILogger) {}
constructor(
private readonly httpClient: HttpClient,
private readonly endPoint: string,
private readonly logger: ILogger
) {}

public async graph(
request: IGraphRequestModel,
progress: XrayScanProgress,
checkCanceled: () => void,
projectKey: string | undefined,
watches: string[] | undefined,
multiScanId?: string,
technologies?: string[],
sleepIntervalMilliseconds: number = XrayScanClient.SLEEP_INTERVAL_MILLISECONDS
): Promise<IGraphResponse> {
try {
if (!request) {
return {} as IGraphResponse;
}
checkCanceled();
const response: IClientResponse = await this.postScanGraph(request, projectKey, watches);
const response: IClientResponse = await this.postScanGraph(
request,
projectKey,
watches,
multiScanId,
technologies
);
return await this.getScanGraphResults(
response.data.scan_id,
progress,
Expand All @@ -50,11 +61,13 @@ export class XrayScanClient {
private async postScanGraph(
request: IGraphRequestModel,
projectKey?: string,
watches?: string[]
watches?: string[],
multiScanId?: string,
technologies?: string[]
): Promise<IClientResponse> {
this.logger.debug('Sending POST scan/graph request...');
const requestParams: IRequestParams = {
url: this.getUrl(projectKey, watches),
url: this.getUrl(projectKey, watches, multiScanId, technologies),
method: 'POST',
data: request,
};
Expand All @@ -76,23 +89,40 @@ export class XrayScanClient {
}

/**
* Get URL for "POST api/v1/scan/graph".
* If no project key provided - api/v1/scan/graph
* If project key was provided - api/v1/scan/graph?project=<projectKey>
* If watches provided - api/v1/scan/graph?watch=<watch-1>&watch=<watch-2>
* Get URL for "POST scan/graph" (Xray: api/v1/scan/graph, XSC: api/v1/sca/scan/graph).
* If no project key provided - /scan/graph
* If project key was provided - /scan/graph?project=<projectKey>
* If watches provided - /scan/graph?watch=<watch-1>&watch=<watch-2>
* If multiScanId provided - /scan/graph?multi_scan_id=<multiScanId>
* If technologies provided - /scan/graph?tech=<tech-1>&tech=<tech-2>
* @param projectKey - Project key or undefined
* @param watches - List of Watches or undefined
* @returns URL for "POST api/v1/scan/graph"
* @param multiScanId - Multi scan ID or undefined
* @param technologies - List of technologies or undefined
* @returns URL for "POST /scan/graph"
*/
private getUrl(projectKey: string | undefined, watches?: string[]): string {
let url: string = XrayScanClient.scanGraphEndpoint;
private getUrl(
projectKey: string | undefined,
watches?: string[],
multiScanId?: string,
technologies?: string[]
): string {
let url: string = this.endPoint;
let params: string[] = [];

if (projectKey && projectKey.length > 0) {
url += `?project=${projectKey}`;
params.push(`project=${projectKey}`);
} else if (watches && watches.length > 0) {
url += '?watch=' + watches.join('&watch=');
params.push(`watch=${watches.join('&watch=')}`);
}
if (multiScanId) {
params.push(`multi_scan_id=${multiScanId}`);
}
if (technologies && technologies.length > 0) {
params.push(`tech=${technologies.join('&tech=')}`);
}

return url;
return params.length > 0 ? url + '?' + params.join('&') : url;
}

/**
Expand All @@ -117,7 +147,7 @@ export class XrayScanClient {
sleepIntervalMilliseconds: number
): Promise<IGraphResponse> {
const scanGraphUrl: string =
XrayScanClient.scanGraphEndpoint +
this.endPoint +
'/' +
scanId +
'?include_licenses=true' +
Expand Down

0 comments on commit 8930428

Please sign in to comment.