const express = require("express"); const bodyParser = require("body-parser"); const { VM } = require("vm2"); const { JSDOM } = require("jsdom"); const cheerio = require("cheerio"); const app = express(); const port = 5000; app.use(bodyParser.json()); app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); next(); }); const TIMEOUT_MS = 5000; // Set timeout to 5000 milliseconds (5 seconds) async function executeUserCode( code, requestObject, environment = {}, name, timeout = TIMEOUT_MS ) { const logs = []; const States = { SUCCESS: "SUCCESS", NOT_A_FUNCTION: "NOT_A_FUNCTION", SCRIPT_ERROR: "ERROR", TIMEOUT: "TIMEOUT", }; const HTTP_STATUS_CODES = { OK: 200, BAD_REQUEST: 400, UNAUTHORIZED: 401, FORBIDDEN: 403, NOT_FOUND: 404, INTERNAL_SERVER_ERROR: 500, BAD_GATEWAY: 502, SERVICE_UNAVAILABLE: 503, GATEWAY_TIMEOUT: 504, }; const Response = ( body = "", headers = {}, status = HTTP_STATUS_CODES.OK ) => ({ body, status, headers, }); const JsonResponse = ( body = {}, headers = {}, status = HTTP_STATUS_CODES.OK ) => ({ status, body: JSON.stringify(body), headers: { "Content-Type": "application/json", ...headers, }, }); const HtmlResponse = ( body = "", headers = {}, status = HTTP_STATUS_CODES.OK ) => ({ status, body, headers: { "Content-Type": "text/html", ...headers, }, }); const TextResponse = ( body = "", headers = {}, status = HTTP_STATUS_CODES.OK ) => ({ status, body, headers: { "Content-Type": "text/plain", ...headers, }, }); const Result = (status, result, environment) => { console.log(`Status: ${status}`); console.log(`Result: ${JSON.stringify(result, null, 2)}`); console.log(`Logs: ${JSON.stringify(logs, null, 2)}`); console.log(`Environment (post): ${JSON.stringify(environment, null, 2)}`); console.log(`\n`); return { status, result, logs, environment, }; }; //Set Function name as environment variable const FUNCTION_NAME = name; // Dynamically import node-fetch const fetch = await import("node-fetch").then((module) => module.default); const vm = new VM({ timeout, sandbox: { fetch, parseHTML: async (html) => { const dom = new JSDOM(html); return dom.window.document; }, console: { log: (...args) => { logs.push(args); console.log(...args); }, error: (...args) => { logs.push(args); console.error(...args); }, }, requestObject, environment, JSDOM, cheerio, HTTP_STATUS_CODES, Response, JsonResponse, HtmlResponse, TextResponse, FUNCTION_NAME, }, require: { external: true, }, }); try { // If the user code is a function that needs to be invoked const userFunction = vm.run(code); if (typeof userFunction === "function") { console.log(`Function: ${code}`); let requestObjectString = JSON.stringify(requestObject, null, 2); console.log(`Request: ${requestObjectString}`); console.log( `Environment (pre): ${JSON.stringify(environment, null, 2)})` ); // Call the user function with request object let result = await userFunction(requestObject); return Result(States.SUCCESS, result, environment); } else { return Result(States.NOT_A_FUNCTION, null, environment); } } catch (err) { if (err.message === "Script execution timed out.") { return Result(States.TIMEOUT, null, environment); } else { return Result(States.SCRIPT_ERROR, err.message || err, environment); } } } app.post("/execute", async (req, res) => { const { code, request, environment, name } = req.body; const timeout = req.query.timeout || TIMEOUT_MS; const result = await executeUserCode( code, request, environment, timeout, name ); res.send(result); }); app.listen(port, () => { console.log(`Server listening on port: ${port}`); });