Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 60 additions & 16 deletions wasm-sandboxes/js/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,62 @@ function wasmRelative(cwd: string, filePath: string): string {
return path.resolve(cwd, filePath).replace(/^\//, '');
}

function resolveNodeModule(fromPath: string, id: string): string | null {
let dir = path.dirname('/' + fromPath);

while (true) {
const base = wasmRelative('/', path.join(dir, 'node_modules', id));
const candidates = [base, base + '.js', base + '/index.js'];

const pkgJson = base + '/package.json';

if (fs.existsSync(pkgJson)) {
try {
const pkg = JSON.parse(fs.readFileSync(pkgJson, 'utf-8') as string);
const main = pkg.main || 'index.js';
candidates.unshift(wasmRelative('/', path.join(dir, 'node_modules', id, main)));
} catch {}
}

for (const candidate of candidates) {
if (fs.existsSync(candidate)) return candidate;
}

const parent = path.dirname(dir);
if (parent === dir) return null;

dir = parent;
}
}

const makeRequire = (fromPath: string) => (id: string) => {
let depPath: string;

if (!id.startsWith('.') && !id.startsWith('/')) {
const resolved = resolveNodeModule(fromPath, id);

if (!resolved) throw new Error(`Cannot find module '${id}'`);

depPath = resolved;
} else {
const base = wasmRelative('/', path.resolve('/' + path.dirname(fromPath), id));

depPath = fs.existsSync(base) ? base
: fs.existsSync(base + '.js') ? base + '.js'
: base + '/index.js';
}

const src = fs.readFileSync(depPath, 'utf-8') as string;
const m = { exports: {} as any };
const fn = new Function('module', 'exports', 'require', '__filename', '__dirname', src);

fn(m, m.exports, makeRequire(depPath), depPath, path.dirname(depPath));

return m.exports;
};

const executeFile = task(
{ name: "executeFile", compute: "MEDIUM", ram: "512MB" },
{ name: "executeFile", compute: "MEDIUM", ram: "512MB", allowedHosts: ["*"] },
async (state: State, filePath: string, args: string[]) => {
const capturedOutput: string[] = [];
const relPath = wasmRelative(state.cwd, filePath);
Expand All @@ -28,18 +82,6 @@ const executeFile = task(
try {
const code = fs.readFileSync(relPath, 'utf-8') as string;
const mod = { exports: {} as any };

const makeRequire = (fromPath: string) => (id: string) => {
const base = wasmRelative('/', path.resolve('/' + path.dirname(fromPath), id));
const depPath = fs.existsSync(base) ? base
: fs.existsSync(base + '.js') ? base + '.js'
: base + '/index.js';
const src = fs.readFileSync(depPath, 'utf-8') as string;
const m = { exports: {} as any };
const fn = new Function('module', 'exports', 'require', '__filename', '__dirname', src);
fn(m, m.exports, makeRequire(depPath), depPath, path.dirname(depPath));
return m.exports;
};
const customRequire = makeRequire(relPath);

const fn = new Function('module', 'exports', 'require', '__filename', '__dirname', code);
Expand All @@ -60,7 +102,7 @@ const executeFile = task(


const executeCode = task(
{ name: "executeCode", compute: "LOW", ram: "256MB" },
{ name: "executeCode", compute: "LOW", ram: "256MB", allowedHosts: ["*"] },
async (state: State, code: string): Promise<unknown> => {
process.chdir(state.cwd);
const capturedOutput: string[] = [];
Expand All @@ -70,14 +112,16 @@ const executeCode = task(
capturedOutput.push(args.map(arg => String(arg)).join(' '));
};

const require = makeRequire(wasmRelative(state.cwd, '.'));

try {
let result;
try {
result = eval(code);
} catch (e) {
if (e instanceof SyntaxError && e.message.includes("return")) {
const fn = new Function(code);
result = fn();
const fn = new Function('require', code);
result = fn(require);
} else {
throw e;
}
Expand Down
29 changes: 24 additions & 5 deletions wasm-sandboxes/python/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
from dataclasses import dataclass
from capsule import task

# Need to import them at build time
import socket
import ssl
import urllib.request
import urllib.parse
import urllib.error
import urllib.response
import http.client
import http.cookiejar

@dataclass
class State:
Expand All @@ -28,12 +37,16 @@ def wasm_relative(cwd: str, file_path: str) -> str:
return joined.lstrip("/")


@task(name="executeFile", compute="MEDIUM", ram="512MB")
@task(name="executeFile", compute="MEDIUM", ram="512MB", allowed_hosts=["*"])
def execute_file(state: str, file_path: str, args: list[str]):
parsed_state = State.from_json(state)
rel_path = wasm_relative(parsed_state.cwd, file_path)
state = State.from_json(state)
rel_path = wasm_relative(state.cwd, file_path)
file_dir = os.path.dirname(rel_path) or "."

site_packages = os.path.join(state.cwd, "site-packages")
if site_packages not in sys.path:
sys.path.insert(0, site_packages)

captured_output = StringIO()
old_stdout = sys.stdout
old_argv = sys.argv
Expand Down Expand Up @@ -72,8 +85,14 @@ def execute_file(state: str, file_path: str, args: list[str]):
return public_result if public_result else None


@task(name="executeCode", compute="LOW", ram="256MB")
def execute_code(_state: str, code: str):
@task(name="executeCode", compute="LOW", ram="256MB", allowed_hosts=["*"])
def execute_code(state: str, code: str):
state = State.from_json(state)

site_packages = os.path.join(state.cwd, "site-packages")
if site_packages not in sys.path:
sys.path.insert(0, site_packages)

tree = ast.parse(code)

if not tree.body:
Expand Down
Loading