├── .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 |
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 |
--------------------------------------------------------------------------------