Theme
Running programs Preview
js
function Run(commandLine: string): boolean; // waits, returns success
function RunAsync(commandLine: string): boolean; // does not wait, returns "started"
function RunCapture(commandLine: string): string; // waits, returns stdout and stderrThis is the escape hatch: anything you can do from a command line on the Connector host, a script can do. Antivirus scanning, format conversion, calling a bespoke internal tool, triggering a backup.
js
if (Run("/bin/bash /opt/scripts/rebuild-index.sh")) {
Log.Info("index rebuilt");
} else {
Log.Error("index rebuild failed");
}| Function | Waits | Returns |
|---|---|---|
Run | Yes | true when the program ran and exited cleanly |
RunAsync | No | true when the program started. Its outcome is never reported |
RunCapture | Yes | Combined standard output and standard error, as one string. "" if it could not start |
RunCapture does not tell you the exit code. It hands you whatever text the program produced, even when the program failed, so you have to judge from the text:
js
var out = RunCapture("/usr/local/bin/scan --quiet " + localPath);
if (out.indexOf("INFECTED") >= 0) {
Log.Error("malware found in " + CtxRelPath());
Exit(1);
}js
var out = RunCapture("sha256sum /var/outbox/delivery.zip");
var digest = out.split(" ")[0];The command line is a string, and a file name is not your data
Every one of these takes a single string that is handed to the operating system to interpret. Concatenating a path that came from a user into that string is a command injection, in the classic sense, on the machine that holds all of your files. A user who can name a file can name it something containing a quote, a semicolon or a backtick.
Never do this:
js
// DANGEROUS. Never build a command line from a path a user controls.
RunCapture("/usr/local/bin/scan " + CtxRelPath());Do this instead: copy the content to a path you invented, and pass that.
js
var safe = "/var/scan/" + ShortUID() + ExtractExt(CtxRelPath());
GetCurrentVFS().ExportFile(CtxRelPath(), safe);
var out = RunCapture("/usr/local/bin/scan --quiet " + safe);
DelFile(safe);ShortUID() produces an alphanumeric name that cannot contain a shell metacharacter, and ExtractExt returns only the extension. Nothing the user chose reaches the command line.
The program runs as the Connector service account
Whatever the Connector can do, the program can do: read the data directory, read the at rest encryption key material, reach the network. Run only software you control, from a path you control. A script that executes something out of a directory users can write to has handed them the machine.
Timeouts
Run and RunCapture block until the program exits, and that time counts against the handler timeout. A program that hangs takes the handler down with it: the engine interrupts the script when the deadline passes, but it cannot always interrupt a blocked call partway through, so a genuinely stuck child process can hold a handler past its budget.
Set a generous timeout on any handler that runs a program, put it on an after handler marked Run in the background, and prefer a program that enforces its own limit. On Linux, timeout is the simplest way to be sure:
js
RunCapture("timeout 30 /usr/local/bin/slow-tool " + safePath);Nothing to run in a container
The published Connector container image is distroless: it contains the Connector binary and nothing else. There is no /bin/sh, no bash, no cmd, no coreutils. Every one of these three functions will fail in that image, because there is nothing to execute.
If your automation depends on running programs, install the Connector natively on Windows or Linux, or build your own image with the tools you need and accept that you are then maintaining it. See Run in Docker.
Platform examples
js
// Windows
Run('cmd /c "C:\\scripts\\process.bat"');
// Linux
Run("/bin/bash /opt/scripts/process.sh");
// A program with arguments, no shell involved
Run("/usr/bin/convert /var/in/photo.png -resize 800x600 /var/out/photo.png");A script that must work on both should branch on something it can observe, since there is no platform constant:
js
var isWindows = DirExists("C:\\Windows");