Skip to content

Commit 48a5718

Browse files
authored
Merge pull request #464 from CheckmarxDev/gpt
Support GPT command
2 parents c74e6b3 + c81743d commit 48a5718

File tree

11 files changed

+206
-16
lines changed

11 files changed

+206
-16
lines changed

package-lock.json

Lines changed: 21 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"README.md"
1010
],
1111
"dependencies": {
12-
"log4js": "^6.9.1"
12+
"log4js": "^6.9.1",
13+
"ts-mockito": "^2.6.1"
1314
},
1415
"scripts": {
1516
"build": "tsc",

src/main/chat/CxChat.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default class CxChat {
2+
conversationId: string;
3+
responses: string[];
4+
5+
constructor(conversationId: string, responses: string[]) {
6+
this.conversationId = conversationId;
7+
this.responses = responses;
8+
}
9+
10+
static parseChat(resultObject: any): CxChat {
11+
return new CxChat(resultObject.conversationId, resultObject.response);
12+
}
13+
}

src/main/mask/CxMask.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import CxMaskedSecret from "./CxMaskedSecret";
2+
3+
export default class CxMask {
4+
maskedSecrets: CxMaskedSecret[];
5+
maskedFile: string;
6+
7+
constructor(maskedSecrets: CxMaskedSecret[], maskedFile: string) {
8+
this.maskedSecrets = maskedSecrets;
9+
this.maskedFile = maskedFile;
10+
}
11+
12+
static parseMask(resultObject: any): CxMask {
13+
return new CxMask(resultObject.maskedSecrets,resultObject.maskedFile);
14+
}
15+
}

src/main/mask/CxMaskedSecret.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export default class CxMaskedSecret {
2+
masked: string;
3+
secret: string;
4+
line: number;
5+
6+
constructor(masked: string, secret: string, line: number) {
7+
this.masked = masked;
8+
this.secret = secret;
9+
this.line = line;
10+
}
11+
}

src/main/wrapper/CxConstants.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ export enum CxConstants {
4343
CMD_KICS_REALTIME = "kics-realtime",
4444
CMD_SCA_REALTIME = "sca-realtime",
4545
CMD_SCA_REALTIME_PROJECT_DIR = "--project-dir",
46+
CMD_CHAT = "chat",
47+
CMD_CHAT_APIKEY = "--chat-apikey",
48+
CMD_CHAT_FILE = "--result-file",
49+
CMD_CHAT_LINE = "--result-line",
50+
CMD_CHAT_SEVERITY = "--result-severity",
51+
CMD_CHAT_VULNERABILITY = "--result-vulnerability",
52+
CMD_CHAT_INPUT = "--user-input",
53+
CMD_CHAT_CONVERSATION_ID = "--conversation-id",
54+
CMD_CHAT_MODEL = "--model",
55+
CMD_MASK_SECRETS = "mask",
4656
SCAN_INFO_FORMAT = "--scan-info-format",
4757
FORMAT = "--format",
4858
FORMAT_JSON = "json",
@@ -70,6 +80,8 @@ export enum CxConstants {
7080
CODE_BASHING_TYPE = "CxCodeBashing",
7181
KICS_REALTIME_TYPE = "CxKicsRealTime",
7282
SCA_REALTIME_TYPE = "CxScaRealTime",
83+
CHAT_TYPE = "CxChat",
84+
MASK_TYPE = "CxMask",
7385
LEARN_MORE_DESCRIPTIONS_TYPE = "CxLearnMoreDescriptions",
7486
KICS_REMEDIATION_TYPE = "CxKicsRemediation",
7587
BFL_TYPE = "CxBFL",
@@ -81,5 +93,6 @@ export enum CxConstants {
8193
SEVERITY_MEDIUM = "medium",
8294
STATE_CONFIRMED = "confirmed",
8395
CMD_LEARN_MORE = "learn-more",
84-
IDE_SCANS_KEY = " scan.config.plugins.ideScans"
96+
IDE_SCANS_KEY = " scan.config.plugins.ideScans",
97+
AI_GUIDED_REMEDIATION_KEY = " scan.config.plugins.aiGuidedRemediation"
8598
}

src/main/wrapper/CxWrapper.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,45 @@ export class CxWrapper {
299299
return output.has(CxConstants.IDE_SCANS_KEY) && output.get(CxConstants.IDE_SCANS_KEY).toLowerCase() === " true";
300300
}
301301

302+
async guidedRemediationEnabled() : Promise<boolean> {
303+
const commands: string[] = [CxConstants.CMD_UTILS, CxConstants.SUB_CMD_TENANT];
304+
commands.push(...this.initializeCommands(false));
305+
const exec = new ExecutionService();
306+
const output = await exec.executeMapTenantOutputCommands(this.config.pathToExecutable, commands);
307+
return output.has(CxConstants.AI_GUIDED_REMEDIATION_KEY) && output.get(CxConstants.AI_GUIDED_REMEDIATION_KEY).toLowerCase() === " true";
308+
}
309+
310+
async chat(apikey: string, file: string, line: number, severity: string, vulnerability: string, input: string, conversationId?: string, model?: string): Promise<CxCommandOutput> {
311+
const commands: string[] = [
312+
CxConstants.CMD_CHAT,
313+
CxConstants.CMD_CHAT_APIKEY, apikey,
314+
CxConstants.CMD_CHAT_FILE, file,
315+
CxConstants.CMD_CHAT_LINE, line.toString(),
316+
CxConstants.CMD_CHAT_SEVERITY, severity,
317+
CxConstants.CMD_CHAT_VULNERABILITY, vulnerability,
318+
CxConstants.CMD_CHAT_INPUT, input,
319+
];
320+
if (conversationId) {
321+
commands.push(CxConstants.CMD_CHAT_CONVERSATION_ID, conversationId)
322+
}
323+
if (model) {
324+
commands.push(CxConstants.CMD_CHAT_MODEL, model)
325+
}
326+
commands.push(...this.initializeCommands(false));
327+
return new ExecutionService().executeCommands(this.config.pathToExecutable, commands, CxConstants.CHAT_TYPE);
328+
}
329+
330+
async maskSecrets( file: string): Promise<CxCommandOutput> {
331+
const commands: string[] = [
332+
CxConstants.CMD_UTILS,
333+
CxConstants.CMD_MASK_SECRETS,
334+
CxConstants.CMD_CHAT_FILE, file,
335+
];
336+
337+
commands.push(...this.initializeCommands(false));
338+
return new ExecutionService().executeCommands(this.config.pathToExecutable, commands, CxConstants.MASK_TYPE);
339+
}
340+
302341
prepareAdditionalParams(additionalParameters: string) : string[] {
303342
const params: string[] = [];
304343

src/main/wrapper/ExecutionService.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import CxNode from "../results/CxNode";
2020
import CxPackageData from "../results/CxPackageData";
2121
import CxKicsRemediation from "../remediation/CxKicsRemediation";
2222
import CxScaRealTime from "../scaRealtime/CxScaRealTime";
23+
import CxChat from "../chat/CxChat";
24+
import CxMask from "../mask/CxMask";
2325

2426

2527
function isJsonString(s: string) {
@@ -206,10 +208,18 @@ export class ExecutionService {
206208
cxCommandOutput.payload = learnMore;
207209
break;
208210
case CxConstants.KICS_REMEDIATION_TYPE:
209-
const kicsRemediationOutput = CxKicsRemediation.parseKicsRemediation(resultObject)
210-
cxCommandOutput.payload = [kicsRemediationOutput]
211+
const kicsRemediationOutput = CxKicsRemediation.parseKicsRemediation(resultObject);
212+
cxCommandOutput.payload = [kicsRemediationOutput];
211213
break;
212-
default:
214+
case CxConstants.CHAT_TYPE:
215+
const chatOutput = CxChat.parseChat(resultObject);
216+
cxCommandOutput.payload = [chatOutput];
217+
break;
218+
case CxConstants.MASK_TYPE:
219+
const maskOutput = CxMask.parseMask(resultObject);
220+
cxCommandOutput.payload = [maskOutput];
221+
break;
222+
default:
213223
cxCommandOutput.payload = resultObject;
214224
}
215225
}

src/tests/ChatTest.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import {CxWrapper} from '../main/wrapper/CxWrapper';
2+
import {CxCommandOutput} from "../main/wrapper/CxCommandOutput";
3+
import CxChat from "../main/chat/CxChat";
4+
import {anything, instance, mock, when} from 'ts-mockito';
5+
import {BaseTest} from "./BaseTest";
6+
7+
function createOutput(exitCode:number,payload:CxChat):CxCommandOutput {
8+
const output = new CxCommandOutput();
9+
output.exitCode=exitCode;
10+
output.status="";
11+
output.payload=[payload];
12+
return output;
13+
}
14+
15+
describe("Gpt Chat Cases", () => {
16+
// tests preparation
17+
const cxScanConfig = new BaseTest();
18+
const mockedWrapper: CxWrapper = mock(CxWrapper);
19+
const originalWrapper: CxWrapper = new CxWrapper(cxScanConfig);
20+
const outputSuccessful = createOutput(0,new CxChat("CONVERSATION",["RESPONSE"] ));
21+
22+
when(mockedWrapper.chat("APIKEY","FILE",anything(),anything(),anything(),anything(),anything(), anything())).thenResolve(
23+
outputSuccessful
24+
);
25+
const wrapper: CxWrapper = instance(mockedWrapper);
26+
27+
it('Gpt Chat Successful case', async () => {
28+
const cxCommandOutput = await wrapper.chat(
29+
"APIKEY",
30+
"FILE",
31+
0,
32+
"SEVERITY",
33+
"VULNERABILITY",
34+
"INPUT",
35+
"CONVERSATION",
36+
"MODEL"
37+
);
38+
expect(cxCommandOutput.exitCode).toBe(0);
39+
});
40+
41+
it('Gpt Chat Failed case', async () => {
42+
const cxCommandOutput = await originalWrapper.chat(
43+
"APIKEY",
44+
"FILE",
45+
0,
46+
"SEVERITY",
47+
"VULNERABILITY",
48+
"INPUT",
49+
"",
50+
"MODEL"
51+
);
52+
expect(cxCommandOutput.exitCode).toBe(0);
53+
expect(cxCommandOutput.payload[0].responses[0]).toBe("It seems that FILE is not available for AI Guided Remediation. Please ensure that you have opened the correct workspace or the relevant file.");
54+
});
55+
});

src/tests/MaskTest.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {CxWrapper} from '../main/wrapper/CxWrapper';
2+
import {CxCommandOutput} from "../main/wrapper/CxCommandOutput";
3+
import {BaseTest} from "./BaseTest";
4+
5+
describe("Mask cases",() => {
6+
const cxScanConfig = new BaseTest();
7+
it('Mask Successful case', async () => {
8+
const auth = new CxWrapper(cxScanConfig);
9+
const data = await auth.maskSecrets("dist/tests/data/package.json")
10+
const cxCommandOutput: CxCommandOutput = data;
11+
expect(cxCommandOutput.payload.length).toEqual(1);
12+
});
13+
14+
});

0 commit comments

Comments
 (0)