From 2dbbe9bd54d7950b95f252fc71b1af9b13d51ee2 Mon Sep 17 00:00:00 2001 From: twells46 <173561638+twells46@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:59:29 -0600 Subject: [PATCH 1/2] Fix c++ compilation failure --- express.js | 68 ++++++++++++++----- .../compiler/ProgrammingLanguage.ts | 2 +- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/express.js b/express.js index e991a07a..f1971b1c 100644 --- a/express.js +++ b/express.js @@ -112,18 +112,63 @@ app.use('/api/ai', createAiRouter(firebaseTokenManager, config)); app.use('/api', proxy(config.dbUrl)); +console.warn('compiling...'); // If we have libkipr (C) artifacts and emsdk, we can compile. if (config.server.dependencies.libkipr_c && config.server.dependencies.emsdk_env) { app.post('/compile', (req, res) => { const startTime = Date.now(); - const language = 'C'; + const language = req.body.language; const userId = req.user?.uid; const sessionId = req.headers['x-session-id'] || req.sessionID || 'unknown'; + const ext = language === 'python' ? 'py' : language; + let cc = 'emcc'; + + // Wrap user's main() in our own "main()" that exits properly + // Required because Asyncify keeps emscripten runtime alive, which would prevent cleanup code from running + let augmentation = ` + #include + + EM_JS(void, on_stop, (), { + if (Module.context.onStop) Module.context.onStop(); + }) + + void simMainWrapper() + { + main(); + on_stop(); + emscripten_force_exit(0); + } + `; + + // C++ requires a couple changes + if (language === 'cpp') { + cc = 'em++'; + augmentation = ` + #include + + EM_JS(void, on_stop, (), { + if (Module.context.onStop) Module.context.onStop(); + }) + + extern "C" void simMainWrapper() + { + main(); + on_stop(); + emscripten_force_exit(0); + } + `; + } // Track session interaction metrics.trackSessionInteraction(sessionId, 'compile'); + if (language === 'undefined') { + return res.status(400).json({ + error: "Expected language" + }); + } + if (!('code' in req.body)) { return res.status(400).json({ error: "Expected code key in body" @@ -141,26 +186,13 @@ if (config.server.dependencies.libkipr_c && config.server.dependencies.emsdk_env // Track code size metrics.compilation.codeSize.observe({ language }, code.length); - // Wrap user's main() in our own "main()" that exits properly - // Required because Asyncify keeps emscripten runtime alive, which would prevent cleanup code from running const augmentedCode = `${code} - #include - - EM_JS(void, on_stop, (), { - if (Module.context.onStop) Module.context.onStop(); - }) - - void simMainWrapper() - { - main(); - on_stop(); - emscripten_force_exit(0); - } + ${augmentation} `; const id = uuid.v4(); - const path = `/tmp/${id}.c`; + const path = `/tmp/${id}.${ext}`; fs.writeFile(path, augmentedCode, err => { if (err) { const durationMs = Date.now() - startTime; @@ -198,7 +230,7 @@ if (config.server.dependencies.libkipr_c && config.server.dependencies.emsdk_env env['EMSDK'] = config.server.dependencies.emsdk_env.EMSDK; env['EM_CONFIG'] = config.server.dependencies.emsdk_env.EM_CONFIG; - exec(`emcc -s WASM=0 -s INVOKE_RUN=0 -s ASYNCIFY -s EXIT_RUNTIME=1 -s "EXPORTED_FUNCTIONS=['_main', '_simMainWrapper']" -I${config.server.dependencies.libkipr_c}/include -L${config.server.dependencies.libkipr_c}/lib -lkipr -o ${path}.js ${path}`, { + exec(`${cc} -s WASM=0 -s INVOKE_RUN=0 -s ASYNCIFY -s EXIT_RUNTIME=1 -s "EXPORTED_FUNCTIONS=['_main', '_simMainWrapper']" -I${config.server.dependencies.libkipr_c}/include -L${config.server.dependencies.libkipr_c}/lib -lkipr -o ${path}.js ${path}`, { env }, (err, stdout, stderr) => { const durationMs = Date.now() - startTime; @@ -451,4 +483,4 @@ app.listen(config.server.port, () => { function setCrossOriginIsolationHeaders(res) { res.header("Cross-Origin-Opener-Policy", "same-origin"); res.header("Cross-Origin-Embedder-Policy", "require-corp"); -} \ No newline at end of file +} diff --git a/src/programming/compiler/ProgrammingLanguage.ts b/src/programming/compiler/ProgrammingLanguage.ts index 6bbabc73..056c4981 100644 --- a/src/programming/compiler/ProgrammingLanguage.ts +++ b/src/programming/compiler/ProgrammingLanguage.ts @@ -11,7 +11,7 @@ namespace ProgrammingLanguage { export const DEFAULT_CODE: { [key in ProgrammingLanguage]: string } = { c: '#include \n#include \n\nint main()\n{\n printf("Hello, World!\\n");\n\n return 0;\n}\n', - cpp: '#include \n#include \n\nint main()\n{\n std::cout << "Hello, World!" << std::endl;\n\n return 0;\n}\n', + cpp: '#include \n#include \n\nint main()\n{\n std::cout << "Hello, World!" << std::endl;\n\n return 0;\n}\n', python: 'from kipr import *\n\nprint(\'Hello, World!\')', graphical: '' }; From 88a01137020cfef122e7fcec902905991f4fa6da Mon Sep 17 00:00:00 2001 From: twells46 <173561638+twells46@users.noreply.github.com> Date: Tue, 25 Nov 2025 08:54:46 -0600 Subject: [PATCH 2/2] Fix potential shell injection --- express.js | 109 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/express.js b/express.js index f1971b1c..68f3ade2 100644 --- a/express.js +++ b/express.js @@ -5,7 +5,7 @@ const bodyParser = require('body-parser'); const morgan = require('morgan'); const fs = require('fs'); const uuid = require('uuid'); -const { exec } = require('child_process'); +const { execFile } = require('child_process'); const session = require('express-session'); const csrf = require('lusca').csrf; const app = express(); @@ -121,54 +121,64 @@ if (config.server.dependencies.libkipr_c && config.server.dependencies.emsdk_env const language = req.body.language; const userId = req.user?.uid; const sessionId = req.headers['x-session-id'] || req.sessionID || 'unknown'; - const ext = language === 'python' ? 'py' : language; - let cc = 'emcc'; - - // Wrap user's main() in our own "main()" that exits properly - // Required because Asyncify keeps emscripten runtime alive, which would prevent cleanup code from running - let augmentation = ` - #include - - EM_JS(void, on_stop, (), { - if (Module.context.onStop) Module.context.onStop(); - }) + let ext; + let cc; + let augmentation; + + switch (language) { + case 'c': + ext = 'c'; + cc = 'emcc'; + + // Wrap user's main() in our own "main()" that exits properly + // Required because Asyncify keeps emscripten runtime alive, which would prevent cleanup code from running + augmentation = ` + #include + + EM_JS(void, on_stop, (), { + if (Module.context.onStop) Module.context.onStop(); + }) + + void simMainWrapper() + { + main(); + on_stop(); + emscripten_force_exit(0); + } + `; + break; + case 'cpp': + ext = 'cpp'; + cc = 'em++'; + augmentation = ` + #include + + EM_JS(void, on_stop, (), { + if (Module.context.onStop) Module.context.onStop(); + }) - void simMainWrapper() - { - main(); - on_stop(); - emscripten_force_exit(0); - } - `; - - // C++ requires a couple changes - if (language === 'cpp') { - cc = 'em++'; - augmentation = ` - #include + extern "C" void simMainWrapper() + { + main(); + on_stop(); + emscripten_force_exit(0); + } + `; + break; + case 'python': + ext = 'py'; break; + case 'graphical': + ext = 'graphical'; break; + default: + return res.status(400).json({ + error: "Expected language" + }); - EM_JS(void, on_stop, (), { - if (Module.context.onStop) Module.context.onStop(); - }) - - extern "C" void simMainWrapper() - { - main(); - on_stop(); - emscripten_force_exit(0); - } - `; } // Track session interaction metrics.trackSessionInteraction(sessionId, 'compile'); - if (language === 'undefined') { - return res.status(400).json({ - error: "Expected language" - }); - } - if (!('code' in req.body)) { return res.status(400).json({ error: "Expected code key in body" @@ -229,8 +239,19 @@ if (config.server.dependencies.libkipr_c && config.server.dependencies.emsdk_env env['PATH'] = `${config.server.dependencies.emsdk_env.PATH}:${process.env.PATH}`; env['EMSDK'] = config.server.dependencies.emsdk_env.EMSDK; env['EM_CONFIG'] = config.server.dependencies.emsdk_env.EM_CONFIG; - - exec(`${cc} -s WASM=0 -s INVOKE_RUN=0 -s ASYNCIFY -s EXIT_RUNTIME=1 -s "EXPORTED_FUNCTIONS=['_main', '_simMainWrapper']" -I${config.server.dependencies.libkipr_c}/include -L${config.server.dependencies.libkipr_c}/lib -lkipr -o ${path}.js ${path}`, { + const args = [ + '-s', 'WASM=0', + '-s', 'INVOKE_RUN=0', + '-s', 'ASYNCIFY', + '-s', 'EXIT_RUNTIME=1', + '-s', "EXPORTED_FUNCTIONS=['_main', '_simMainWrapper']", + `-I${config.server.dependencies.libkipr_c}/include`, + `-L${config.server.dependencies.libkipr_c}/lib`, + '-lkipr', + '-o', `${path}.js`, + path + ]; + execFile(cc, args, { env }, (err, stdout, stderr) => { const durationMs = Date.now() - startTime;