Skip to content

Commit e1e2e83

Browse files
committed
added singleton and factory design patterns to CxWrapper
1 parent d0fb006 commit e1e2e83

12 files changed

+144
-72
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"postbuild": "copyfiles -u 1 src/tests/data/* dist/;",
2323
"lint": "eslint . --ext .ts",
2424
"lint-and-fix": "eslint . --ext .ts --fix",
25-
"test": "copyfiles -u 1 src/tests/data/* dist/; tsc && jest --runInBand"
25+
"test": "copyfiles -u 1 src/tests/data/* dist/; tsc && jest"
2626
},
2727
"repository": "https://github.com/CheckmarxDev/ast-cli-javascript-wrapper-runtime-cli.git",
2828
"author": "Jay Nanduri",

src/main/wrapper/CxWrapper.ts

Lines changed: 79 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ import {getLoggerWithFilePath, logger} from "./loggerConfig";
77
import * as os from "os";
88
import CxBFL from "../bfl/CxBFL";
99
import {CxInstaller} from "../osinstaller/CxInstaller";
10+
import {Semaphore} from "async-mutex";
1011

1112

1213
type ParamTypeMap = Map<CxParamType, string>;
1314

1415
export class CxWrapper {
15-
config: CxConfig = new CxConfig();
16-
cxInstaller: CxInstaller = new CxInstaller(process.platform);
16+
private static instance: CxWrapper;
17+
private static semaphore = new Semaphore(1); // Semaphore with 1 slot
18+
config: CxConfig;
19+
cxInstaller: CxInstaller;
1720
constructor(cxScanConfig: CxConfig, logFilePath?: string) {
21+
this.cxInstaller = new CxInstaller(process.platform);
22+
this.config = new CxConfig();
1823
getLoggerWithFilePath(logFilePath)
1924
if (cxScanConfig.apiKey) {
2025
this.config.apiKey = cxScanConfig.apiKey;
@@ -44,8 +49,54 @@ export class CxWrapper {
4449
}
4550
}
4651

47-
async initializeCommands(formatRequired: boolean): Promise<string[]> {
48-
await this.cxInstaller.downloadIfNotInstalledCLI()
52+
static async getInstance(cxScanConfig: CxConfig, logFilePath: string): Promise<CxWrapper> {
53+
const [_, release] = await this.semaphore.acquire();
54+
if (!CxWrapper.instance) {
55+
CxWrapper.instance = new CxWrapper(cxScanConfig, logFilePath);
56+
}
57+
release();
58+
59+
return CxWrapper.instance;
60+
}
61+
62+
setScanConfig(cxScanConfig: CxConfig) {
63+
if (cxScanConfig.apiKey) {
64+
this.config.apiKey = cxScanConfig.apiKey;
65+
} else if (cxScanConfig.clientId && cxScanConfig.clientSecret) {
66+
logger.info("Received clientId and clientSecret");
67+
this.config.clientId = cxScanConfig.clientId;
68+
this.config.clientSecret = cxScanConfig.clientSecret;
69+
} else {
70+
logger.info("Did not receive ClientId/Secret or ApiKey from cli arguments");
71+
}
72+
if (cxScanConfig.pathToExecutable) {
73+
this.config.pathToExecutable = cxScanConfig.pathToExecutable;
74+
} else {
75+
this.config.pathToExecutable = this.cxInstaller.getExecutablePath();
76+
}
77+
if (cxScanConfig.baseUri) {
78+
this.config.baseUri = cxScanConfig.baseUri;
79+
}
80+
if (cxScanConfig.baseAuthUri) {
81+
this.config.baseAuthUri = cxScanConfig.baseAuthUri;
82+
}
83+
if (cxScanConfig.tenant) {
84+
this.config.tenant = cxScanConfig.tenant;
85+
}
86+
if (cxScanConfig.additionalParameters) {
87+
this.config.additionalParameters = cxScanConfig.additionalParameters;
88+
}
89+
}
90+
91+
GetScanConfig(): CxConfig {
92+
return this.config;
93+
}
94+
95+
async init(): Promise<void> {
96+
return await this.cxInstaller.downloadIfNotInstalledCLI();
97+
}
98+
99+
initializeCommands(formatRequired: boolean): string[] {
49100
this.config.pathToExecutable = this.cxInstaller.getExecutablePath();
50101

51102
const list: string[] = [];
@@ -87,14 +138,14 @@ export class CxWrapper {
87138

88139
async authValidate(): Promise<CxCommandOutput> {
89140
const commands: string[] = [CxConstants.CMD_AUTH, CxConstants.SUB_CMD_VALIDATE];
90-
commands.push(...await this.initializeCommands(false));
141+
commands.push(...this.initializeCommands(false));
91142
const exec = new ExecutionService();
92143
return await exec.executeCommands(this.config.pathToExecutable, commands);
93144
}
94145

95146
async scanCreate(params: ParamTypeMap): Promise<CxCommandOutput> {
96147
const commands: string[] = [CxConstants.CMD_SCAN, CxConstants.SUB_CMD_CREATE];
97-
commands.push(...await this.initializeCommands(false));
148+
commands.push(...this.initializeCommands(false));
98149
commands.push(CxConstants.SCAN_INFO_FORMAT);
99150
commands.push(CxConstants.FORMAT_JSON);
100151

@@ -132,37 +183,37 @@ export class CxWrapper {
132183
commands.push('"js-wrapper"');
133184
}
134185

135-
commands.push(...await this.initializeCommands(false));
186+
commands.push(...this.initializeCommands(false));
136187
const exec = new ExecutionService();
137188
return await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.SCAN_VORPAL);
138189
}
139190

140191
async scanCancel(id: string): Promise<CxCommandOutput> {
141192
const commands: string[] = [CxConstants.CMD_SCAN, CxConstants.SUB_CMD_CANCEL, CxConstants.SCAN_ID, id];
142-
commands.push(...await this.initializeCommands(false));
193+
commands.push(...this.initializeCommands(false));
143194
const exec = new ExecutionService();
144195
return await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.SCAN_TYPE);
145196
}
146197

147198
async scanShow(id: string): Promise<CxCommandOutput> {
148199
const commands: string[] = [CxConstants.CMD_SCAN, CxConstants.SUB_CMD_SHOW, CxConstants.SCAN_ID, id];
149-
commands.push(...await this.initializeCommands(true));
200+
commands.push(...this.initializeCommands(true));
150201
const exec = new ExecutionService();
151202
return await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.SCAN_TYPE);
152203
}
153204

154205
async scanList(filters: string): Promise<CxCommandOutput> {
155206
const validated_filters = this.filterArguments(filters);
156207
const commands: string[] = [CxConstants.CMD_SCAN, "list"].concat(validated_filters);
157-
commands.push(...await this.initializeCommands(true));
208+
commands.push(...this.initializeCommands(true));
158209
const exec = new ExecutionService();
159210
return await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.SCAN_TYPE);
160211
}
161212

162213
async projectList(filters: string): Promise<CxCommandOutput> {
163214
const validated_filters = this.filterArguments(filters);
164215
const commands: string[] = [CxConstants.CMD_PROJECT, "list"].concat(validated_filters);
165-
commands.push(...await this.initializeCommands(true));
216+
commands.push(...this.initializeCommands(true));
166217
const exec = new ExecutionService();
167218
return await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.PROJECT_TYPE);
168219
}
@@ -171,28 +222,28 @@ export class CxWrapper {
171222
// Verify and add possible branch filter by name
172223
const validated_filters = this.filterArguments(CxConstants.BRANCH_NAME + filters)
173224
const commands: string[] = [CxConstants.CMD_PROJECT, CxConstants.SUB_CMD_BRANCHES, CxConstants.PROJECT_ID, projectId].concat(validated_filters);
174-
commands.push(...await this.initializeCommands(false));
225+
commands.push(...this.initializeCommands(false));
175226
const exec = new ExecutionService();
176227
return await exec.executeCommands(this.config.pathToExecutable, commands);
177228
}
178229

179230
async projectShow(projectId: string): Promise<CxCommandOutput> {
180231
const commands: string[] = [CxConstants.CMD_PROJECT, CxConstants.SUB_CMD_SHOW, CxConstants.PROJECT_ID, projectId];
181-
commands.push(...await this.initializeCommands(true));
232+
commands.push(...this.initializeCommands(true));
182233
const exec = new ExecutionService();
183234
return await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.PROJECT_TYPE);
184235
}
185236

186237
async triageShow(projectId: string, similarityId: string, scanType: string): Promise<CxCommandOutput> {
187238
const commands: string[] = [CxConstants.CMD_TRIAGE, CxConstants.SUB_CMD_SHOW, CxConstants.PROJECT_ID, projectId, CxConstants.SIMILARITY_ID, similarityId, CxConstants.SCAN_TYPES_SUB_CMD, scanType];
188-
commands.push(...await this.initializeCommands(true));
239+
commands.push(...this.initializeCommands(true));
189240
const exec = new ExecutionService();
190241
return await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.PREDICATE_TYPE);
191242
}
192243

193244
async triageUpdate(projectId: string, similarityId: string, scanType: string, state: string, comment: string, severity: string): Promise<CxCommandOutput> {
194245
const commands: string[] = [CxConstants.CMD_TRIAGE, CxConstants.SUB_CMD_UPDATE, CxConstants.PROJECT_ID, projectId, CxConstants.SIMILARITY_ID, similarityId, CxConstants.SCAN_TYPES_SUB_CMD, scanType, CxConstants.STATE, state, CxConstants.COMMENT, comment, CxConstants.SEVERITY, severity];
195-
commands.push(...await this.initializeCommands(false));
246+
commands.push(...this.initializeCommands(false));
196247
const exec = new ExecutionService();
197248
return await exec.executeCommands(this.config.pathToExecutable, commands);
198249
}
@@ -225,7 +276,7 @@ export class CxWrapper {
225276

226277
async codeBashingList(cweId: string, language: string, queryName: string): Promise<CxCommandOutput> {
227278
const commands: string[] = [CxConstants.CMD_RESULT, CxConstants.CMD_CODE_BASHING, CxConstants.LANGUAGE, language, CxConstants.VULNERABILITY_TYPE, queryName, CxConstants.CWE_ID, cweId];
228-
commands.push(...await this.initializeCommands(true));
279+
commands.push(...this.initializeCommands(true));
229280
const exec = new ExecutionService();
230281
return await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.CODE_BASHING_TYPE);
231282
}
@@ -244,13 +295,13 @@ export class CxWrapper {
244295
commands.push(CxConstants.AGENT);
245296
commands.push(agent);
246297
}
247-
commands.push(...await this.initializeCommands(false));
298+
commands.push(...this.initializeCommands(false));
248299
return commands;
249300
}
250301

251302
async getResultsBfl(scanId: string, queryId: string, resultNodes: any[]) {
252303
const commands: string[] = [CxConstants.CMD_RESULT, CxConstants.SUB_CMD_BFL, CxConstants.SCAN_ID, scanId, CxConstants.QUERY_ID, queryId];
253-
commands.push(...await this.initializeCommands(true));
304+
commands.push(...this.initializeCommands(true));
254305
const exec = new ExecutionService();
255306
const response = await exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.BFL_TYPE);
256307
if (response) {
@@ -265,7 +316,7 @@ export class CxWrapper {
265316
if (engine.length > 0) {
266317
commands.push(CxConstants.ENGINE, engine)
267318
}
268-
commands.push(...await this.initializeCommands(false));
319+
commands.push(...this.initializeCommands(false));
269320
const exec = new ExecutionService();
270321
return exec.executeKicsCommands(this.config.pathToExecutable, commands, CxConstants.KICS_REALTIME_TYPE);
271322
}
@@ -277,14 +328,14 @@ export class CxWrapper {
277328
*/
278329
async runScaRealtimeScan(projectDirPath: string): Promise<CxCommandOutput> {
279330
const commands: string[] = [CxConstants.CMD_SCAN, CxConstants.CMD_SCA_REALTIME, CxConstants.CMD_SCA_REALTIME_PROJECT_DIR, projectDirPath];
280-
commands.push(...await this.initializeCommands(false));
331+
commands.push(...this.initializeCommands(false));
281332
return new ExecutionService().executeCommands(this.config.pathToExecutable, commands, CxConstants.SCA_REALTIME_TYPE);
282333
}
283334

284335

285336
async learnMore(queryId: string) {
286337
const commands: string[] = [CxConstants.CMD_UTILS, CxConstants.CMD_LEARN_MORE, CxConstants.QUERY_ID, queryId]
287-
commands.push(...await this.initializeCommands(true))
338+
commands.push(...this.initializeCommands(true))
288339
const exec = new ExecutionService();
289340
return exec.executeCommands(this.config.pathToExecutable, commands, CxConstants.LEARN_MORE_DESCRIPTIONS_TYPE);
290341
}
@@ -297,29 +348,29 @@ export class CxWrapper {
297348
if (similarityIds) {
298349
commands.push(CxConstants.KICS_REMEDIATION_SIMILARITY_IDS, similarityIds)
299350
}
300-
commands.push(...await this.initializeCommands(false));
351+
commands.push(...this.initializeCommands(false));
301352
const exec = new ExecutionService();
302353
return exec.executeKicsCommands(this.config.pathToExecutable, commands, CxConstants.KICS_REMEDIATION_TYPE);
303354
}
304355

305356
async scaRemediation(packageFiles: string, packages: string, packageVersion: string): Promise<CxCommandOutput> {
306357
const commands: string[] = [CxConstants.CMD_UTILS, CxConstants.CMD_REMEDIATION, CxConstants.SUB_CMD_REMEDIATION_SCA, CxConstants.SCA_REMEDIATION_PACKAGE_FILES, packageFiles, CxConstants.SCA_REMEDIATION_PACKAGE, packages, CxConstants.SCA_REMEDIATION_PACKAGE_VERSION, packageVersion];
307-
commands.push(...await this.initializeCommands(false));
358+
commands.push(...this.initializeCommands(false));
308359
const exec = new ExecutionService();
309360
return exec.executeCommands(this.config.pathToExecutable, commands);
310361
}
311362

312363
async ideScansEnabled(): Promise<boolean> {
313364
const commands: string[] = [CxConstants.CMD_UTILS, CxConstants.SUB_CMD_TENANT];
314-
commands.push(...await this.initializeCommands(false));
365+
commands.push(...this.initializeCommands(false));
315366
const exec = new ExecutionService();
316367
const output = await exec.executeMapTenantOutputCommands(this.config.pathToExecutable, commands);
317368
return output.has(CxConstants.IDE_SCANS_KEY) && output.get(CxConstants.IDE_SCANS_KEY).toLowerCase() === " true";
318369
}
319370

320371
async guidedRemediationEnabled(): Promise<boolean> {
321372
const commands: string[] = [CxConstants.CMD_UTILS, CxConstants.SUB_CMD_TENANT];
322-
commands.push(...await this.initializeCommands(false));
373+
commands.push(...this.initializeCommands(false));
323374
const exec = new ExecutionService();
324375
const output = await exec.executeMapTenantOutputCommands(this.config.pathToExecutable, commands);
325376
return output.has(CxConstants.AI_GUIDED_REMEDIATION_KEY) && output.get(CxConstants.AI_GUIDED_REMEDIATION_KEY).toLowerCase() === " true";
@@ -342,7 +393,7 @@ export class CxWrapper {
342393
if (model) {
343394
commands.push(CxConstants.CMD_CHAT_MODEL, model)
344395
}
345-
commands.push(...await this.initializeCommands(false));
396+
commands.push(...this.initializeCommands(false));
346397
return new ExecutionService().executeCommands(this.config.pathToExecutable, commands, CxConstants.CHAT_TYPE);
347398
}
348399

@@ -362,7 +413,7 @@ export class CxWrapper {
362413
if (model) {
363414
commands.push(CxConstants.CMD_CHAT_MODEL, model)
364415
}
365-
commands.push(...await this.initializeCommands(false));
416+
commands.push(...this.initializeCommands(false));
366417
return new ExecutionService().executeCommands(this.config.pathToExecutable, commands, CxConstants.CHAT_TYPE);
367418
}
368419

@@ -373,7 +424,7 @@ export class CxWrapper {
373424
CxConstants.CMD_CHAT_FILE, file,
374425
];
375426

376-
commands.push(...await this.initializeCommands(false));
427+
commands.push(...this.initializeCommands(false));
377428
return new ExecutionService().executeCommands(this.config.pathToExecutable, commands, CxConstants.MASK_TYPE);
378429
}
379430

src/main/wrapper/CxWrapperFactory.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {CxWrapper} from "./CxWrapper";
2+
import {CxConfig} from "./CxConfig";
3+
4+
class CxWrapperFactory {
5+
static async createWrapper(cxScanConfig: CxConfig, type?: string, logFilePath?: string) {
6+
let wrapper: CxWrapper;
7+
8+
if (type === 'mock') {
9+
wrapper = new CxWrapper(cxScanConfig, logFilePath);
10+
}
11+
else {
12+
wrapper = await CxWrapper.getInstance(cxScanConfig, logFilePath);
13+
}
14+
await wrapper.init();
15+
return wrapper;
16+
}
17+
}
18+
19+
export default CxWrapperFactory;

src/tests/AuthTest.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import {CxWrapper} from '../main/wrapper/CxWrapper';
21
import {CxCommandOutput} from "../main/wrapper/CxCommandOutput";
32
import {CxConfig} from "../main/wrapper/CxConfig";
43
import {BaseTest} from "./BaseTest";
4+
import CxWrapperFactory from "../main/wrapper/CxWrapperFactory";
55

66
describe("Authentication validation",() => {
77
const cxScanConfig = new BaseTest();
88
it('Result authentication successful case', async () => {
9-
const auth = new CxWrapper(cxScanConfig);
9+
const auth = await CxWrapperFactory.createWrapper(cxScanConfig);
1010
const cxCommandOutput: CxCommandOutput = await auth.authValidate();
1111
expect(cxCommandOutput.exitCode).toBe(0);
1212
});
@@ -18,7 +18,7 @@ describe("Authentication validation",() => {
1818
cxScanConfig_fail.clientSecret = "error";
1919
cxScanConfig_fail.tenant = process.env["CX_TENANT"];
2020
cxScanConfig_fail.apiKey = "error";
21-
const auth = new CxWrapper(cxScanConfig_fail);
21+
const auth = await CxWrapperFactory.createWrapper(cxScanConfig_fail,'mock');
2222
const cxCommandOutput: CxCommandOutput = await auth.authValidate();
2323
expect(cxCommandOutput.exitCode).toBe(1);
2424
});

src/tests/ChatTest.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {CxCommandOutput} from "../main/wrapper/CxCommandOutput";
33
import CxChat from "../main/chat/CxChat";
44
import {anything, instance, mock, when} from "ts-mockito";
55
import {BaseTest} from "./BaseTest";
6+
import CxWrapperFactory from "../main/wrapper/CxWrapperFactory";
67

78
function createOutput(exitCode:number,payload:CxChat):CxCommandOutput {
89
const output = new CxCommandOutput();
@@ -16,10 +17,9 @@ describe("Gpt Chat Cases", () => {
1617
// tests preparation
1718
const cxScanConfig = new BaseTest();
1819
const mockedWrapper: CxWrapper = mock(CxWrapper);
19-
const originalWrapper: CxWrapper = new CxWrapper(cxScanConfig);
20-
const outputSuccessful = createOutput(0,new CxChat("CONVERSATION",["RESPONSE"] ));
20+
const outputSuccessful = createOutput(0, new CxChat("CONVERSATION", ["RESPONSE"]));
2121

22-
when(mockedWrapper.kicsChat("APIKEY","FILE",anything(),anything(),anything(),anything(),anything(), anything())).thenResolve(
22+
when(mockedWrapper.kicsChat("APIKEY", "FILE", anything(), anything(), anything(), anything(), anything(), anything())).thenResolve(
2323
outputSuccessful
2424
);
2525
const wrapper: CxWrapper = instance(mockedWrapper);
@@ -39,6 +39,7 @@ describe("Gpt Chat Cases", () => {
3939
});
4040

4141
it('KICS Gpt Chat Failed case', async () => {
42+
const originalWrapper: CxWrapper = await CxWrapperFactory.createWrapper(cxScanConfig);
4243
const cxCommandOutput = await originalWrapper.kicsChat(
4344
"APIKEY",
4445
"FILE",
@@ -54,6 +55,7 @@ describe("Gpt Chat Cases", () => {
5455
});
5556

5657
it('Sast Gpt Chat Failed case', async () => {
58+
const originalWrapper: CxWrapper = await CxWrapperFactory.createWrapper(cxScanConfig);
5759
const cxCommandOutput = await originalWrapper.sastChat(
5860
"APIKEY",
5961
"SOURCE_FILE",

src/tests/LearnMoreDescriptions.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import {BaseTest} from "./BaseTest";
2-
import {CxWrapper} from "../main/wrapper/CxWrapper";
32
import {CxCommandOutput} from "../main/wrapper/CxCommandOutput";
3+
import CxWrapperFactory from "../main/wrapper/CxWrapperFactory";
44

55
describe("LearnMoreDescriptions cases",() => {
66
const cxScanConfig = new BaseTest();
77
it('LearnMoreDescriptions Successful case', async () => {
8-
const auth = new CxWrapper(cxScanConfig);
8+
const auth = await CxWrapperFactory.createWrapper(cxScanConfig);
99
const queryId = process.env.CX_TEST_QUERY_ID;
1010
const data = await auth.learnMore(queryId !== undefined? queryId : "16772998409937314312")
1111
const cxCommandOutput: CxCommandOutput = data;
1212
expect(cxCommandOutput.payload.length).toBeGreaterThan(0);
1313
})
1414

1515
it('LearnMoreDescriptions Failure case', async () => {
16-
const auth = new CxWrapper(cxScanConfig);
16+
const auth = await CxWrapperFactory.createWrapper(cxScanConfig);
1717
const data = await auth.learnMore("")
1818
const cxCommandOutput: CxCommandOutput = data;
1919
expect(cxCommandOutput.status).toBe("Value of query-id is invalid\n");

0 commit comments

Comments
 (0)