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
.