From 270db0187f963a87e5d53cfbff3db9986cb61b26 Mon Sep 17 00:00:00 2001 From: Eric Simpson Date: Fri, 5 Jun 2026 16:35:59 -0400 Subject: [PATCH 1/3] improve security - qualify cl commands, use qtemp, dlt table after dspffd Signed-off-by: Eric Simpson --- client/src/components/gencmdxml/gencmdxml.ts | 6 +++--- client/src/components/syntaxChecker/checker.ts | 6 +++--- client/src/gencmddoc.ts | 2 +- client/src/utils.ts | 12 +++++++++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/client/src/components/gencmdxml/gencmdxml.ts b/client/src/components/gencmdxml/gencmdxml.ts index d47545e..9372d9f 100644 --- a/client/src/components/gencmdxml/gencmdxml.ts +++ b/client/src/components/gencmdxml/gencmdxml.ts @@ -58,7 +58,7 @@ export default class GenCmdXml implements IBMiComponent { const tempLib = this.getLibrary(connection); // Create QTOOLS source file (ignore error if it exists) - const createSourceFile = await connection.runCommand({ command: `CRTSRCPF ${tempLib}/QTOOLS AUT(*ALL)`, noLibList: true }) + const createSourceFile = await connection.runCommand({ command: `QSYS/CRTSRCPF ${tempLib}/QTOOLS AUT(*ALL)`, noLibList: true }) // Upload CL source const clSource = getGenCmdXmlClSrc(); @@ -66,7 +66,7 @@ export default class GenCmdXml implements IBMiComponent { // Create CL program const createProgram = await connection.runCommand({ - command: `CRTBNDCL PGM(${tempLib}/${GenCmdXml.PGM_NAME}) SRCFILE(${tempLib}/QTOOLS) DBGVIEW(*SOURCE) TEXT('${this.currentVersion} - CLLE XML Generator for Commands')`, + command: `QSYS/CRTBNDCL PGM(${tempLib}/${GenCmdXml.PGM_NAME}) SRCFILE(${tempLib}/QTOOLS) DBGVIEW(*SOURCE) TEXT('${this.currentVersion} - CLLE XML Generator for Commands')`, noLibList: true }); if (createProgram.code !== 0) { @@ -97,7 +97,7 @@ export default class GenCmdXml implements IBMiComponent { const targetName = vsCodeTools.makeid(); const callResult = await connection.runCommand({ - command: `CALL PGM(${tempLib}/${GenCmdXml.PGM_NAME}) PARM('${targetName}' '${targetCommand}')`, + command: `QSYS/CALL PGM(${tempLib}/${GenCmdXml.PGM_NAME}) PARM('${targetName}' '${targetCommand}')`, }); if (callResult.code === 0) { console.log(callResult); diff --git a/client/src/components/syntaxChecker/checker.ts b/client/src/components/syntaxChecker/checker.ts index cc9211f..abf7565 100644 --- a/client/src/components/syntaxChecker/checker.ts +++ b/client/src/components/syntaxChecker/checker.ts @@ -71,7 +71,7 @@ export class CLSyntaxChecker implements IBMiComponent { await content.writeStreamfileRaw(cppPath, cppBytes); // Create C++ module - const createModule = `CRTCPPMOD MODULE(${library}/${CLSyntaxChecker.PGM_NAME}) SRCSTMF('${cppPath}') DBGVIEW(*LIST) LANGLVL(*EXTENDED0X) OUTPUT(*PRINT)`; + const createModule = `QSYS/CRTCPPMOD MODULE(${library}/${CLSyntaxChecker.PGM_NAME}) SRCSTMF('${cppPath}') DBGVIEW(*LIST) LANGLVL(*EXTENDED0X) OUTPUT(*PRINT)`; const createModuleResult = await connection.runCommand({ command: createModule, noLibList: true @@ -81,7 +81,7 @@ export class CLSyntaxChecker implements IBMiComponent { } // Create C++ program - const createProgram = `CRTPGM PGM(${library}/${CLSyntaxChecker.PGM_NAME}) MODULE(${library}/${CLSyntaxChecker.PGM_NAME}) ACTGRP(*CALLER)`; + const createProgram = `QSYS/CRTPGM PGM(${library}/${CLSyntaxChecker.PGM_NAME}) MODULE(${library}/${CLSyntaxChecker.PGM_NAME}) ACTGRP(*CALLER)`; const createProgramResult = await connection.runCommand({ command: createProgram, noLibList: true @@ -105,7 +105,7 @@ export class CLSyntaxChecker implements IBMiComponent { } // Create UDTF - const createUdtf = `RUNSQLSTM SRCSTMF('${sqlPath}') COMMIT(*NONE) NAMING(*SYS)`; + const createUdtf = `QSYS/RUNSQLSTM SRCSTMF('${sqlPath}') COMMIT(*NONE) NAMING(*SYS)`; const createUdtfResult = await connection.runCommand({ command: createUdtf, noLibList: true diff --git a/client/src/gencmddoc.ts b/client/src/gencmddoc.ts index 05398cf..3251480 100644 --- a/client/src/gencmddoc.ts +++ b/client/src/gencmddoc.ts @@ -67,7 +67,7 @@ export class GenCmdDoc { const toStmf = `${library.replace('*', '')}_${object}`; const toDir = config.tempDir; const generateResult = await connection.runCommand({ - command: `GENCMDDOC CMD(${cmd}) GENOPT(*HTML *SHOWCHOICEPGMVAL) REPLACE(*YES) TOSTMF('${toStmf}') TODIR('${toDir}')` + command: `QSYS/GENCMDDOC CMD(${cmd}) GENOPT(*HTML *SHOWCHOICEPGMVAL) REPLACE(*YES) TOSTMF('${toStmf}') TODIR('${toDir}')` }); if (generateResult.code === 0) { diff --git a/client/src/utils.ts b/client/src/utils.ts index 339e655..02bc8da 100644 --- a/client/src/utils.ts +++ b/client/src/utils.ts @@ -77,21 +77,27 @@ export async function getFileDefinition(objectName: string, library = `*LIBL`): const content = connection.getContent(); const config = connection.getConfig(); - const tempLib = config.tempLibrary; + const tempLib = 'QTEMP'; const dateStr = Date.now().toString().substr(-6); const randomFile = `R${objectName.substring(0, 3)}${dateStr}`.substring(0, 10); const fullPath = `${tempLib}/${randomFile}`; const outfileRes = await connection.runCommand({ - command: `DSPFFD FILE(${library}/${objectName}) OUTPUT(*OUTFILE) OUTFILE(${fullPath})`, + command: `QSYS/DSPFFD FILE(${library}/${objectName}) OUTPUT(*OUTFILE) OUTFILE(${fullPath})`, environment: `ile` }); console.log(outfileRes); const resultCode = outfileRes.code || 0; if (resultCode === 0) { - const data: object[] = await content.getTable(config.tempLibrary, randomFile, randomFile, true); + const data: object[] = await content.getTable(tempLib, randomFile, randomFile, true); console.log(`Temp OUTFILE read. ${data.length} rows.`); + + connection.runCommand({ + environment: `ile`, + command: `QSYS/DLTOBJ OBJ(${fullPath}) OBJTYPE(*FILE)` + }); + return data; } } From e764d3d43e0bc01a6833690fbdf40f5a2fb60d6f Mon Sep 17 00:00:00 2001 From: Sanjula Ganepola Date: Mon, 8 Jun 2026 16:38:31 -0400 Subject: [PATCH 2/3] Remove HTML file after use Signed-off-by: Sanjula Ganepola --- client/src/gencmddoc.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/gencmddoc.ts b/client/src/gencmddoc.ts index 4a0b9ce..757e239 100644 --- a/client/src/gencmddoc.ts +++ b/client/src/gencmddoc.ts @@ -71,7 +71,10 @@ export class GenCmdDoc { }); if (generateResult.code === 0) { - const html = (await content.downloadStreamfileRaw(`${toDir}/${toStmf}`)).toString(); + const htmlFilePath = `${toDir}/${toStmf}`; + const html = (await content.downloadStreamfileRaw(htmlFilePath)).toString(); + + const result = await connection.sendCommand({ command: `rm -rf ${htmlFilePath}` }).catch(() => { }); return html; } } From a9c7092a3932f01bd44b82921ab1413a1f4eb726 Mon Sep 17 00:00:00 2001 From: Sanjula Ganepola Date: Mon, 8 Jun 2026 17:58:17 -0400 Subject: [PATCH 3/3] Temp cleanup, fix sql options Signed-off-by: Sanjula Ganepola --- client/src/components/gencmdxml/gencmdxml.ts | 9 +++++++++ client/src/components/syntaxChecker/checker.ts | 13 +++++++++++++ client/src/components/syntaxChecker/udtfSource.ts | 4 +++- client/src/gencmddoc.ts | 5 +++-- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/client/src/components/gencmdxml/gencmdxml.ts b/client/src/components/gencmdxml/gencmdxml.ts index 2ef4881..2738fb7 100644 --- a/client/src/components/gencmdxml/gencmdxml.ts +++ b/client/src/components/gencmdxml/gencmdxml.ts @@ -73,6 +73,15 @@ export default class GenCmdXml implements IBMiComponent { return { status: `Error` }; } + // Clean up + try { + // Delete temporary source member + await connection.runCommand({ + command: `QSYS/DLTF FILE(${tempLib}/QTOOLS)`, + noLibList: true + }); + } catch (error) { } + return { status: `Installed` }; } diff --git a/client/src/components/syntaxChecker/checker.ts b/client/src/components/syntaxChecker/checker.ts index 84dde2b..fb9fb5c 100644 --- a/client/src/components/syntaxChecker/checker.ts +++ b/client/src/components/syntaxChecker/checker.ts @@ -114,6 +114,19 @@ export class CLSyntaxChecker implements IBMiComponent { return { status: `Error` } } + // Clean up + try { + // Remove temporary source stream files + await connection.sendCommand({ command: `rm -rf ${cppPath}` }); + await connection.sendCommand({ command: `rm -rf ${sqlPath}` }); + + // Remove intermediate module + await connection.runCommand({ + command: `QSYS/DLTOBJ OBJ(${library}/${CLSyntaxChecker.PGM_NAME}) OBJTYPE(*MODULE)`, + noLibList: true + }); + } catch (error) { } + return { status: `Installed` }; }); } diff --git a/client/src/components/syntaxChecker/udtfSource.ts b/client/src/components/syntaxChecker/udtfSource.ts index d967ae4..e52216c 100644 --- a/client/src/components/syntaxChecker/udtfSource.ts +++ b/client/src/components/syntaxChecker/udtfSource.ts @@ -20,7 +20,9 @@ CREATE or REPLACE FUNCTION ${schema}.${CLSyntaxChecker.UDTF_NAME} ( SCRATCHPAD 8400 SPECIFIC ${CLSyntaxChecker.UDTF_NAME} EXTERNAL NAME '${schema}/${CLSyntaxChecker.PGM_NAME}' - PARAMETER STYLE DB2SQL; + PARAMETER STYLE DB2SQL + SET OPTION USRPRF=*USER, + DYNUSRPRF=*USER; LABEL on specific routine ${schema}.${CLSyntaxChecker.UDTF_NAME} IS diff --git a/client/src/gencmddoc.ts b/client/src/gencmddoc.ts index 757e239..6ac4984 100644 --- a/client/src/gencmddoc.ts +++ b/client/src/gencmddoc.ts @@ -2,6 +2,7 @@ import { getInstance } from './api/ibmi'; import { window, ViewColumn } from 'vscode'; import { NodeHtmlMarkdown } from "node-html-markdown"; import { JSDOM } from "jsdom"; +import * as path from "path"; export interface CLDoc { command: { @@ -71,10 +72,10 @@ export class GenCmdDoc { }); if (generateResult.code === 0) { - const htmlFilePath = `${toDir}/${toStmf}`; + const htmlFilePath = path.posix.join(toDir, toStmf); const html = (await content.downloadStreamfileRaw(htmlFilePath)).toString(); - const result = await connection.sendCommand({ command: `rm -rf ${htmlFilePath}` }).catch(() => { }); + await connection.sendCommand({ command: `rm -rf ${htmlFilePath}` }); return html; } }