fennel.dofile sandboxing question

This is more like a question, as I'm unsure what the correct behavior should be here.

I'm assuming that if I pass {:env some-env-table-here} to fennel.dofile my original env should not be affected by a script ran via dofile. However, if the script uses require it still populates root package.loaded.

Here's an example:

; file: f1.fnl
(local fennel (require :fennel))
(print (fennel.view (. package.loaded :f3)))
(fennel.dofile "f2.fnl" {:env {: require}} :f2)
(print (fennel.view (. package.loaded :f3)))
;; file f2.fnl
(local f3 (require :f3))
;; file f3.fnl
(fn foo [] 42)
{: foo}

Running the f1 file prints nil then the table:

fennel f1.fnl 
{:foo #<function: 0x556f6e14c4a0>}

I've thought that maybe this is due to the fact that the default searcher populates root package.loaded, but creating a new searcher and replacing it in the fake env doesn't help here.

So my question: is is possible to properly sandbox fennel.dofile so everything that happens doesn't leak out?

Assigned to
3 months ago
3 months ago
No labels applied.

~technomancy REPORTED BY_DESIGN 3 months ago

The reason this happens is that you passed the real require to your environment, and that function lexically closes over the real package table. Setting the :env will only affect functions which look up a global. The environment can't prevent a closure from using a value that it's already closed over.

If you want to prevent require from changing package then you'll have to pass in a fake require instead. We actually do this in the compiler sandbox so that macros can use require to load modules which are themselves sandboxed: https://git.sr.ht/~technomancy/fennel/tree/main/src/fennel/specials.fnl#L1101

Register here or Log in to comment, or comment via email.