Skip to main content

Extension Sandboxing

Phylum's CLI extensions allow developers to impose additional restrictions when running third party applications. This can protect the system from damage when these applications contain vulnerabilities or execute untrusted code.

Example​

The following code provides an example on how you could sandbox cat to only allow access to files in the current working directory or below it:

// Ensure a file path is passed as the only argument.
if (Deno.args.length !== 1) {
console.log("USAGE: local-cat <FILE>");
Deno.exit(123);
}

// Run `cat` in our sandboxed environment.
const output = Phylum.runSandboxed({
cmd: 'cat',
args: [Deno.args[0]],
stdout: 'inherit',
stderr: 'inherit',
exceptions: {
read: ['./'],
write: false,
run: false,
net: false,
},
});

// Propagate the exit code for `cat`.
Deno.exit(output.code);

When running this against a file in your local directory, it will print its content, otherwise, you'll see cat printing the following error:

cat: /tmp/illegal: Permission denied

The important part in this code snippet is the exceptions field. By default access to most system resources is restricted, so if you want to access them from within the sandbox you'll have to add an exception.

Available fields for exceptions are read, write, run, and net. The run permission is a superset of read that allows for execution. While read, write, and run accept either a path to be allowed or a boolean, net only allows for a boolean value.

Limitations​

The Phylum.runSandboxed method is the only recommended means of spawning child processes from an extension. The Deno.Command method should be avoided in order to prevent extensions from escaping the sandbox, and will be disabled in a future version.

The Phylum.runSandboxed method is only allowed to request permissions that are at least as restrictive as the ones specified in the manifest.

Advanced Usage​

By default, access to some paths is granted automatically to make extension sandboxing easier and ensure portability between operating systems. If you want more control over your sandboxing exceptions, you can pass strict: true to the exceptions:

const output = Phylum.runSandboxed({
cmd: 'cat',
args: [Deno.args[0]],
stdout: 'inherit',
stderr: 'inherit',
exceptions: {
// Discard all default sandboxing exceptions.
strict: true,

// You can see the required additional exceptions here.
// These paths might differ between operating systems and distributions.
run: ['/usr/bin/cat', '/usr/lib'],

// These are identical to our previous example.
read: ['./'],
write: false,
net: false,
},
});

Finding Required Exceptions​

It can be somewhat difficult to find out which exceptions you need to add to allow your application to run without any errors. To simplify this a bit you can use the find-permissions extension.

This extension will run a script against each path in your filesystem recursively to validate what the most granular necessary exceptions are. Once completed, it will output all necessary paths.

Most invocations of this extension will probably look something like this:

phylum find-permissions \
--read \
--write \
--pre-bin ./setup.sh \
--bin ./test.sh \
--post-bin ./cleanup.sh

By passing both --read and --write we check for both permissions at the same time. The test.sh script should contain the executable we want to sandbox; in our example it would run cat against some local files. The --pre-bin and --post-bin are optional, but here we could setup and remove local files to run cat against for example.

Since this crawls your entire directory tree, it might take some time. If you don't need file-level granularity you can help speed it up by passing --skip-files.