Initial commit

This commit is contained in:
Peter Stockings
2023-12-14 21:46:49 +11:00
commit ab04b77526
5 changed files with 1840 additions and 0 deletions

130
.gitignore vendored Normal file
View File

@@ -0,0 +1,130 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

1
Procfile Normal file
View File

@@ -0,0 +1 @@
web: node server.js

1510
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

25
package.json Normal file
View File

@@ -0,0 +1,25 @@
{
"name": "isolator",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"engines": {
"node": "16.8.0",
"npm": "10.1.0"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"jsdom": "^23.0.1",
"node-fetch": "^3.3.2",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"vm2": "^3.9.19"
}
}

174
server.js Normal file
View File

@@ -0,0 +1,174 @@
const express = require("express");
const bodyParser = require("body-parser");
const { VM } = require("vm2");
const { JSDOM } = require("jsdom");
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, 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 = (state, result) => {
console.log(`Status: ${state}`);
console.log(`Result: ${JSON.stringify(result, null, 2)}`);
console.log(`Logs: ${JSON.stringify(logs, null, 2)}`);
console.log(`\n`);
return {
state,
result,
logs,
};
};
// 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,
HTTP_STATUS_CODES,
Response,
JsonResponse,
HtmlResponse,
TextResponse,
},
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}`);
// Call the user function with request object
let result = await userFunction(requestObject);
return Result(States.SUCCESS, result);
} else {
return Result(States.NOT_A_FUNCTION, null);
}
} catch (err) {
if (err.message === "Script execution timed out.") {
return Result(States.TIMEOUT, null);
} else {
return Result(States.SCRIPT_ERROR, err.message || err);
}
}
}
app.post("/execute", async (req, res) => {
const { code, request } = req.body;
const timeout = req.query.timeout || TIMEOUT_MS;
const result = await executeUserCode(code, request, timeout);
res.send(result);
});
app.listen(port, () => {
console.log(`Server listening on port: ${port}`);
});