Skip to content

Commit

Permalink
Add a CLI to the sandbox (#302)
Browse files Browse the repository at this point in the history
  • Loading branch information
taybenlor authored Nov 17, 2024
1 parent 931f9ce commit a66f6dc
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 48 deletions.
42 changes: 42 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion packages/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@ Run untrusted code from programming languages inside a WebAssembly sandbox.

This is an unreleased proof of concept.

## Quickstart
## Quickstart CLI

`src/main.ts` is a CLI program that runs files using Runno.

```
$ deno --allow-net --allow-read src/main.ts python test.py
Hello, World!
```

_Note: Currently network access is required to fetch the WASM binaries from runno.dev_

Supported runtimes are: `python`, `ruby`, `quickjs`, `php-cgi`, `sqlite`, `clang`, and `clangpp`.

## Quickstart API

The simplest use is to run some code in a supported language:

Expand Down
1 change: 1 addition & 0 deletions packages/sandbox/deno.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"imports": {
"@cliffy/command": "jsr:@cliffy/[email protected]",
"@runno/wasi": "npm:@runno/wasi@^0.7.0",
"@std/tar": "jsr:@std/tar@^0.1.3"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/sandbox/lib/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { runCode, runFS } from "./runtime.ts";
import { fetchWASIFS } from "./helpers.ts";

export { runCode, runFS };
export { runCode, runFS, fetchWASIFS };
5 changes: 3 additions & 2 deletions packages/sandbox/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@runno/sandbox",
"version": "0.7.0",
"description": "A sandbox for running code server-side in JavaScript environments",
"description": "Run untrusted code from programming languages inside a WebAssembly sandbox.",
"keywords": [
"sandbox",
"python",
Expand All @@ -28,7 +28,8 @@
"url": "git+https://github.com/taybenlor/runno.git"
},
"scripts": {
"test": "deno test --no-check --allow-net"
"test": "deno test --no-check --allow-net",
"cli": "deno --allow-net --allow-read src/main.ts"
},
"bugs": {
"url": "https://github.com/taybenlor/runno/issues"
Expand Down
123 changes: 79 additions & 44 deletions packages/sandbox/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,81 @@
import { runCode, runFS } from "../lib/main.ts";
import { WASIFS } from "@runno/wasi";

const codeResult = await runCode(
"python",
`
print("Hello, World!");
import { runFS } from "../lib/runtime.ts";
import { Command } from "@cliffy/command";
import { Runtime } from "../lib/types.ts";
import { fetchWASIFS } from "../lib/main.ts";

function isRuntime(runtime: string): runtime is Runtime {
return [
"python",
"ruby",
"quickjs",
"php-cgi",
"clang",
"clangpp",
"sqlite",
].includes(runtime);
}

const command = new Command()
.name("runno")
.version("0.7.0")
.description(
`A CLI for running code in a sandbox environment, powered by Runno & WASI.
Supports python, ruby, quickjs, php-cgi, clang, and clangpp.
Entry is a path to a file, which will be added on top of the base filesystem and used as the entrypoint.
`
);

console.log("Run Code Result:", codeResult);

const fsResult = await runFS("python", "/program", {
"/program": {
path: "/program",
content: `
from package import say_hello
say_hello()
print('------')
import os
print("/package contains", os.listdir('/package'))
`,
mode: "string",
timestamps: {
access: new Date(),
modification: new Date(),
change: new Date(),
},
},
"/package/__init__.py": {
path: "/package/__init__.py",
content: `
def say_hello():
print("Hello from package")
`,
mode: "string",
timestamps: {
access: new Date(),
modification: new Date(),
change: new Date(),
},
},
});

console.log("Run FS Result:", fsResult);
)
.arguments("<runtime:string> <entry:string>")
.option(
"-f, --filesystem <filesystem:string>",
"A tgz file to use as the base filesystem"
)
.action(
async (options: { filesystem?: string }, ...args: [string, string]) => {
const [runtimeString, entry] = args;
if (!isRuntime(runtimeString)) {
throw new Error(`Unsupported runtime: ${runtimeString}`);
}
const runtime: Runtime = runtimeString;

// TODO: Use filesystem helpers
const entryName = entry.split("/").pop() ?? entry;
let fs: WASIFS = {
[`/${entryName}`]: {
path: `/${entryName}`,
content: await Deno.readFile(entry),
mode: "binary",
timestamps: {
access: new Date(),
modification: new Date(),
change: new Date(),
},
},
};

if (options.filesystem) {
const baseFS = fetchWASIFS(options.filesystem);
fs = { ...baseFS, ...fs };
}

const result = await runFS(runtime, entryName, fs);

switch (result.resultType) {
case "complete":
console.error(result.stderr);
console.log(result.stdout);
break;
case "crash":
console.error(result.error);
Deno.exit(1);
break;
case "terminated":
console.error("Terminated");
Deno.exit(1);
break;
}
}
);

await command.parse(Deno.args);

0 comments on commit a66f6dc

Please sign in to comment.