├── CNAME ├── _config.yml ├── .gitignore ├── runners ├── lxc │ ├── lxc-check-container.bash │ ├── lxc-dispose.bash │ ├── lxc-init.bash │ └── lxc-execute.bash ├── win32 │ ├── js.bat │ ├── ruby.bat │ ├── python2.bat │ ├── python3.bat │ ├── bat.bat │ └── go.bat └── unix │ ├── bash.sh │ ├── js.sh │ ├── ruby.sh │ ├── python2.sh │ ├── python3.sh │ ├── go.sh │ ├── java.sh │ ├── cpp.sh │ └── gcc.sh ├── .npmignore ├── src ├── index.ts ├── functions │ ├── environment.ts │ ├── supported-languages.ts │ └── executor.ts ├── constants.ts └── lxc.ts ├── SECURITY.md ├── .github └── workflows │ ├── node.js.yml │ └── codeql-analysis.yml ├── package.json ├── LICENSE ├── tests └── all.test.js ├── README.md └── tsconfig.json /CNAME: -------------------------------------------------------------------------------- 1 | cee.koeck.dev -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | runners/lxc/i 4 | runners/lxc/lockfile -------------------------------------------------------------------------------- /runners/lxc/lxc-check-container.bash: -------------------------------------------------------------------------------- 1 | lxc-attach --clear-env -n $1 -- \ 2 | /bin/bash -l -c "id" -------------------------------------------------------------------------------- /runners/lxc/lxc-dispose.bash: -------------------------------------------------------------------------------- 1 | runners=$1 2 | 3 | for i in {1..$runners}; do 4 | userdel runner$i 5 | done 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | tests/ 2 | src/ 3 | _config.yml 4 | .github/ 5 | runners/lxc/i 6 | runners/lxc/lockfile 7 | CNAME 8 | SECURITY.md -------------------------------------------------------------------------------- /runners/win32/js.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | C: 3 | cd %1 >nul 2>&1 4 | set /p args=nul 2>&1 4 | set /p args=nul 2>&1 4 | set /p args=nul 2>&1 4 | set /p args=nul 2>&1 4 | copy code.code code.bat >nul 2>&1 5 | set /p args=nul 2>&1 4 | copy code.code code.go >nul 2>&1 5 | set /p args=> /etc/security/limits.conf 9 | echo "runner$i hard nproc $nproc" >> /etc/security/limits.conf 10 | echo "runner$i soft nofile $nofile" >> /etc/security/limits.conf 11 | echo "runner$i hard nofile $nofile" >> /etc/security/limits.conf 12 | done 13 | -------------------------------------------------------------------------------- /src/functions/supported-languages.ts: -------------------------------------------------------------------------------- 1 | import { parseEnvironment } from "./environment"; 2 | import { platform } from "os"; 3 | import { readdirSync } from "fs"; 4 | import { join } from "path"; 5 | 6 | export default function getSupportedLanguages(): string[] { 7 | const env = parseEnvironment(platform()); 8 | const runnerpath = join(__dirname, "..", "..", "runners", env); 9 | const files = readdirSync(runnerpath); 10 | return files.map((file) => { 11 | return file.split(".")[0]; 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | > 0.4.1 | :white_check_mark: | 8 | | < 0.4.1 | :x: | 9 | 10 | Security updates are available since v0.4.1 11 | 12 | ## Reporting a Vulnerability 13 | 14 | If you discover a security vulnerability, we ask you to report it to us via email ([simonkoeck04@gmail.com](mailto:simonkoeck04@gmail.com)). Please don't create a public issue. 15 | 16 | Thanks for your contribution. 17 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export enum Language { 4 | PYTHON3 = "python3", 5 | PYTHON2 = "python2", 6 | C = "gcc", 7 | BASH = "bash", 8 | BATCH = "bat", 9 | JAVASCRIPT = "js", 10 | RUBY = "ruby", 11 | CPP = "cpp", 12 | JAVA = "java", 13 | GO = "go", 14 | } 15 | 16 | export enum Environment { 17 | UNIX = "unix", 18 | WIN = "win32", 19 | } 20 | 21 | export interface IExecuteOptions { 22 | timeout?: number; 23 | } 24 | 25 | export const defaultExecutionTimeout = 5; 26 | 27 | export interface ILxcInitOptions { 28 | runners?: number; 29 | maxProcesses?: number; 30 | maxFiles?: number; 31 | } 32 | 33 | export const defaultLxcInitOptions: ILxcInitOptions = { 34 | runners: 100, 35 | maxProcesses: 64, 36 | maxFiles: 2048, 37 | }; 38 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | branches: [main] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | matrix: 18 | node-version: [14.x] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - run: npm ci 27 | - run: npm run build --if-present 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-execution-engine", 3 | "version": "0.4.6", 4 | "description": "A fast and secure Code-Execution-Engine in Javascript.", 5 | "main": "lib/index.js", 6 | "types": "./lib/index.d.ts", 7 | "scripts": { 8 | "test": "jest tests/*.js", 9 | "build": "tsc -p ." 10 | }, 11 | "keywords": [ 12 | "Code", 13 | "Execution", 14 | "Engine", 15 | "Runner" 16 | ], 17 | "author": "Simon Köck", 18 | "license": "MIT", 19 | "devDependencies": { 20 | "jest": "^26.6.3", 21 | "typescript": "^4.2.3" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/simonkoeck/code-execution-engine.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/simonkoeck/code-execution-engine/issues" 29 | }, 30 | "homepage": "https://github.com/simonkoeck/code-execution-engine#readme", 31 | "dependencies": { 32 | "@types/node": "^14.14.36" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Simon Köck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /runners/lxc/lxc-execute.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir="$( cd "$( dirname "$0" )" && pwd )" 4 | 5 | id=$1 6 | containername=$2 7 | 8 | 9 | touch $dir/lockfile 10 | 11 | # process incrementor 12 | exec 200>$dir/lockfile 13 | flock 200 14 | 15 | touch $dir/i 16 | runner=$(cat $dir/i) 17 | let 'runner = runner % 150 + 1' 18 | 19 | echo $runner > $dir/i 20 | exec 200>&- 21 | 22 | # prevent users from spying on each other 23 | lxc-attach --clear-env -n $containername -- \ 24 | /bin/bash -c " 25 | chown runner$runner: -R /tmp/$id 26 | chmod 700 /tmp/$id 27 | " > /dev/null 2>&1 28 | 29 | # runner 30 | timeout -s KILL 20 \ 31 | lxc-attach --clear-env -n $containername -- \ 32 | /bin/bash -l -c "runuser runner$runner /tmp/$id/runner.sh /tmp/$id" 33 | 34 | 35 | lxc-attach --clear-env -n $containername -- \ 36 | /bin/bash -c " 37 | while pgrep -u runner$runner > /dev/null 38 | do 39 | pkill -u runner$runner --signal SIGKILL 40 | done 41 | find /tmp -user runner$runner -delete 42 | find /var/tmp -user runner$runner -delete 43 | find /var/lock -user runner$runner -delete 44 | find /dev/shm -user runner$runner -delete 45 | find /run/lock -user runner$runner -delete 46 | " > /dev/null 2>&1 & 47 | -------------------------------------------------------------------------------- /tests/all.test.js: -------------------------------------------------------------------------------- 1 | var cee = require("../lib/index"); 2 | 3 | describe("Testing Basic Languages", () => { 4 | test("Python3", () => { 5 | return cee 6 | .execute("x = input(); print(x);", cee.languages.PYTHON3, [], "123") 7 | .then((response) => { 8 | expect(response).toBe("123"); 9 | }); 10 | }, 30000); 11 | test("Python2", () => { 12 | return cee 13 | .execute("print '123'", cee.languages.PYTHON2) 14 | .then((response) => { 15 | expect(response).toBe("123"); 16 | }); 17 | }, 30000); 18 | test("Bash", () => { 19 | return cee.execute("echo 123", cee.languages.BASH).then((response) => { 20 | expect(response).toBe("123"); 21 | }); 22 | }, 30000); 23 | test("JS", () => { 24 | return cee 25 | .execute("console.log(123)", cee.languages.JAVASCRIPT) 26 | .then((response) => { 27 | expect(response).toBe("123"); 28 | }); 29 | }, 30000); 30 | test("Ruby", () => { 31 | return cee.execute("puts 123", cee.languages.RUBY).then((response) => { 32 | expect(response).toBe("123"); 33 | }); 34 | }, 30000); 35 | test("C", () => { 36 | return cee 37 | .execute( 38 | `#include 39 | 40 | int main() { 41 | printf("123"); 42 | return 0; 43 | } 44 | `, 45 | cee.languages.C 46 | ) 47 | .then((response) => { 48 | expect(response).toBe("123"); 49 | }); 50 | }, 30000); 51 | test("C++", () => { 52 | return cee 53 | .execute( 54 | `#include "iostream" 55 | int main(void) 56 | { 57 | std::cout << "123"; 58 | return 0; 59 | }`, 60 | cee.languages.CPP 61 | ) 62 | .then((response) => { 63 | expect(response).toBe("123"); 64 | }); 65 | }, 30000); 66 | test("Java", () => { 67 | return cee 68 | .execute( 69 | `public class HelloWorld 70 | { 71 | 72 | public static void main (String[] args) 73 | { 74 | System.out.println("123"); 75 | } 76 | }`, 77 | cee.languages.JAVA 78 | ) 79 | .then((response) => { 80 | expect(response).toBe("123"); 81 | }); 82 | }, 30000); 83 | }); 84 | 85 | describe("Testing Basic Functions", () => { 86 | test("getSupportedLanguages()", () => { 87 | var l = cee.getSupportedLanguages(); 88 | expect(l.length).toBeGreaterThan(0); 89 | }, 30000); 90 | }); 91 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '26 1 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /src/functions/executor.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { platform, tmpdir } from "os"; 4 | import { writeFileSync, mkdirSync, rmdirSync } from "fs"; 5 | import { exec } from "child_process"; 6 | import { 7 | Language, 8 | Environment, 9 | IExecuteOptions, 10 | defaultExecutionTimeout, 11 | } from "../constants"; 12 | import { join } from "path"; 13 | import { parseEnvironment } from "./environment"; 14 | 15 | /** 16 | * 17 | * @param input The code that should be executed 18 | * @param language Language of the input parameter 19 | * @param args Array of command line arguments 20 | * @param stdin stdin of the executed code 21 | * @returns Stdout of the code or an exception if stderr is not empty 22 | */ 23 | export default async function execute( 24 | input: string, 25 | language: Language, 26 | args: string[] = [], 27 | stdin: string = "", 28 | options: IExecuteOptions = { timeout: defaultExecutionTimeout } 29 | ): Promise { 30 | // Check Platform 31 | const env = parseEnvironment(platform()); 32 | 33 | const id = new Date().getTime() + "_" + Math.floor(Math.random() * 10000); 34 | 35 | const tempFolder: string = join(tmpdir(), id); 36 | 37 | try { 38 | mkdirSync(tempFolder); 39 | writeFileSync(`${tempFolder}/code.code`, input); 40 | writeFileSync( 41 | `${tempFolder}/args.args`, 42 | env == Environment.WIN ? args.join(" ") : args.join("\n") 43 | ); 44 | writeFileSync(`${tempFolder}/stdin.stdin`, stdin); 45 | writeFileSync( 46 | `${tempFolder}/timeout.timeout`, 47 | (options.timeout || defaultExecutionTimeout).toString() 48 | ); 49 | } catch (e) { 50 | throw e; 51 | } 52 | 53 | var command = env === Environment.WIN ? "" : "sh"; 54 | var filetype = env === Environment.WIN ? "bat" : "sh"; 55 | 56 | // Path to runner file 57 | var runnerpath = join( 58 | __dirname, 59 | "..", 60 | "..", 61 | "runners", 62 | env, 63 | `${language}.${filetype}` 64 | ); 65 | 66 | // Execute code 67 | return new Promise((resolve, reject) => { 68 | // if username has a space in it 69 | var runcmd = 70 | env == Environment.WIN 71 | ? `${command} "${runnerpath}" ${tempFolder}` 72 | : `${command} ${runnerpath} ${tempFolder}`; 73 | // run command 74 | exec(runcmd, (err, stdout, stderr) => { 75 | // Delete created folder 76 | try { 77 | rmdirSync(tempFolder, { recursive: true }); 78 | } catch (e) {} 79 | 80 | if (stderr) { 81 | // Remove newline from stderr 82 | if (stderr.endsWith("\n")) stderr = stderr.slice(0, -1); 83 | return reject(stderr); 84 | } 85 | 86 | // Remove newline from stdout 87 | if (stdout.endsWith("\n")) stdout = stdout.slice(0, -1); 88 | 89 | resolve(stdout); 90 | }); 91 | }); 92 | } 93 | -------------------------------------------------------------------------------- /src/lxc.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { exec } from "child_process"; 4 | import { mkdirSync, writeFileSync, readFileSync } from "fs"; 5 | import { platform } from "os"; 6 | import { 7 | defaultExecutionTimeout, 8 | defaultLxcInitOptions, 9 | IExecuteOptions, 10 | ILxcInitOptions, 11 | Language, 12 | } from "./constants"; 13 | 14 | class LXC { 15 | LXC_ROOT_FS: string; 16 | CONTAINER: string; 17 | 18 | constructor(container: string) { 19 | this.CONTAINER = container; 20 | // Check OS 21 | const os = platform(); 22 | if (os !== "linux") { 23 | throw { 24 | name: "UnsupportedOSError", 25 | message: "LXCs only work on linux machines", 26 | }; 27 | } 28 | 29 | this.LXC_ROOT_FS = `${process.env.HOME}/.local/share/lxc/${container}/rootfs`; 30 | // Check if Container exists 31 | exec( 32 | `bash ${__dirname}/../runners/lxc/lxc-check-container.bash ${container}`, 33 | (err, stdout, stderr) => { 34 | if (stdout === "") 35 | throw { 36 | name: "LXCContainerNotFound", 37 | message: "Your specified LXC-Container couldn't be found.", 38 | }; 39 | } 40 | ); 41 | } 42 | 43 | /** 44 | * Run this function the first time, after installing the container. Please be patient, this could take a while 45 | * 46 | * @param options Pass options for the initialization to the function. 47 | * @returns 48 | */ 49 | init(options: ILxcInitOptions = defaultLxcInitOptions) { 50 | try { 51 | // Copy .sh file to correct location 52 | const shFile = readFileSync( 53 | `${__dirname}/../runners/lxc/lxc-init.bash`, 54 | "utf-8" 55 | ); 56 | writeFileSync(`${this.LXC_ROOT_FS}/tmp/lxc-init.bash`, shFile); 57 | 58 | exec(`chmod +x ${this.LXC_ROOT_FS}/tmp/lxc-init.bash`); 59 | } catch (e) { 60 | throw e; 61 | } 62 | 63 | return new Promise((resolve, reject) => { 64 | exec( 65 | `lxc-attach --clear-env -n ${ 66 | this.CONTAINER 67 | } -- bash /tmp/lxc-init.bash ${ 68 | options.runners || defaultLxcInitOptions.runners 69 | } ${options.maxProcesses || defaultLxcInitOptions.maxProcesses} ${ 70 | options.maxFiles || defaultLxcInitOptions.maxFiles 71 | }`, 72 | (err, stdout, stderr) => { 73 | if (err) return reject(err); 74 | if (stderr) return reject(stderr); 75 | 76 | // Remove newline from stdout 77 | if (stdout.endsWith("\n")) stdout = stdout.slice(0, -1); 78 | 79 | resolve(stdout); 80 | } 81 | ); 82 | }); 83 | } 84 | 85 | /** 86 | * 87 | * @param input The code that should be executed 88 | * @param language Language of the input parameter 89 | * @param args Array of command line arguments 90 | * @param stdin stdin of the executed code 91 | * @returns Stdout of the code or an exception if stderr is not empty 92 | */ 93 | execute( 94 | input: string, 95 | language: Language, 96 | args: string[] = [], 97 | stdin: string = "", 98 | options: IExecuteOptions = { timeout: defaultExecutionTimeout } 99 | ) { 100 | const id = new Date().getTime() + "_" + Math.floor(Math.random() * 10000); 101 | 102 | try { 103 | mkdirSync(`${this.LXC_ROOT_FS}/tmp/${id}`); 104 | writeFileSync(`${this.LXC_ROOT_FS}/tmp/${id}/code.code`, input); 105 | writeFileSync(`${this.LXC_ROOT_FS}/tmp/${id}/args.args`, args.join("\n")); 106 | writeFileSync(`${this.LXC_ROOT_FS}/tmp/${id}/stdin.stdin`, stdin); 107 | writeFileSync( 108 | `${this.LXC_ROOT_FS}/tmp/${id}/timeout.timeout`, 109 | (options.timeout || defaultExecutionTimeout).toString() 110 | ); 111 | 112 | // Copy .sh file to correct location 113 | const shFile = readFileSync( 114 | `${__dirname}/../runners/unix/${language}.sh`, 115 | "utf-8" 116 | ); 117 | writeFileSync(`${this.LXC_ROOT_FS}/tmp/${id}/runner.sh`, shFile); 118 | 119 | exec(`chmod +x ${this.LXC_ROOT_FS}/tmp/${id}/runner.sh`); 120 | } catch (e) { 121 | throw e; 122 | } 123 | 124 | return new Promise((resolve, reject) => { 125 | exec( 126 | `bash ${__dirname}/../runners/lxc/lxc-execute.bash ${id} ${this.CONTAINER}`, 127 | (err, stdout, stderr) => { 128 | if (err) return reject(err); 129 | if (stderr) return reject(stderr); 130 | 131 | // Remove newline from stdout 132 | if (stdout.endsWith("\n")) stdout = stdout.slice(0, -1); 133 | 134 | resolve(stdout); 135 | } 136 | ); 137 | }); 138 | } 139 | } 140 | 141 | export default LXC; 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg?style=for-the-badge)](https://GitHub.com/simonkoeck/code-execution-engine/graphs/commit-activity) [![GitHub license](https://img.shields.io/github/license/simonkoeck/code-execution-engine?style=for-the-badge)](https://github.com/simonkoeck/code-execution-engine/blob/master/LICENSE) ![npm](https://img.shields.io/npm/dw/code-execution-engine?style=for-the-badge) ![GitHub last commit](https://img.shields.io/github/last-commit/simonkoeck/code-execution-engine?style=for-the-badge) [![npm](https://img.shields.io/npm/v/code-execution-engine?style=for-the-badge)](https://www.npmjs.com/package/code-execution-engine) 2 | 3 | # Code-Execution-Engine 4 | 5 | A fast and secure Code-Execution-Engine in Javascript. 6 | 7 | ❗This package is not secure by default. Visit [Security](#security) for production projects.❗ 8 | 9 | ## Table of Contents 10 | 11 | - [Installation](#installation) 12 | 13 | - [Usage](#usage) 14 | 15 | - [Supported Languages](#supported-languages) 16 | 17 | - [Security](#security) 18 | 19 | - [LXC](#lxc) 20 | 21 | - [Contributing](#contributing) 22 | 23 | - [Credits](#credits) 24 | 25 | - [License](#license) 26 | 27 | 28 | 29 | ## Installation 30 | 31 | Use the package manager [npm](https://www.npmjs.com) to install Code-Execution-Engine. 32 | 33 | ```bash 34 | $ npm install code-execution-engine 35 | ``` 36 | 37 | Or use the [yarn](https://yarnpkg.com) package manager. 38 | 39 | ```bash 40 | $ yarn add code-execution-engine 41 | ``` 42 | 43 | 44 | 45 | ## Usage 46 | 47 | ```javascript 48 | const cee = require("code-execution-engine"); 49 | 50 | cee 51 | .execute("print('Hello World')", cee.languages.PYTHON3, [], "", { 52 | timeout: 5, 53 | }) 54 | .then((result) => { 55 | console.log(result); 56 | }) 57 | .catch((error) => { 58 | console.error(error); 59 | }); 60 | ``` 61 | 62 | ### execute(_input_, _language_, [_args_], [_stdin_], [_options_]) → Promise<String> 63 | 64 | Returns the result (stdout) of the executed code. If stderr is not empty, an exception will be thrown with the content of stderr. 65 | 66 | **_input_**: string – The source code that should be executed. 67 | 68 | **_language_**: cee.Language – Pass the language the code is written in, for example, `cee.languages.PYTHON3`. [Supported Lanuages](#supported-languages) 69 | 70 | **_args_**: string[] - Command-Line arguments that are passed to the script 71 | 72 | **_stdin_**: string - Set the stdin for the script 73 | 74 | **_options_**: IExecuteOptions 75 | 76 |       _timeout_: number - Max execution time of the script. This option doesn't work on windows. Defaults to `5` 77 | 78 | 79 | 80 | ## Supported Languages 81 | 82 | Get the supported languages on your platform by calling 83 | 84 | ```js 85 | cee.getSupportedLanguages(); 86 | ``` 87 | 88 | | | Linux / MacOS | Windows | 89 | | :----------: | :-----------: | :-----: | 90 | | `Python3` | ✔️ | ✔️ | 91 | | `Python2` | ✔️ | ✔️ | 92 | | `Javascript` | ✔️ | ✔️ | 93 | | `Ruby` | ✔️ | ✔️ | 94 | | `Go` | ✔️ | ✔️ | 95 | | `Batch` | | ✔️ | 96 | | `Bash` | ✔️ | | 97 | | `C` | ✔️ | | 98 | | `C++` | ✔️ | | 99 | | `Java` | ✔️ | | 100 | 101 | More supported languages coming soon. 102 | 103 | 104 | 105 | ## Security 106 | 107 | IMPORTANT: There are no security modules available for windows! 108 | 109 | ```javascript 110 | const cee = require("code-execution-engine"); 111 | 112 | const executor = new cee.LXC("[NAME OF YOUR LXC-CONTAINER]"); 113 | 114 | // Run this function after the installation of the container 115 | executor.init({ 116 | runners: 150, 117 | // limitations per runner 118 | maxProcesses: 64, 119 | maxFiles: 2048, 120 | }); 121 | 122 | executor 123 | .execute("echo 'Im in a secure environment!'", cee.languages.BASH) 124 | .then((result) => { 125 | console.log(result); 126 | }) 127 | .catch((error) => { 128 | console.error(error); 129 | }); 130 | ``` 131 | 132 | To use LXC, follow the instructions below to set up LXC. 133 | 134 | 135 | 136 | ## LXC 137 | 138 | LXC's are Linux containers, that run the code in a different and secure environment. To use them, you need to install them first. LXC's are only available on Linux-Systems. 139 | 140 | To use this package with LXC, you need to install an unprivileged container. 141 | 142 | **Follow these instructions: [linuxcontainers.org](https://linuxcontainers.org/lxc/getting-started/#creating-unprivileged-containers-as-a-user)** 143 | 144 | 145 | 146 | ## Contributing 147 | 148 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 149 | 150 | Please make sure to update tests as appropriate. 151 | 152 | 153 | 154 | ## Credits 155 | 156 | Inspired by: [engineer-man/piston](https://github.com/engineer-man/piston) 157 | 158 | 159 | 160 | ## License 161 | 162 | [MIT](https://choosealicense.com/licenses/mit/) 163 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 8 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 13 | "declaration": true /* Generates corresponding '.d.ts' file. */, 14 | "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, 15 | "sourceMap": true /* Generates corresponding '.map' file. */, 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./lib" /* Redirect output structure to the directory. */, 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true /* Enable all strict type-checking options. */, 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 44 | 45 | /* Module Resolution Options */ 46 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 47 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 48 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 49 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 50 | // "typeRoots": [], /* List of folders to include type definitions from. */ 51 | // "types": [], /* Type declaration files to be included in compilation. */ 52 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 53 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 56 | 57 | /* Source Map Options */ 58 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 61 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 62 | 63 | /* Experimental Options */ 64 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 65 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 66 | 67 | /* Advanced Options */ 68 | "skipLibCheck": true /* Skip type checking of declaration files. */, 69 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 70 | }, 71 | "include": ["src"] 72 | } 73 | --------------------------------------------------------------------------------