@specs-feup/clava
Version:
A C/C++ source-to-source compiler written in Typescript
96 lines • 4.53 kB
JavaScript
import { spawn, spawnSync } from "child_process";
import { addActiveChildProcess } from "@specs-feup/lara/code/ChildProcessHandling.js";
export default class Sandbox {
/**
* Executes a command in a restricted environment and returns the output as a string
*
* **Sanitize the input before calling this method**
*
* @param command - String containing the command to be executed
* @param args - Array of strings containing the arguments to be passed to the command
* @param env - Object containing the environment variables to be set for the command
* @returns Output of the command as a string
*/
static executeSandboxedCommand(command, args = [], env = new Map()) {
// Set up a new child process with a restricted environment
const child = spawnSync(command, args, {
env: {
PATH: "/usr/local/bin:/usr/bin:/bin", // Set a limited PATH environment variable
HOME: "/tmp", // Set a limited HOME environment variable
USER: "sandbox", // Set a limited user for the child process
...Object.fromEntries(env),
},
cwd: "/tmp", // Set a restricted current working directory
stdio: ["ignore", "pipe", "ignore", "ignore"],
});
if (child.error) {
throw new Error("Invalid executable");
}
else if (child.status !== 0) {
throw new Error(`Command exited with status ${String(child.status)}: ${child.stderr.toString()}`);
}
// Return the output of the child process as a string
return child.stdout.toString();
}
/**
* Executes a command in a restricted environment and returns the output as a string
*
* **Sanitize the input before calling this method**
*
* @param command - String containing the command to be executed
* @param args - Array of strings containing the arguments to be passed to the command
* @param env - Object containing environment variables to be set for the child process
* @returns Output of the command as a string
*/
static executeSandboxedCommandAsync(command, args = [], env = {}) {
// Set up a new child process with a restricted environment
const child = spawn(command, args, {
env: {
PATH: "/usr/local/bin:/usr/bin:/bin", // Set a limited PATH environment variable
HOME: "/tmp", // Set a limited HOME environment variable
USER: "sandbox", // Set a limited user for the child process
...env,
},
cwd: "/tmp", // Set a restricted current working directory
stdio: ["ignore", "pipe", "ignore", "ignore"],
});
addActiveChildProcess(child);
return child;
}
/**
* Ensure command string does not contain any characters that could be used
*
* @param command - String containing the command to be sanitized
* @returns The original command or throws an error if invalid
*/
static sanitizeCommand(command) {
return new Promise((resolve, reject) => {
/**
* This regular expression, /^[^;&|]*$/, is a pattern that matches any
* string that does not contain certain characters often used in shell
* commands. Specifically, it matches strings that do not contain
* semicolons (;), ampersands (&), or vertical bars (|).
*/
const pattern = /^[^;&|]*$/;
if (!pattern.test(command.join(" "))) {
reject(new Error("Invalid command"));
}
resolve(command);
});
}
static splitCommandArgsEnv(commandArgs) {
return new Promise((resolve) => {
// regex to match environment variable declarations
const envRegex = /^([a-zA-Z_][a-zA-Z0-9_]*)=(.*)$/;
// filter environment variables
const env = commandArgs.filter((arg) => typeof arg === "string" && envRegex.test(arg));
// filter command and arguments
const commandArgsWithoutEnv = commandArgs.filter((arg) => !env.includes(String(arg)));
// take the first argument as the command
const commandCandidate = commandArgsWithoutEnv.shift();
const command = commandCandidate ? String(commandCandidate) : "";
resolve([command, commandArgsWithoutEnv, env]);
});
}
}
//# sourceMappingURL=Sandbox.js.map