diff --git a/src/core/corelib.c b/src/core/corelib.c index a3d8d06d..b42f96fd 100644 --- a/src/core/corelib.c +++ b/src/core/corelib.c @@ -667,6 +667,59 @@ JANET_CORE_FN(janet_core_memcmp, return janet_wrap_integer(memcmp(a.bytes + offset_a, b.bytes + offset_b, (size_t) len)); } +typedef struct SandboxOption { + const char *name; + uint32_t flag; +} SandboxOption; + +static const SandboxOption sandbox_options[] = { + {"all", JANET_SANDBOX_ALL}, + {"env", JANET_SANDBOX_ENV}, + {"ffi", JANET_SANDBOX_FFI}, + {"fs", JANET_SANDBOX_FS}, + {"fs-read", JANET_SANDBOX_FS_READ}, + {"fs-write", JANET_SANDBOX_FS_WRITE}, + {"hrtime", JANET_SANDBOX_HRTIME}, + {"net", JANET_SANDBOX_NET}, + {"net-connect", JANET_SANDBOX_NET_CONNECT}, + {"net-listen", JANET_SANDBOX_NET_LISTEN}, + {"sandbox", JANET_SANDBOX_SANDBOX}, + {"subprocess", JANET_SANDBOX_SUBPROCESS}, + {NULL, 0} +}; + +JANET_CORE_FN(janet_core_sandbox, + "(sandbox & forbidden-capabilities)", + "Disable feature sets to prevent the interpreter from using certain system resources. " + "Once a feature is disabled, there is no way to re-enable it. Cabapiblities can be:\n\n" + "* :sandbox - disallow calling this function\n" + "* :fs - disallow access to the file system\n" + "* :fs-read - disallow read access to the file system\n" + "* :fs-write - disallow write access to the file system\n" + "* :env - disallow reading and write env variables\n" + "* :subprocess - disallow running subprocesses\n" + "* :hrtime - disallow high-resolution timers\n" + "* :ffi - disallow FFI (recommended if disabling anythin else)\n" + "* :net-connect - disallow making outbound network connctions\n" + "* :net-listen - disallow accepting inbound network connctions\n" + "* :net - disallow network access\n" + "* :all - disallow all (except IO to stdout, stderr, and stdin)") { + uint32_t flags = 0; + for (int32_t i = 0; i < argc; i++) { + JanetKeyword kw = janet_getkeyword(argv, i); + const SandboxOption *opt = sandbox_options; + while (opt->name != NULL) { + if (janet_cstrcmp(kw, opt->name) == 0) { + flags |= opt->flag; + break; + } + opt++; + } + } + janet_sandbox(flags); + return janet_wrap_nil(); +} + #ifdef JANET_BOOTSTRAP /* Utility for inline assembly */ @@ -970,6 +1023,7 @@ static void janet_load_libs(JanetTable *env) { JANET_CORE_REG("signal", janet_core_signal), JANET_CORE_REG("memcmp", janet_core_memcmp), JANET_CORE_REG("getproto", janet_core_getproto), + JANET_CORE_REG("sandbox", janet_core_sandbox), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, corelib_cfuns); diff --git a/src/core/ev.c b/src/core/ev.c index fa8856e6..fedc44f0 100644 --- a/src/core/ev.c +++ b/src/core/ev.c @@ -2778,6 +2778,7 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) { uint32_t flags = args.tag; args.tag = 0; janet_init(); + janet_vm.sandbox_flags = (uint32_t) args.argi; JanetTryState tstate; JanetSignal signal = janet_try(&tstate); if (!signal) { @@ -2930,13 +2931,13 @@ JANET_CORE_FN(cfun_ev_thread, JanetEVGenericMessage arguments; memset(&arguments, 0, sizeof(arguments)); arguments.tag = (uint32_t) flags; - arguments.argi = argc; + arguments.argi = (uint32_t) janet_vm.sandbox_flags; arguments.argp = buffer; arguments.fiber = NULL; janet_ev_threaded_call(janet_go_thread_subr, arguments, janet_ev_default_threaded_callback); return janet_wrap_nil(); } else { - janet_ev_threaded_await(janet_go_thread_subr, (uint32_t) flags, argc, buffer); + janet_ev_threaded_await(janet_go_thread_subr, (uint32_t) flags, (uint32_t) janet_vm.sandbox_flags, buffer); } } diff --git a/src/core/ffi.c b/src/core/ffi.c index 0e5de3de..d0b3119a 100644 --- a/src/core/ffi.c +++ b/src/core/ffi.c @@ -1301,6 +1301,7 @@ JANET_CORE_FN(cfun_ffi_jitfn, "(ffi/jitfn bytes)", "Create an abstract type that can be used as the pointer argument to `ffi/call`. The content " "of `bytes` is architecture specific machine code that will be copied into executable memory.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_fixarity(argc, 1); JanetByteView bytes = janet_getbytes(argv, 0); @@ -1349,6 +1350,7 @@ JANET_CORE_FN(cfun_ffi_call, "(ffi/call pointer signature & args)", "Call a raw pointer as a function pointer. The function signature specifies " "how Janet values in `args` are converted to native machine types.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_arity(argc, 2, -1); void *function_pointer = janet_ffi_get_callable_pointer(argv, 0); JanetFFISignature *signature = janet_getabstract(argv, 1, &janet_signature_type); @@ -1373,6 +1375,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write, "Append a native tyep to a buffer such as it would appear in memory. This can be used " "to pass pointers to structs in the ffi, or send C/C++/native structs over the network " "or to files. Returns a modifed buffer or a new buffer if one is not supplied.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_arity(argc, 2, 3); JanetFFIType type = decode_ffi_type(argv[0]); uint32_t el_size = (uint32_t) type_size(type); @@ -1389,6 +1392,7 @@ JANET_CORE_FN(cfun_ffi_buffer_read, "Parse a native struct out of a buffer and convert it to normal Janet data structures. " "This function is the inverse of `ffi/write`. `bytes` can also be a raw pointer, although " "this is unsafe.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_arity(argc, 2, 3); JanetFFIType type = decode_ffi_type(argv[0]); size_t offset = (size_t) janet_optnat(argv, argc, 2, 0); @@ -1435,6 +1439,7 @@ JANET_CORE_FN(janet_core_raw_native, " or run any code from it. This is different than `native`, which will " "run initialization code to get a module table. If `path` is nil, opens the current running binary. " "Returns a `core/native`.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_arity(argc, 0, 1); const char *path = janet_optcstring(argv, argc, 0, NULL); Clib lib = load_clib(path); @@ -1450,6 +1455,7 @@ JANET_CORE_FN(janet_core_native_lookup, "(ffi/lookup native symbol-name)", "Lookup a symbol from a native object. All symbol lookups will return a raw pointer " "if the symbol is found, else nil.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_fixarity(argc, 2); JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); const char *sym = janet_getcstring(argv, 1); @@ -1463,6 +1469,7 @@ JANET_CORE_FN(janet_core_native_close, "(ffi/close native)", "Free a native object. Dereferencing pointers to symbols in the object will have undefined " "behavior after freeing.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_fixarity(argc, 1); JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); if (anative->closed) janet_panic("native object already closed"); @@ -1475,6 +1482,7 @@ JANET_CORE_FN(janet_core_native_close, JANET_CORE_FN(cfun_ffi_malloc, "(ffi/malloc size)", "Allocates memory directly using the system memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_fixarity(argc, 1); size_t size = janet_getsize(argv, 0); if (size == 0) return janet_wrap_nil(); @@ -1484,6 +1492,7 @@ JANET_CORE_FN(cfun_ffi_malloc, JANET_CORE_FN(cfun_ffi_free, "(ffi/free pointer)", "Free memory allocated with `ffi/malloc`.") { + janet_sandbox_assert(JANET_SANDBOX_FFI); janet_fixarity(argc, 1); if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil(); void *pointer = janet_getpointer(argv, 0); diff --git a/src/core/io.c b/src/core/io.c index 31cf8b07..e89ce5f9 100644 --- a/src/core/io.c +++ b/src/core/io.c @@ -69,12 +69,15 @@ static int32_t checkflags(const uint8_t *str) { break; case 'w': flags |= JANET_FILE_WRITE; + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); break; case 'a': flags |= JANET_FILE_APPEND; + janet_sandbox_assert(JANET_SANDBOX_FS); break; case 'r': flags |= JANET_FILE_READ; + janet_sandbox_assert(JANET_SANDBOX_FS_READ); break; } for (i = 1; i < len; i++) { @@ -84,6 +87,7 @@ static int32_t checkflags(const uint8_t *str) { break; case '+': if (flags & JANET_FILE_UPDATE) return -1; + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); flags |= JANET_FILE_UPDATE; break; case 'b': @@ -116,6 +120,7 @@ JANET_CORE_FN(cfun_io_temp, "(file/temp)", "Open an anonymous temporary file that is removed on close. " "Raises an error on failure.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); (void)argv; janet_fixarity(argc, 0); // XXX use mkostemp when we can to avoid CLOEXEC race. @@ -148,6 +153,7 @@ JANET_CORE_FN(cfun_io_fopen, flags = checkflags(fmode); } else { fmode = (const uint8_t *)"r"; + janet_sandbox_assert(JANET_SANDBOX_FS_READ); flags = JANET_FILE_READ; } FILE *f = fopen((const char *)fname, (const char *)fmode); diff --git a/src/core/net.c b/src/core/net.c index 2ea715e8..02522403 100644 --- a/src/core/net.c +++ b/src/core/net.c @@ -379,6 +379,7 @@ JANET_CORE_FN(cfun_net_connect, "to specify a connection type, either :stream or :datagram. The default is :stream. " "Bindhost is an optional string to select from what address to make the outgoing " "connection, with the default being the same as using the OS's preferred address. ") { + janet_sandbox_assert(JANET_SANDBOX_NET_CONNECT); janet_arity(argc, 2, 5); /* Check arguments */ @@ -573,6 +574,7 @@ JANET_CORE_FN(cfun_net_listen, "The type parameter specifies the type of network connection, either " "a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is " ":stream. The host and port arguments are the same as in net/address.") { + janet_sandbox_assert(JANET_SANDBOX_NET_LISTEN); janet_arity(argc, 2, 3); /* Get host, port, and handler*/ diff --git a/src/core/os.c b/src/core/os.c index 7fdfcd67..0708e7d1 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -878,6 +878,7 @@ static JanetFile *get_stdio_for_handle(JanetHandle handle, void *orig, int iswri #endif static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) { + janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS); janet_arity(argc, 1, 3); /* Get flags */ @@ -1171,6 +1172,7 @@ static JanetEVGenericMessage os_shell_subr(JanetEVGenericMessage args) { JANET_CORE_FN(os_shell, "(os/shell str)", "Pass a command string str directly to the system shell.") { + janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS); janet_arity(argc, 0, 1); const char *cmd = argc ? janet_getcstring(argv, 0) @@ -1190,6 +1192,7 @@ JANET_CORE_FN(os_shell, JANET_CORE_FN(os_environ, "(os/environ)", "Get a copy of the OS environment table.") { + janet_sandbox_assert(JANET_SANDBOX_ENV); (void) argv; janet_fixarity(argc, 0); int32_t nenv = 0; @@ -1221,6 +1224,7 @@ JANET_CORE_FN(os_environ, JANET_CORE_FN(os_getenv, "(os/getenv variable &opt dflt)", "Get the string value of an environment variable.") { + janet_sandbox_assert(JANET_SANDBOX_ENV); janet_arity(argc, 1, 2); const char *cstr = janet_getcstring(argv, 0); const char *res = getenv(cstr); @@ -1244,6 +1248,7 @@ JANET_CORE_FN(os_setenv, #define SETENV(K,V) setenv(K, V, 1) #define UNSETENV(K) unsetenv(K) #endif + janet_sandbox_assert(JANET_SANDBOX_ENV); janet_arity(argc, 1, 2); const char *ks = janet_getcstring(argv, 0); const char *vs = janet_optcstring(argv, argc, 1, NULL); @@ -1271,6 +1276,7 @@ JANET_CORE_FN(os_clock, "(os/clock)", "Return the number of whole + fractional seconds since some fixed point in time. The clock " "is guaranteed to be non-decreasing in real time.") { + janet_sandbox_assert(JANET_SANDBOX_HRTIME); janet_fixarity(argc, 0); (void) argv; struct timespec tv; @@ -1512,6 +1518,7 @@ JANET_CORE_FN(os_link, "Iff symlink is truthy, creates a symlink. " "Iff symlink is falsey or not provided, " "creates a hard link. Does not work on Windows.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); janet_arity(argc, 2, 3); #ifdef JANET_WINDOWS (void) argc; @@ -1530,6 +1537,7 @@ JANET_CORE_FN(os_link, JANET_CORE_FN(os_symlink, "(os/symlink oldpath newpath)", "Create a symlink from oldpath to newpath, returning nil. Same as `(os/link oldpath newpath true)`.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); janet_fixarity(argc, 2); #ifdef JANET_WINDOWS (void) argc; @@ -1552,6 +1560,7 @@ JANET_CORE_FN(os_mkdir, "Create a new directory. The path will be relative to the current directory if relative, otherwise " "it will be an absolute path. Returns true if the directory was created, false if the directory already exists, and " "errors otherwise.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); janet_fixarity(argc, 1); const char *path = janet_getcstring(argv, 0); #ifdef JANET_WINDOWS @@ -1567,6 +1576,7 @@ JANET_CORE_FN(os_mkdir, JANET_CORE_FN(os_rmdir, "(os/rmdir path)", "Delete a directory. The directory must be empty to succeed.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); janet_fixarity(argc, 1); const char *path = janet_getcstring(argv, 0); #ifdef JANET_WINDOWS @@ -1581,6 +1591,7 @@ JANET_CORE_FN(os_rmdir, JANET_CORE_FN(os_cd, "(os/cd path)", "Change current directory to path. Returns nil on success, errors on failure.") { + janet_sandbox_assert(JANET_SANDBOX_FS_READ); janet_fixarity(argc, 1); const char *path = janet_getcstring(argv, 0); #ifdef JANET_WINDOWS @@ -1596,6 +1607,7 @@ JANET_CORE_FN(os_touch, "(os/touch path &opt actime modtime)", "Update the access time and modification times for a file. By default, sets " "times to the current time.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); janet_arity(argc, 1, 3); const char *path = janet_getcstring(argv, 0); struct utimbuf timebuf, *bufp; @@ -1845,6 +1857,7 @@ static const struct OsStatGetter os_stat_getters[] = { }; static Janet os_stat_or_lstat(int do_lstat, int32_t argc, Janet *argv) { + janet_sandbox_assert(JANET_SANDBOX_FS_READ); janet_arity(argc, 1, 2); const char *path = janet_getcstring(argv, 0); JanetTable *tab = NULL; @@ -1926,6 +1939,7 @@ JANET_CORE_FN(os_chmod, "`os/perm-string`, or an integer as returned by `os/perm-int`. " "When `mode` is an integer, it is interpreted as a Unix permission value, best specified in octal, like " "8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); janet_fixarity(argc, 2); const char *path = janet_getcstring(argv, 0); #ifdef JANET_WINDOWS @@ -1941,6 +1955,7 @@ JANET_CORE_FN(os_chmod, JANET_CORE_FN(os_umask, "(os/umask mask)", "Set a new umask, returns the old umask.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); janet_fixarity(argc, 1); int mask = (int) os_getmode(argv, 0); #ifdef JANET_WINDOWS @@ -1956,6 +1971,7 @@ JANET_CORE_FN(os_dir, "(os/dir dir &opt array)", "Iterate over files and subdirectories in a directory. Returns an array of paths parts, " "with only the file name or directory name and no prefix.") { + janet_sandbox_assert(JANET_SANDBOX_FS_READ); janet_arity(argc, 1, 2); const char *dir = janet_getcstring(argv, 0); JanetArray *paths = (argc == 2) ? janet_getarray(argv, 1) : janet_array(0); @@ -1993,6 +2009,7 @@ JANET_CORE_FN(os_dir, JANET_CORE_FN(os_rename, "(os/rename oldname newname)", "Rename a file on disk to a new path. Returns nil.") { + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); janet_fixarity(argc, 2); const char *src = janet_getcstring(argv, 0); const char *dest = janet_getcstring(argv, 1); @@ -2007,6 +2024,7 @@ JANET_CORE_FN(os_realpath, "(os/realpath path)", "Get the absolute path for a given path, following ../, ./, and symlinks. " "Returns an absolute path as a string.") { + janet_sandbox_assert(JANET_SANDBOX_FS_READ); janet_fixarity(argc, 1); const char *src = janet_getcstring(argv, 0); #ifdef JANET_NO_REALPATH @@ -2101,19 +2119,23 @@ JANET_CORE_FN(os_open, case 'r': desiredAccess |= GENERIC_READ; stream_flags |= JANET_STREAM_READABLE; + janet_sandbox_assert(JANET_SANDBOX_FS_READ); break; case 'w': desiredAccess |= GENERIC_WRITE; stream_flags |= JANET_STREAM_WRITABLE; + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); break; case 'c': creatUnix |= OCREAT; + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); break; case 'e': creatUnix |= OEXCL; break; case 't': creatUnix |= OTRUNC; + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); break; /* Windows only flags */ case 'D': @@ -2183,19 +2205,23 @@ JANET_CORE_FN(os_open, case 'r': read_flag = 1; stream_flags |= JANET_STREAM_READABLE; + janet_sandbox_assert(JANET_SANDBOX_FS_READ); break; case 'w': write_flag = 1; stream_flags |= JANET_STREAM_WRITABLE; + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); break; case 'c': open_flags |= O_CREAT; + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); break; case 'e': open_flags |= O_EXCL; break; case 't': open_flags |= O_TRUNC; + janet_sandbox_assert(JANET_SANDBOX_FS_WRITE); break; /* posix only */ case 'x': @@ -2270,48 +2296,65 @@ void janet_lib_os(JanetTable *env) { JANET_CORE_REG("os/arch", os_arch), JANET_CORE_REG("os/compiler", os_compiler), #ifndef JANET_REDUCED_OS + + /* misc (un-sandboxed) */ + JANET_CORE_REG("os/cpu-count", os_cpu_count), + JANET_CORE_REG("os/cwd", os_cwd), + JANET_CORE_REG("os/cryptorand", os_cryptorand), + JANET_CORE_REG("os/perm-string", os_permission_string), + JANET_CORE_REG("os/perm-int", os_permission_int), + JANET_CORE_REG("os/mktime", os_mktime), + JANET_CORE_REG("os/time", os_time), /* not high resolution */ + JANET_CORE_REG("os/date", os_date), /* not high resolution */ + JANET_CORE_REG("os/sleep", os_sleep), + + /* env functions */ JANET_CORE_REG("os/environ", os_environ), JANET_CORE_REG("os/getenv", os_getenv), + JANET_CORE_REG("os/setenv", os_setenv), + + /* fs read */ JANET_CORE_REG("os/dir", os_dir), JANET_CORE_REG("os/stat", os_stat), JANET_CORE_REG("os/lstat", os_lstat), JANET_CORE_REG("os/chmod", os_chmod), JANET_CORE_REG("os/touch", os_touch), + JANET_CORE_REG("os/realpath", os_realpath), JANET_CORE_REG("os/cd", os_cd), - JANET_CORE_REG("os/cpu-count", os_cpu_count), #ifndef JANET_NO_UMASK JANET_CORE_REG("os/umask", os_umask), #endif +#ifndef JANET_NO_SYMLINKS + JANET_CORE_REG("os/readlink", os_readlink), +#endif + + /* fs write */ JANET_CORE_REG("os/mkdir", os_mkdir), JANET_CORE_REG("os/rmdir", os_rmdir), JANET_CORE_REG("os/rm", os_remove), JANET_CORE_REG("os/link", os_link), + JANET_CORE_REG("os/rename", os_rename), #ifndef JANET_NO_SYMLINKS JANET_CORE_REG("os/symlink", os_symlink), - JANET_CORE_REG("os/readlink", os_readlink), #endif + + /* processes */ #ifndef JANET_NO_PROCESSES JANET_CORE_REG("os/execute", os_execute), JANET_CORE_REG("os/spawn", os_spawn), JANET_CORE_REG("os/shell", os_shell), + /* no need to sandbox process management if you can't create processes + * (allows for limited functionality if use exposes C-functions to create specific processes) */ JANET_CORE_REG("os/proc-wait", os_proc_wait), JANET_CORE_REG("os/proc-kill", os_proc_kill), JANET_CORE_REG("os/proc-close", os_proc_close), #endif - JANET_CORE_REG("os/setenv", os_setenv), - JANET_CORE_REG("os/time", os_time), - JANET_CORE_REG("os/mktime", os_mktime), + + /* high resolution timers */ JANET_CORE_REG("os/clock", os_clock), - JANET_CORE_REG("os/sleep", os_sleep), - JANET_CORE_REG("os/cwd", os_cwd), - JANET_CORE_REG("os/cryptorand", os_cryptorand), - JANET_CORE_REG("os/date", os_date), - JANET_CORE_REG("os/rename", os_rename), - JANET_CORE_REG("os/realpath", os_realpath), - JANET_CORE_REG("os/perm-string", os_permission_string), - JANET_CORE_REG("os/perm-int", os_permission_int), + #ifdef JANET_EV - JANET_CORE_REG("os/open", os_open), + JANET_CORE_REG("os/open", os_open), /* fs read and write */ JANET_CORE_REG("os/pipe", os_pipe), #endif #endif diff --git a/src/core/state.h b/src/core/state.h index 01b51c2e..0813d392 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -135,6 +135,9 @@ struct JanetVM { size_t scratch_cap; size_t scratch_len; + /* Sandbox flags */ + uint32_t sandbox_flags; + /* Random number generator */ JanetRNG rng; diff --git a/src/core/vm.c b/src/core/vm.c index 374af2f0..3a29e207 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1559,6 +1559,9 @@ int janet_init(void) { janet_vm.scratch_len = 0; janet_vm.scratch_cap = 0; + /* Sandbox flags */ + janet_vm.sandbox_flags = 0; + /* Initialize registry */ janet_vm.registry = NULL; janet_vm.registry_cap = 0; @@ -1600,6 +1603,18 @@ int janet_init(void) { return 0; } +/* Disable some features at runtime with no way to re-enable them */ +void janet_sandbox(uint32_t flags) { + janet_sandbox_assert(JANET_SANDBOX_SANDBOX); + janet_vm.sandbox_flags |= flags; +} + +void janet_sandbox_assert(uint32_t forbidden_flags) { + if (forbidden_flags & janet_vm.sandbox_flags) { + janet_panic("operation forbidden by sandbox"); + } +} + /* Clear all memory associated with the VM */ void janet_deinit(void) { janet_clear_memory(); diff --git a/src/include/janet.h b/src/include/janet.h index 5c2a26ce..896b789e 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1798,6 +1798,22 @@ JANET_API Janet janet_mcall(const char *name, int32_t argc, Janet *argv); JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err); JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix); +/* Sandboxing API */ +#define JANET_SANDBOX_SANDBOX 1 +#define JANET_SANDBOX_SUBPROCESS 2 +#define JANET_SANDBOX_NET_CONNECT 4 +#define JANET_SANDBOX_NET_LISTEN 8 +#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN) +#define JANET_SANDBOX_FFI 16 +#define JANET_SANDBOX_FS_WRITE 32 +#define JANET_SANDBOX_FS_READ 64 +#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ) +#define JANET_SANDBOX_HRTIME 128 +#define JANET_SANDBOX_ENV 256 +#define JANET_SANDBOX_ALL (UINT32_MAX) +JANET_API void janet_sandbox(uint32_t flags); +JANET_API void janet_sandbox_assert(uint32_t forbidden_flags); + /* Scratch Memory API */ typedef void (*JanetScratchFinalizer)(void *);