Skip to content

CompilerSandboxGranularity

Phil Hagelberg edited this page Jan 6, 2022 · 1 revision

Currently code defaults to having all macros sandboxed. It's possible to disable the sandboxing altogether, but sometimes that's not ideal. What if you want to just allow a specific macro module to have access to things without opening it up to every macro?

Settings

The most basic improvement would be to disable the sandbox on a per-module basis.

This could be enabled by a command line flag such as --compiler-sandbox-skip module1,my-module,macrotown giving a comma-delimited list of modules for which to disable the sandbox. There is precedent for this in the existing flag --skip-include module1,module2,module3 using a comma-delimited list of module names, or --globals g1,g2 with identifiers rather than module names.

Macro capabilities

We could build even more fine-grained access by specifying a list of globals to allow on a per-module basis. However, this seems less useful than it could be; really there are only three interesting things you would want to let thru:

  • io.open (both read and write)
  • io.popen
  • require

The most common option is going to be reading files. Macros which read files are a really valid use case for non-pure-function macros, and so we should definitely allow it. However, rather than just allowing blanket access to all of io.open we should allow you to specify a directory to read from, and replace io.open in that environment with a function which checks the argument to ensure that it's reading from the specified directory before passing on the call to the real io.open.

Similarly we could let you specify that a given module is only allowed to call io.popen with a given string as its argument.

We could also allow you to specify that you want module X to be allowed to require an un-sandboxed version of module Y; for instance if you want access to the network or something, but this seems like a stretch; at this point it's probably better to just switch off the sandboxing entirely?

Specifying options

At this point we are reaching the limit of what it's possible to convey thru command-line flags. The list of capabilities should be stored in a file and loaded by the compiler. Perhaps something like this:

$ fennel --macro-capabilities capabilities.fnl --compile foo.fnl

Where capabilities.fnl would be something like this, loaded in a strict sandboxed environment of course:

{:foo.macros {:read ["/path/to/config"
                     "resources/"]
              :write ["/tmp"]}
 :other.macros {:execute ["/sbin/ifconfig"]}}
Clone this wiki locally