├── .gitignore ├── API ├── DockerSandbox.js ├── DockerTimeout.sh ├── Payload │ ├── create_user.sql │ ├── destroy_user.sql │ ├── javaRunner.sh │ ├── script.sh │ └── sql_runner.sh ├── app.js ├── compilers.js ├── index.html ├── langs ├── langs.js ├── package.json └── temp │ └── ReadMe.txt ├── Arch.png ├── LICENSE ├── README.md └── Setup ├── Dockerfile ├── Install_12.04.sh ├── Install_13.04_13.10.sh ├── Install_14.04.sh ├── Install_16.04.sh ├── Install_Cent6.sh ├── Install_Mint.sh └── UpdateDocker.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | -------------------------------------------------------------------------------- /API/DockerSandbox.js: -------------------------------------------------------------------------------- 1 | /* 2 | *File: DockerSandbox.js 3 | *Author: Osman Ali Mian/Asad Memon 4 | *Created: 3rd June 2014 5 | *Revised on: 25th June 2014 (Added folder mount permission and changed executing user to nobody using -u argument) 6 | *Revised on: 30th June 2014 (Changed the way errors are logged on console, added language name into error messages) 7 | */ 8 | 9 | 10 | /** 11 | * @Constructor 12 | * @variable DockerSandbox 13 | * @description This constructor stores all the arguments needed to prepare and execute a Docker Sandbox 14 | * @param {Number} timeout_value: The Time_out limit for code execution in Docker 15 | * @param {String} path: The current working directory where the current API folder is kept 16 | * @param {String} folder: The name of the folder that would be mounted/shared with Docker container, this will be concatenated with path 17 | * @param {String} vm_name: The TAG of the Docker VM that we wish to execute 18 | * @param {String} compiler_name: The compiler/interpretor to use for carrying out the translation 19 | * @param {String} file_name: The file_name to which source code will be written 20 | * @param {String} code: The actual code 21 | * @param {String} output_command: Used in case of compilers only, to execute the object code, send " " in case of interpretors 22 | */ 23 | var DockerSandbox = function(timeout_value,path,folder,vm_name,compiler_name,file_name,code,output_command,languageName,e_arguments,stdin_data) 24 | { 25 | 26 | this.timeout_value=timeout_value; 27 | this.path=path; 28 | this.folder=folder; 29 | this.vm_name=vm_name; 30 | this.compiler_name=compiler_name; 31 | this.file_name=file_name; 32 | this.code = code; 33 | this.output_command=output_command; 34 | this.langName=languageName; 35 | this.extra_arguments=e_arguments; 36 | this.stdin_data=stdin_data; 37 | } 38 | 39 | 40 | /** 41 | * @function 42 | * @name DockerSandbox.run 43 | * @description Function that first prepares the Docker environment and then executes the Docker sandbox 44 | * @param {Function pointer} success ????? 45 | */ 46 | DockerSandbox.prototype.run = function(success) 47 | { 48 | var sandbox = this; 49 | 50 | this.prepare( function(){ 51 | sandbox.execute(success); 52 | }); 53 | } 54 | 55 | 56 | /* 57 | * @function 58 | * @name DockerSandbox.prepare 59 | * @description Function that creates a directory with the folder name already provided through constructor 60 | * and then copies contents of folder named Payload to the created folder, this newly created folder will be mounted 61 | * on the Docker Container. A file with the name specified in file_name variable of this class is created and all the 62 | * code written in 'code' variable of this class is copied into this file. 63 | * Summary: This function produces a folder that contains the source file and 2 scripts, this folder is mounted to our 64 | * Docker container when we run it. 65 | * @param {Function pointer} success ????? 66 | */ 67 | DockerSandbox.prototype.prepare = function(success) 68 | { 69 | var exec = require('child_process').exec; 70 | var fs = require('fs'); 71 | var sandbox = this; 72 | 73 | exec("mkdir "+ this.path+this.folder + " && cp "+this.path+"/Payload/* "+this.path+this.folder+"&& chmod 777 "+ this.path+this.folder,function(st) 74 | { 75 | fs.writeFile(sandbox.path + sandbox.folder+"/" + sandbox.file_name, sandbox.code,function(err) 76 | { 77 | if (err) 78 | { 79 | console.log(err); 80 | } 81 | else 82 | { 83 | console.log(sandbox.langName+" file was saved!"); 84 | exec("chmod 777 \'"+sandbox.path+sandbox.folder+"/"+sandbox.file_name+"\'") 85 | 86 | fs.writeFile(sandbox.path + sandbox.folder+"/inputFile", sandbox.stdin_data,function(err) 87 | { 88 | if (err) 89 | { 90 | console.log(err); 91 | } 92 | else 93 | { 94 | console.log("Input file was saved!"); 95 | success(); 96 | } 97 | }); 98 | 99 | 100 | } 101 | }); 102 | 103 | 104 | 105 | 106 | }); 107 | 108 | } 109 | 110 | /* 111 | * @function 112 | * @name DockerSandbox.execute 113 | * @precondition: DockerSandbox.prepare() has successfully completed 114 | * @description: This function takes the newly created folder prepared by DockerSandbox.prepare() and spawns a Docker container 115 | * with the folder mounted inside the container with the name '/usercode/' and calls the script.sh file present in that folder 116 | * to carry out the compilation. The Sandbox is spawned ASYNCHRONOUSLY and is supervised for a timeout limit specified in timeout_limit 117 | * variable in this class. This function keeps checking for the file "Completed" until the file is created by script.sh or the timeout occurs 118 | * In case of timeout an error message is returned back, otherwise the contents of the file (which could be the program output or log of 119 | * compilation error) is returned. In the end the function deletes the temporary folder and exits 120 | * 121 | * Summary: Run the Docker container and execute script.sh inside it. Return the output generated and delete the mounted folder 122 | * 123 | * @param {Function pointer} success ????? 124 | */ 125 | 126 | DockerSandbox.prototype.execute = function(success) 127 | { 128 | var exec = require('child_process').exec; 129 | var fs = require('fs'); 130 | var myC = 0; //variable to enforce the timeout_value 131 | var sandbox = this; 132 | 133 | //this statement is what is executed 134 | var st = this.path+'DockerTimeout.sh ' + this.timeout_value + 's -u mysql -e \'NODE_PATH=/usr/local/lib/node_modules\' -i -t -v "' + this.path + this.folder + '":/usercode ' + this.vm_name + ' /usercode/script.sh ' + this.compiler_name + ' ' + this.file_name + ' ' + this.output_command+ ' ' + this.extra_arguments; 135 | 136 | //log the statement in console 137 | console.log(st); 138 | 139 | //execute the Docker, This is done ASYNCHRONOUSLY 140 | exec(st); 141 | console.log("------------------------------") 142 | //Check For File named "completed" after every 1 second 143 | var intid = setInterval(function() 144 | { 145 | //Displaying the checking message after 1 second interval, testing purposes only 146 | //console.log("Checking " + sandbox.path+sandbox.folder + ": for completion: " + myC); 147 | 148 | myC = myC + 1; 149 | 150 | fs.readFile(sandbox.path + sandbox.folder + '/completed', 'utf8', function(err, data) { 151 | 152 | //if file is not available yet and the file interval is not yet up carry on 153 | if (err && myC < sandbox.timeout_value) 154 | { 155 | //console.log(err); 156 | return; 157 | } 158 | //if file is found simply display a message and proceed 159 | else if (myC < sandbox.timeout_value) 160 | { 161 | console.log("DONE") 162 | //check for possible errors 163 | fs.readFile(sandbox.path + sandbox.folder + '/errors', 'utf8', function(err2, data2) 164 | { 165 | if(!data2) data2="" 166 | console.log("Error file: ") 167 | console.log(data2) 168 | 169 | console.log("Main File") 170 | console.log(data) 171 | 172 | var lines = data.toString().split('*-COMPILEBOX::ENDOFOUTPUT-*') 173 | data=lines[0] 174 | var time=lines[1] 175 | 176 | console.log("Time: ") 177 | console.log(time) 178 | 179 | 180 | success(data,time,data2) 181 | }); 182 | 183 | //return the data to the calling functoin 184 | 185 | } 186 | //if time is up. Save an error message to the data variable 187 | else 188 | { 189 | //Since the time is up, we take the partial output and return it. 190 | fs.readFile(sandbox.path + sandbox.folder + '/logfile.txt', 'utf8', function(err, data){ 191 | if (!data) data = ""; 192 | data += "\nExecution Timed Out"; 193 | console.log("Timed Out: "+sandbox.folder+" "+sandbox.langName) 194 | fs.readFile(sandbox.path + sandbox.folder + '/errors', 'utf8', function(err2, data2) 195 | { 196 | if(!data2) data2="" 197 | 198 | var lines = data.toString().split('*---*') 199 | data=lines[0] 200 | var time=lines[1] 201 | 202 | console.log("Time: ") 203 | console.log(time) 204 | 205 | success(data,data2) 206 | }); 207 | }); 208 | 209 | } 210 | 211 | 212 | //now remove the temporary directory 213 | console.log("ATTEMPTING TO REMOVE: " + sandbox.folder); 214 | console.log("------------------------------") 215 | exec("rm -r " + sandbox.folder); 216 | 217 | 218 | clearInterval(intid); 219 | }); 220 | }, 1000); 221 | 222 | } 223 | 224 | 225 | module.exports = DockerSandbox; 226 | -------------------------------------------------------------------------------- /API/DockerTimeout.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | to=$1 5 | shift 6 | 7 | cont=$(docker run --rm -d "$@") 8 | code=$(timeout "$to" docker wait "$cont" || true) 9 | docker kill $cont &> /dev/null 10 | echo -n 'status: ' 11 | if [ -z "$code" ]; then 12 | echo timeout 13 | else 14 | echo exited: $code 15 | fi 16 | 17 | echo output: 18 | # pipe to sed simply for pretty nice indentation 19 | docker logs $cont | sed 's/^/\t/' 20 | 21 | docker rm $cont &> /dev/null 22 | -------------------------------------------------------------------------------- /API/Payload/create_user.sql: -------------------------------------------------------------------------------- 1 | create user 'test' IDENTIFIED BY 'test123'; 2 | create database ri_db; 3 | grant all privileges on ri_db.* to 'test'; 4 | -------------------------------------------------------------------------------- /API/Payload/destroy_user.sql: -------------------------------------------------------------------------------- 1 | 2 | drop database ri_db; 3 | drop user test; 4 | -------------------------------------------------------------------------------- /API/Payload/javaRunner.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | 4 | ###################################################################################### 5 | # This script is specifically made to handle the compilation of java 6 | # The idea is to cater with the difference of class name(s) inside file 7 | # and the file name itself when executing the java .class files 8 | # 9 | # This script is executed after javac compiler has already compiled the file 10 | # into classes 11 | # 12 | # The cd /usercode/ command is Docker case specific. Description is mentioned 13 | # below. 14 | # 15 | # The script works as follows 16 | # 1. Search the current directory for all files that have a .class extension 17 | # 2. Run javap on the class name 18 | # The javap tool disassembles compiled Java™ files and prints 19 | # out a representation of the Java program. This may be helpful 20 | # when the original source code is no longer available on a system. 21 | # source: publib.boulder.ibm.com/infocenter/iseries/v5r4/topic/rzaha/javap.htm 22 | # 23 | # 3. If the class contains the main function run the java command to run it and exit 24 | # 4. This script only executes the first main function it finds 25 | # 26 | # Inspired From: http://stackoverflow.com/a/8870863 27 | # 28 | ####################################################################################### 29 | 30 | 31 | #The folder which we mount on docker is named the usercode. 32 | #Move into the directory and execute the loop 33 | cd /usercode/ 34 | 35 | 36 | 37 | for classfile in *.class; do 38 | classname=${classfile%.*} 39 | #echo $classname 40 | 41 | #Execute fgrep with -q option to not display anything on stdout when the match is found 42 | if javap -public $classname | fgrep -q 'public static void main(java.lang.String[])'; then 43 | java $classname "$@" 44 | exit 0; 45 | fi 46 | done 47 | -------------------------------------------------------------------------------- /API/Payload/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ######################################################################## 4 | # - This is the main script that is used to compile/interpret the source code 5 | # - The script takes 3 arguments 6 | # 1. The compiler that is to compile the source file. 7 | # 2. The source file that is to be compiled/interpreted 8 | # 3. Additional argument only needed for compilers, to execute the object code 9 | # 10 | # - Sample execution command: $: ./script.sh g++ file.cpp ./a.out 11 | # 12 | ######################################################################## 13 | 14 | compiler=$1 15 | file=$2 16 | output=$3 17 | addtionalArg=$4 18 | 19 | 20 | ######################################################################## 21 | # - The script works as follows 22 | # - It first stores the stdout and std err to another stream 23 | # - The output of the stream is then sent to respective files 24 | # 25 | # 26 | # - if third arguemtn is empty Branch 1 is followed. An interpretor was called 27 | # - else Branch2 is followed, a compiler was invoked 28 | # - In Branch2. We first check if the compile operation was a success (code returned 0) 29 | # 30 | # - If the return code from compile is 0 follow Branch2a and call the output command 31 | # - Else follow Branch2b and output error Message 32 | # 33 | # - Stderr and Stdout are restored 34 | # - Once the logfile is completely written, it is renamed to "completed" 35 | # - The purpose of creating the "completed" file is because NodeJs searches for this file 36 | # - Upon finding this file, the NodeJS Api returns its content to the browser and deletes the folder 37 | # 38 | # 39 | ######################################################################## 40 | 41 | exec 1> $"/usercode/logfile.txt" 42 | exec 2> $"/usercode/errors" 43 | #3>&1 4>&2 > 44 | 45 | START=$(date +%s.%2N) 46 | #Branch 1 47 | if [ "$output" = "" ]; then 48 | $compiler /usercode/$file -< $"/usercode/inputFile" #| tee /usercode/output.txt 49 | #Branch 2 50 | else 51 | #In case of compile errors, redirect them to a file 52 | $compiler /usercode/$file $addtionalArg #&> /usercode/errors.txt 53 | #Branch 2a 54 | if [ $? -eq 0 ]; then 55 | $output -< $"/usercode/inputFile" #| tee /usercode/output.txt 56 | #Branch 2b 57 | else 58 | echo "Compilation Failed" 59 | #if compilation fails, display the output file 60 | #cat /usercode/errors.txt 61 | fi 62 | fi 63 | 64 | #exec 1>&3 2>&4 65 | 66 | #head -100 /usercode/logfile.txt 67 | #touch /usercode/completed 68 | END=$(date +%s.%2N) 69 | runtime=$(echo "$END - $START" | bc) 70 | 71 | 72 | echo "*-COMPILEBOX::ENDOFOUTPUT-*" $runtime 73 | 74 | 75 | mv /usercode/logfile.txt /usercode/completed 76 | 77 | -------------------------------------------------------------------------------- /API/Payload/sql_runner.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | cd /usercode/ 4 | sudo service mysql start 5 | echo "----BEGIN-----" 6 | mysql mysql< create_user.sql -u'root' 7 | mysql ri_db < $1 -u'test' -p'test123' 8 | mysql mysql< destroy_user.sql -u'root' 9 | echo "----END----" 10 | 11 | -------------------------------------------------------------------------------- /API/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | *File: app.js 3 | *Author: Asad Memon / Osman Ali Mian 4 | *Last Modified: 5th June 2014 5 | *Revised on: 30th June 2014 (Introduced Express-Brute for Bruteforce protection) 6 | */ 7 | 8 | 9 | 10 | 11 | var express = require('express'); 12 | var http = require('http'); 13 | var arr = require('./compilers'); 14 | var sandBox = require('./DockerSandbox'); 15 | var bodyParser = require('body-parser'); 16 | var app = express(); 17 | var server = http.createServer(app); 18 | var port=8080; 19 | 20 | 21 | var ExpressBrute = require('express-brute'); 22 | var store = new ExpressBrute.MemoryStore(); // stores state locally, don't use this in production 23 | var bruteforce = new ExpressBrute(store,{ 24 | freeRetries: 50, 25 | lifetime: 3600 26 | }); 27 | 28 | app.use(express.static(__dirname)); 29 | app.use(bodyParser()); 30 | 31 | app.all('*', function(req, res, next) 32 | { 33 | res.header('Access-Control-Allow-Origin', '*'); 34 | res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); 35 | res.header('Access-Control-Allow-Headers', 'Content-Type'); 36 | 37 | next(); 38 | }); 39 | 40 | function random(size) { 41 | //returns a crypto-safe random 42 | return require("crypto").randomBytes(size).toString('hex'); 43 | } 44 | 45 | 46 | app.post('/compile',bruteforce.prevent,function(req, res) 47 | { 48 | 49 | var language = req.body.language; 50 | var code = req.body.code; 51 | var stdin = req.body.stdin; 52 | 53 | var folder= 'temp/' + random(10); //folder in which the temporary folder will be saved 54 | var path=__dirname+"/"; //current working path 55 | var vm_name='virtual_machine'; //name of virtual machine that we want to execute 56 | var timeout_value=20;//Timeout Value, In Seconds 57 | 58 | //details of this are present in DockerSandbox.js 59 | var sandboxType = new sandBox(timeout_value,path,folder,vm_name,arr.compilerArray[language][0],arr.compilerArray[language][1],code,arr.compilerArray[language][2],arr.compilerArray[language][3],arr.compilerArray[language][4],stdin); 60 | 61 | 62 | //data will contain the output of the compiled/interpreted code 63 | //the result maybe normal program output, list of error messages or a Timeout error 64 | sandboxType.run(function(data,exec_time,err) 65 | { 66 | //console.log("Data: received: "+ data) 67 | res.send({output:data, langid: language,code:code, errors:err, time:exec_time}); 68 | }); 69 | 70 | }); 71 | 72 | 73 | app.get('/', function(req, res) 74 | { 75 | res.sendfile("./index.html"); 76 | }); 77 | 78 | console.log("Listening at "+port) 79 | server.listen(port); 80 | -------------------------------------------------------------------------------- /API/compilers.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | This file stores the compiler/interpretor details that are provided to DockerSandbox.sh by the app.js 4 | The index is the key field, 5 | First column contains the compiler/interpretor that will be used for translation 6 | Second column is the file name to use when storing the source code 7 | Third column is optional, it contains the command to invoke the compiled program, it is used only for compilers 8 | Fourth column is just the language name for display on console, for verbose error messages 9 | Fifth column is optional, it contains additional arguments/flags for compilers 10 | 11 | You can add more languages to this API by simply adding another row in this file along with installing it in your 12 | Docker VM. 13 | 14 | Author: Osman Ali 15 | Date: 3 - JUN - 2014 16 | *Revised on: 30th June 2014 (Added Column number 4 to display the name of languages to console) 17 | */ 18 | 19 | exports.compilerArray= [ ["python","file.py","","Python",""], 20 | ["ruby","file.rb","","Ruby",""], 21 | ["clojure","file.clj","","Clojure",""], 22 | ["php","file.php","","Php",""], 23 | ["nodejs","file.js","","Nodejs",""], 24 | ["scala","file.scala","","Scala",""], 25 | ["\'go run\'","file.go","","Go",""], 26 | ["\'g++ -o /usercode/a.out\' ","file.cpp","/usercode/a.out","C/C++",""], 27 | ["javac","file.java","\'./usercode/javaRunner.sh\'","Java",""], 28 | ["\'vbnc -nologo -quiet\'","file.vb","\'mono /usercode/file.exe\'","VB.Net",""], 29 | ["gmcs","file.cs","\'mono /usercode/file.exe\'","C#",""], 30 | ["/bin/bash","file.sh"," ","Bash",""], 31 | ["gcc ","file.m"," /usercode/a.out","Objective-C","\' -o /usercode/a.out -I/usr/include/GNUstep -L/usr/lib/GNUstep -lobjc -lgnustep-base -Wall -fconstant-string-class=NSConstantString\'"], 32 | ["/usercode/sql_runner.sh","file.sql","","MYSQL",""], 33 | ["perl","file.pl","","Perl",""], 34 | ["\'env HOME=/opt/rust /opt/rust/.cargo/bin/rustc\'","file.rs","/usercode/a.out","Rust","\'-o /usercode/a.out\'"] ]; 35 | -------------------------------------------------------------------------------- /API/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 29 | 30 | 31 |

32 | This is an example client to demo the sandboxed compiling. 33 |

34 |

Code Compile

35 | 36 | 37 | 38 |
39 | 40 | 41 |
42 |
43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 |

Output appears here

54 |
55 |

Tests

56 | 57 | 58 | 59 |

Output of all tests appear here

60 |

61 | 62 | 63 | 64 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /API/langs: -------------------------------------------------------------------------------- 1 | 2 | var LANGS = { 3 | "C#": [10, "text/x-csharp"], 4 | "C/C++": [7, "text/x-c++src"], 5 | "Clojure": [2, "text/x-clojure"], 6 | "Java": [8, "text/x-java"], 7 | "Go": [6, "text/x-go"], 8 | "Plain JavaScript": [4, "text/javascript"], 9 | "PHP": [3, "text/x-php"], 10 | "Python": [0, "text/x-python"], 11 | "Ruby": [1, "text/x-ruby"], 12 | "Scala": [5, "text/x-scala"], 13 | "VB.NET": [9, "text/x-vb"], 14 | "Bash": [11, "text/x-bash"], 15 | "Objective-C": [12,"text/x-objectivec"] 16 | } 17 | 18 | 19 | 20 | var Codes = { 21 | "Java": "/* package whatever; // don't place package name! */\n\nimport java.io.*;\n\nclass myCode\n{\n\tpublic static void main (String[] args) throws java.lang.Exception\n\t{\n\t\t\n\t\tSystem.out.println(\"Hello\");\n\t}\n}", 22 | "C/C++": "#include \nusing namespace std;\n\nint main() {\n\tcout<<\"Hello\";\n\treturn 0;\n}", 23 | "C#": "using System;\n\npublic class Test\n{\n\tpublic static void Main()\n\t{\n\t\t\tConsole.WriteLine(\"Hello\");\n\t}\n}", 24 | "Clojure": '(println "Hello")', 25 | "Go": "package main\nimport \"fmt\"\n\nfunc main(){\n \n\tfmt.Printf(\"Hello\")\n}", 26 | "Plain JavaScript": "//Not happy with Plain JS? Use JS/HTML/CSS option for using your own libraries.\n\nconsole.log(\"Hello\");", 27 | "PHP": "\n\n@interface Test\n+ (const char *) classStringValue;\n@end\n\n@implementation Test\n+ (const char *) classStringValue;\n{\n\treturn \"Hello\";\n}\n@end\n\nint main(void)\n{\n\tprintf(\"%s\n\", [Test classStringValue]);\n\treturn 0;\n}", 32 | "Scala": "object HelloWorld {def main(args: Array[String]) = println(\"Hello\")}", 33 | "VB.NET": "Imports System\n\nPublic Class Test\n\tPublic Shared Sub Main() \n \tSystem.Console.WriteLine(\"Hello\")\n\tEnd Sub\nEnd Class" 34 | } 35 | -------------------------------------------------------------------------------- /API/langs.js: -------------------------------------------------------------------------------- 1 | 2 | var LANGS = { 3 | "C#": [10, "text/x-csharp"], 4 | "C/C++": [7, "text/x-c++src"], 5 | "Clojure": [2, "text/x-clojure"], 6 | "Java": [8, "text/x-java"], 7 | "Go": [6, "text/x-go"], 8 | "Plain JavaScript": [4, "text/javascript"], 9 | "PHP": [3, "text/x-php"], 10 | "Python": [0, "text/x-python"], 11 | "Ruby": [1, "text/x-ruby"], 12 | "Scala": [5, "text/x-scala"], 13 | "VB.NET": [9, "text/x-vb"], 14 | "Bash": [11, "text/x-bash"], 15 | "Objective-C": [12,"text/x-objectivec"], 16 | "MySQL": [13,"text/x-sql"], 17 | "Perl": [14, "text/x-perl"], 18 | "Rust": [15, "text/rust"], 19 | } 20 | 21 | 22 | 23 | var Codes = { 24 | "Perl" : "use strict;\nuse warnings\n;use v5.14; say 'Hello';", 25 | "MySQL":"create table myTable(name varchar(10));\ninsert into myTable values(\"Hello\");\nselect * from myTable;", 26 | "Java": "/* package whatever; // don't place package name! */\n\nimport java.io.*;\n\nclass myCode\n{\n\tpublic static void main (String[] args) throws java.lang.Exception\n\t{\n\t\t\n\t\tSystem.out.println(\"Hello\");\n\t}\n}", 27 | "C/C++": "#include \nusing namespace std;\n\nint main() {\n\tcout<<\"Hello\";\n\treturn 0;\n}", 28 | "C#": "using System;\n\npublic class Test\n{\n\tpublic static void Main()\n\t{\n\t\t\tConsole.WriteLine(\"Hello\");\n\t}\n}", 29 | "Clojure": '(println "Hello")', 30 | "Go": "package main\nimport \"fmt\"\n\nfunc main(){\n \n\tfmt.Printf(\"Hello\")\n}", 31 | "Plain JavaScript": "//Not happy with Plain JS? Use JS/HTML/CSS option for using your own libraries.\n\nconsole.log(\"Hello\");", 32 | "PHP": "\n\n@interface Test\n+ (const char *) classStringValue;\n@end\n\n@implementation Test\n+ (const char *) classStringValue;\n{\n return \"Hey!\";\n}\n@end\n\nint main(void)\n{\n printf(\"%s\\n\", [Test classStringValue]);\n return 0;\n}", 37 | "Scala": "object HelloWorld {def main(args: Array[String]) = println(\"Hello\")}", 38 | "VB.NET": "Imports System\n\nPublic Class Test\n\tPublic Shared Sub Main() \n \tSystem.Console.WriteLine(\"Hello\")\n\tEnd Sub\nEnd Class", 39 | "Rust": "fn main() {\n\tprintln!(\"Hello\");\n}", 40 | } 41 | 42 | -------------------------------------------------------------------------------- /API/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "API", 3 | "version": "1.0.0", 4 | "main": "app.js", 5 | "dependencies": { 6 | "express": "4.*", 7 | "body-parser" : "*", 8 | "express-brute":"*", 9 | "exports": "*" 10 | }, 11 | "author": "Osman Ali", 12 | "license": "MIT" 13 | } 14 | -------------------------------------------------------------------------------- /API/temp/ReadMe.txt: -------------------------------------------------------------------------------- 1 | This folder will contain the temporary shared directories which will be mounted to docker containers. -------------------------------------------------------------------------------- /Arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remoteinterview/compilebox/6ce9193c882ecfd2a2739bf80d0c103152888f0d/Arch.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 RemoteInterview.io 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What is it? ## 2 | CompileBox is a *Docker* based sandbox to run untrusted code and return the output to your app. Users can submit their code in any of the supported languages. The system will test the code in an isolated environment. This way you do not have to worry about untrusted code possibly damaging your server intentionally or unintentionally. 3 | You can use this system to allow your users to compile their code right in the browser. 4 | 5 | Check the example work at: 6 | 7 | - Basic Example: [compile.remoteinterview.io][1] 8 | - Prettified View: [codepad.remoteinterview.io][2] 9 | 10 | ## How does it work? ## 11 | 12 | The client-side app submits the code and the languageID to the server through the API. The API then creates a new *Docker* container and runs the code using the compiler/interpreter of that language. The program runs inside a virtual machine with limited resources and has a time-limit for execution (20s by default). Once the output is ready it is sent back to the client-side app. The *Docker* container is destroyed and all the files are deleted from the server. 13 | 14 | No two coders have access to each other’s *Docker* or files. 15 | 16 | 17 | ## Installation Instructions ## 18 | 19 | * Go to the 'Setup' directory. 20 | - Open the Terminal as root user 21 | 22 | - Execute the script **Install_*.sh**, select the file which best suites your Operating System description. This will install the Docker and NodeJs pre-requisites to your system and create an image called 'virtual_machine' inside the Docker. DockerVM may take around 20 to 30 minutes depending on your internet connection. 23 | 24 | - Once the Install script executes successfully, copy the folder named 'API' to your desired path. 25 | 26 | - Open app.js in the API folder and set the variable values as follows. 27 | 28 | 1. **timeout_value**: The time in seconds till which the API should wait for the output of the code before generating an "Execution Timed Out" message. 29 | 2. **port**: The port on which the server will listen, the default port is 80. 30 | 31 | - To test the installation, open a new terminal windows, cd to the API folder and type the following command 32 | ``` 33 | $ npm install . 34 | ``` 35 | to install all needed nodejs modules, followed by 36 | 37 | ``` 38 | $ sudo nodejs app.js 39 | ``` 40 | - If everything has been setup correctly in app.js file, you will see the following message on your terminal 41 | ``` 42 | Listening at 43 | ``` 44 | 45 | - Navigate your browser to http://127.0.0.1/ 46 | 47 | ## Supported Operating Systems ## 48 | The CompileBox API has been installed and run succesfully on the following platforms 49 | - Ubuntu 12.04 LTS 50 | - Ubuntu 13.10 51 | - Ubuntu 14.04 LTS 52 | - Ubuntu 16.04 LTS 53 | - Linux Mint 15 54 | - CentOS 6 (root required not sudo) 55 | 56 | ## Selecting The languages for Installation Inside Docker ## 57 | 58 | The default Dockerfile installs the most used languages. To remove/change any, follow these steps 59 | 60 | In order to select languages of your own choice you need to make 2 changes.
61 | 1. Dockerfile: This file contains commands that you would normally give in your terminal to install that language. Add the required commands preceeded by the RUN keyword inside the Dockerfile. Run the "UpdateDocker.sh" script, present in the same folder if you are adding new language to already installed API, execute the Install_*.sh script otherwise, from your terminal after making the changes to your Dockerfile.
62 | 2. Compilers.js: This file is inside the API folder. The compiler name, the source file name and the execution commands to Docker Container are taken from this file. This file only contains an array, which is described in detail inside the file. Add the credentials of the language you are adding to this array.
63 | 64 | The next time you wish to compile using this language, simply issue the language_id , which is same as the index of the language in the array present in Compilers.js, along with your code to the NodeJs server. 65 | 66 | > Note: Additionally while setting up the API for the first time, you can comment out those languages from the Dockerfile that you do not wish to install, since they can be added later. 67 | 68 | ## Adding Your Own Languages ## 69 | 70 | 71 | In order to add your own languages you need to following steps. 72 |
73 | 1. Modify the Dockerfile: The Dockerfile is present in the Setup folder and contains the commands that you would normally write in your terminal to install a particular language. Append the commands for the language of your choice to the end of the Dockerfile.
74 | 2. Execute UpdateDocker.sh and wait for your language to be installed inside the virtual machine.
75 | 3. Modify Compilers.js: Compilers.js file is available in the API folder and contains the information needed by app.js to compile a given source code inside Docker container. The file only consists of an array which is described in detail inside the file. Add the credentials for your language to the Array. 76 | 77 | > Note: You should be connected to the Internet when you run UpdateDocker.sh 78 | 79 | [1]: http://compile.remoteinterview.io 80 | [2]: http://codepad.remoteinterview.io 81 | -------------------------------------------------------------------------------- /Setup/Dockerfile: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # Dockerfile to build sandbox for executing user code 3 | # Based on Ubuntu 4 | ############################################################ 5 | 6 | FROM chug/ubuntu14.04x64 7 | MAINTAINER ASAD MEMON, OSMAN ALI 8 | 9 | # Update the repository sources list 10 | RUN echo "deb http://archive.ubuntu.com/ubuntu trusty main universe" > /etc/apt/sources.list 11 | RUN apt-get update 12 | #RUN apt-get upgrade 13 | #Install all the languages/compilers we are supporting. 14 | RUN apt-get install -y gcc 15 | RUN apt-get install -y g++ 16 | RUN apt-get install -y php5-cli 17 | RUN apt-get install -y ruby 18 | RUN apt-get install -y python 19 | RUN apt-get install -y mono-xsp2 mono-xsp2-base 20 | 21 | RUN apt-get install -y mono-vbnc 22 | RUN apt-get install -y npm 23 | RUN apt-get install -y golang-go 24 | RUN apt-get install -y nodejs 25 | 26 | RUN npm install -g underscore request express jade shelljs passport http sys jquery lodash async mocha moment connect validator restify ejs ws co when helmet wrench brain mustache should backbone forever debug && export NODE_PATH=/usr/local/lib/node_modules/ 27 | 28 | RUN apt-get install -y clojure1.4 29 | 30 | 31 | #prepare for Java download 32 | RUN apt-get install -y python-software-properties 33 | RUN apt-get install -y software-properties-common 34 | 35 | #grab oracle java (auto accept licence) 36 | RUN add-apt-repository -y ppa:webupd8team/java 37 | RUN apt-get update 38 | RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections 39 | RUN apt-get install -y oracle-java8-installer 40 | 41 | 42 | RUN apt-get install -y gobjc 43 | RUN apt-get install -y gnustep-devel && sed -i 's/#define BASE_NATIVE_OBJC_EXCEPTIONS 1/#define BASE_NATIVE_OBJC_EXCEPTIONS 0/g' /usr/include/GNUstep/GNUstepBase/GSConfig.h 44 | 45 | 46 | RUN apt-get install -y scala 47 | RUN apt-get install -y mysql-server 48 | RUN apt-get install -y perl 49 | 50 | RUN apt-get install -y curl 51 | RUN mkdir -p /opt/rust && \ 52 | curl https://sh.rustup.rs -sSf | HOME=/opt/rust sh -s -- --no-modify-path -y && \ 53 | chmod -R 777 /opt/rust 54 | 55 | RUN apt-get install -y sudo 56 | RUN apt-get install -y bc 57 | 58 | RUN echo "mysql ALL = NOPASSWD: /usr/sbin/service mysql start" | cat >> /etc/sudoers 59 | 60 | -------------------------------------------------------------------------------- /Setup/Install_12.04.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ########################### 4 | # Docker SETUP 5 | ########################### 6 | apt-get update 7 | 8 | echo "Installing Docker" 9 | 10 | [ -e /usr/lib/apt/methods/https ] || { 11 | apt-get update 12 | apt-get install -y apt-transport-https 13 | } 14 | 15 | apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 16 | 17 | sh -c "echo deb https://get.docker.io/ubuntu docker main\ 18 | > /etc/apt/sources.list.d/docker.list" 19 | 20 | apt-get update 21 | apt-get install -y lxc-docker 22 | 23 | 24 | ########################### 25 | # NodeJS setup 26 | ########################### 27 | 28 | echo "Installing NodeJS" 29 | 30 | apt-get update 31 | apt-get install -y nodejs 32 | apt-get install -y npm 33 | 34 | chmod 777 ../API/DockerTimeout.sh 35 | chmod 777 ../API/Payload/script.sh 36 | chmod 777 ../API/Payload/javaRunner.sh 37 | chmod 777 UpdateDocker.sh 38 | 39 | ./UpdateDocker.sh 40 | 41 | -------------------------------------------------------------------------------- /Setup/Install_13.04_13.10.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | 5 | ########################### 6 | # Docker SETUP 7 | ########################### 8 | echo "Setting up Docker" 9 | apt-get update 10 | apt-get install -y linux-image-extra-`uname -r` 11 | apt-get install -y cgroup-lite 12 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 13 | sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ > /etc/apt/sources.list.d/docker.list" 14 | 15 | apt-get update 16 | apt-get install -y lxc-docker 17 | 18 | ########################### 19 | # NodeJS setup 20 | ########################### 21 | 22 | echo "Setting up NodeJs" 23 | 24 | apt-get update 25 | apt-get install -y nodejs 26 | apt-get install -y npm 27 | 28 | ############################# 29 | # Starting Docker 30 | ############################# 31 | 32 | chmod 777 ../API/DockerTimeout.sh 33 | chmod 777 ../API/Payload/script.sh 34 | chmod 777 ../API/Payload/javaRunner.sh 35 | chmod 777 UpdateDocker.sh 36 | 37 | 38 | ./UpdateDocker.sh 39 | -------------------------------------------------------------------------------- /Setup/Install_14.04.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ########################### 4 | # Docker SETUP 5 | ########################### 6 | apt-get update 7 | apt-get install -y docker.io 8 | ln -sf /usr/bin/docker.io /usr/local/bin/docker 9 | sed -i '$acomplete -F _docker docker' /etc/bash_completion.d/docker.io 10 | 11 | echo "Docker Setup complete" 12 | 13 | ########################### 14 | # NodeJS setup 15 | ########################### 16 | apt-get update 17 | apt-get install -y nodejs 18 | apt-get install -y npm 19 | echo "NodeJS setup Complete" 20 | 21 | ########################### 22 | # Start Docker 23 | ########################### 24 | chmod 777 ../API/DockerTimeout.sh 25 | chmod 777 ../API/Payload/script.sh 26 | chmod 777 ../API/Payload/javaRunner.sh 27 | chmod 777 UpdateDocker.sh 28 | 29 | service docker.io restart 30 | ./UpdateDocker.sh 31 | -------------------------------------------------------------------------------- /Setup/Install_16.04.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ########################### 4 | # Docker SETUP 5 | ########################### 6 | apt-get update 7 | apt-get install -y docker.io 8 | 9 | echo "Docker Setup complete" 10 | 11 | ########################### 12 | # NodeJS setup 13 | ########################### 14 | apt-get update 15 | apt-get install -y nodejs 16 | apt-get install -y npm 17 | echo "NodeJS setup Complete" 18 | 19 | ########################### 20 | # Start Docker 21 | ########################### 22 | chmod 777 ../API/DockerTimeout.sh 23 | chmod 777 ../API/Payload/script.sh 24 | chmod 777 ../API/Payload/javaRunner.sh 25 | chmod 777 UpdateDocker.sh 26 | 27 | systemctl restart docker 28 | ./UpdateDocker.sh 29 | -------------------------------------------------------------------------------- /Setup/Install_Cent6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # NodeJS Setup requires running script as root (not sudo) 4 | 5 | ########################### 6 | # Docker SETUP 7 | ########################### 8 | rpm -iUvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm 9 | yum update -y 10 | yum install -y docker-io 11 | echo "Docker Setup Complete" 12 | 13 | ############################ 14 | # NodeJS 6 Setup 15 | ############################ 16 | curl --silent --location https://rpm.nodesource.com/setup_6.x | bash - 17 | yum -y install nodejs gcc-c++ make 18 | echo "NodeJS Setup Complete" 19 | 20 | ############################ 21 | # Start Docker 22 | ############################ 23 | chmod 777 ../API/DockerTimeout.sh 24 | chmod 777 ../API/Payload/script.h 25 | chmod 777 ../API/Payload/javaRunner.sh 26 | chmod 777 UpdateDocker.sh 27 | 28 | service docker restart 29 | ./UpdateDocker.sh 30 | -------------------------------------------------------------------------------- /Setup/Install_Mint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | vm_name = $1 4 | 5 | ########################### 6 | # Docker SETUP 7 | ########################### 8 | echo "Setting up Docker" 9 | apt-get update 10 | apt-get install -y linux-image-extra-`uname -r` 11 | apt-get install -y cgroup-lite 12 | sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" 13 | apt-get update 14 | apt-get install -y lxc-docker 15 | 16 | ########################### 17 | # NodeJS setup 18 | ########################### 19 | 20 | echo "Setting up NodeJs" 21 | 22 | apt-get update 23 | apt-get install -y nodejs 24 | apt-get install -y npm 25 | 26 | ############################# 27 | # Starting Docker 28 | ############################# 29 | 30 | chmod 777 ../API/DockerTimeout.sh 31 | chmod 777 ../API/Payload/script.sh 32 | chmod 777 ../API/Payload/javaRunner.sh 33 | chmod 777 UpdateDocker.sh 34 | 35 | 36 | ./UpdateDocker.sh 37 | -------------------------------------------------------------------------------- /Setup/UpdateDocker.sh: -------------------------------------------------------------------------------- 1 | 2 | echo "Creating Docker Image" 3 | docker build -t 'virtual_machine' - < Dockerfile 4 | echo "Retrieving Installed Docker Images" 5 | docker images 6 | --------------------------------------------------------------------------------