The CodeCollab
compiler used for running untrusted code. Currently supports 18 different languages.
This package allows you to run untrusted code in a collaborative environment. Multiple users can run code together simply by attaching themselves to an already running docker container. You can start this docker container using the {Compiler} included in this package or simply attach to an already existing docker container using the {Attacher}.
NOTE: This package uses node-pty
to create a pseudo-tty. As such, escape sequences are not removed from the program output. It is highly recommended you use this library with xterm.js
or any other terminal emulator that is able to process escape sequences.
- Docker
- Docker daemon running that doesn't require sudo access.
Using NPM:
$ npm install @portatolova/cc-compiler
This package requires that you build the included docker image. Navigate to the package directory and type the following command in the "setup" directory to install the docker image.
$ ./setup.sh
It is recommend that u build this image on any computer running on x86 architecture. There is no guarantees the image will work as intended on ARM devices.
This package comes with a Compiler and an Attacher:
const Compiler = require("@portatolova/cc-compiler").Compiler;
const Attacher = require("@portatolova/cc-compiler").Attacher;
The compiler class is used to initialise and run the untrusted user code.
// Initialises the compiler
let compiler = new Compiler(options);
compiler.exec(); // Starts the compiler
compiler.push(data); // Sends data into the running program
compiler.stop(); // Stops the compiler
options.langNum
Index of language to Compile in (More info below)options.mainFile
The file in the compilation directory that will be ran during compilationoptions.pathToFiles
The compilation directory. (Where the files are stored) The path must be an absolute path and not a relative path.options.containerName
Name of the docker container created for compilationoptions.folderName
(OPTIONAL) Name of directory where code will be moved to. Defaults to "code"
compiler.exec()
Starts compilation.
compiler.push(data: string)
Pipes data into the program's STDIN stream.
compiler.resize(size: { cols: number, rows: number })
Resizes the underlying pseudo-tty.
compiler.stop()
Stops compilation.
Each language has an index or "langNum" used to identify it. Here are the langNums and their associated languages. (NOTE: There is no index 7)
* 0 - Python 3.7
* 1 - NodeJS
* 2 - CoffeeScript
* 3 - Typescript
* 4 - C++
* 5 - C
* 6 - C#
* 8 - Python 2.7
* 9 - Bash Shell
* 10 - Java
* 11 - Swift
* 12 - Golang
* 13 - PHP
* 14 - Ruby
* 15 - Perl
* 16 - Kotlin
* 17 - Elixir
* 18 - Scala
Starting from v4.0.0, you do not need to have a specific folder structure. Simply set options.pathToFiles
to where your code is stored and it will be mounted into the docker container as options.folderName
. If options.folderName
is not specified, code
is used instead.
The attacher class is used to attach to an existing docker container.
// Initialises the attacher
let attacher = new Attacher(options);
attacher.attach(); // Attaches to the compiler
attacher.push(data); // Sends data into the running program
attacher.stop(); // Stops the compiler
options.pathToFiles
The compilation directory. (Where the files are stored) The path must be an absolute path and not a relative path.options.containerName
Name of the docker container created for compilation
attacher.attach()
Attach to an existing compiler.
attacher.push()
Pipes data into the program's STDIN stream.
attacher.resize(size: { cols: number, rows: number })
Resizes the underlying pseudo-tty.
attacher.stop()
Stops compilation.
Both the Compiler and Attach can emit 3 events.
error
emitted whenever the compiler encountered an error.inc
emitted whenever the running program outputs anything to STDOUT or STDERR.done
emitted when the program is finished running or it has timed out.
The Compiler also has an additional event
launched
emitted when the program has launched successfully.
This example runs code stored in /Users/carlvoller/test using Python 3.9. The compiler is listening for a "launched" event in which afterwards, it will attach the attacher to the compiler. The attacher then listens for "inc" and "done" events for program output and errors as well as for when compilation finishes. It then prints all output to the console.
const Compiler = require("../index").Compiler;
const Attacher = require("../index").Attacher;
// Initialise Compiler
let comp = new Compiler({
langNum: 0,
mainFile: "main.py",
pathToFiles: "/Users/carlvoller/test",
containerName: "Test1",
folderName: "My Code"
});
// Initialise Attacher
let attach = new Attacher({
pathToFiles: "/Users/carlvoller/test",
containerName: "Test1"
});
comp.exec(); // Start Compiler
// Listen for events
comp.on("launched", () => {
console.log("Compiler has been launched.");
// Attach the attacher
attach.attach();
});
// Listen for data coming out from the program. STDERR is coloured red with escape sequences.
attach.on("inc", (inc) => {
let output = inc.out;
console.log(`"Program output: ${output}`);
});
// Listen for when the program finishes running OR timed out
attach.on("done", (done) => {
let output = done.out
timedOut = done.timedOut; // Did the program time out. (Took > 1 minute)
console.log(`"Program output: ${output}`);
console.log(`"Program time out status: ${timedOut}`);
});
// Listen for any errors during initialisation
comp.on("error", (err) => console.log("An error occurred: ", err));
attach.on("error", (err) => console.log("An error occurred: ", err));