Skip to content

Latest commit

 

History

37 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cl-exec-sandbox

cl-exec-sandbox is a policy-driven process sandboxing library for Common Lisp. Its policy model provides:

  • read, write, and deny filesystem rules
  • literal paths, deny globs, and portable special roots
  • more-specific nested policy overrides
  • read-only host and workspace-write presets
  • protected project metadata such as .git
  • full, isolated, and managed proxy-only network modes
  • process, user, IPC, UTS, and network namespace isolation on Linux
  • fresh /proc and minimal /dev mounts
  • child timeouts and optionally bounded captured output
  • explicit capability discovery

One policy is translated by a per-host backend. A policy a backend cannot enforce signals sandbox-unavailable naming the missing capability. Query sandbox-capabilities or sandbox-supported-p before relying on a capability.

The policy surface and Linux enforcement model were checked against OpenAI Codex commit 2e1607ee2fa8099a233df7437adee5f16a741905. Codex is a reference.

Linux backend

The Linux backend targets x86-64 and SBCL. It uses the system bwrap binary for filesystem and namespace isolation, plus a private helper for no_new_privs, seccomp, and managed proxy routing. The helper lives beside the Lisp sources.

macOS backend

The macOS backend runs commands under Seatbelt through /usr/bin/sandbox-exec, generating a profile from the same policy. Resolved rules are emitted from the broadest to the most specific, so Seatbelt’s last-match-wins resolution reproduces nested overrides and protected metadata. Whole-root rules are emitted before device access, so a read-only root still permits /dev. A rule naming a single file becomes a literal filter.

On macOS:

  • :enabled and :isolated networking work
  • sandbox-capabilities reports :process-namespaces as false
  • process separation is an accidental-damage boundary

Deny globs need rg on either host. Seatbelt installations outside /usr/bin can set CL_EXEC_SANDBOX_SEATBELT to an absolute executable path.

Windows backend

The experimental Windows backend combines a fresh AppContainer identity with an invocation-specific restricted-token SID for deny rules and a kill-on-close Job Object for descendants. Run it as a standard Windows user. Build scripts/build-windows-helper.ps1 from a Visual Studio SDK developer shell with Clang installed, then ship build/cl-exec-sandbox-windows.exe beside the Lisp sources. Alternatively, set CL_EXEC_SANDBOX_WINDOWS_HELPER to its absolute path outside writable scopes.

Use appcontainer-sandbox-policy with explicit existing :read-roots and :workspace-roots. Include the command’s binaries and dependencies in the read scopes. Windows also supplies its normal AppContainer system-resource access and private profile storage. Networking is isolated, including loopback.

(let ((policy
        (cl-exec-sandbox:appcontainer-sandbox-policy
         :workspace-roots (list #P"C:/work/project/")
         :read-roots (list #P"C:/tools/"))))
  (cl-exec-sandbox:run-sandboxed
   "C:/tools/tool.exe" '("--version")
   :policy policy :working-directory #P"C:/work/project/" :timeout 30))

The backend rejects whole-host policies, network modes other than :isolated, proc mounts, PID namespaces, globs, drive roots, UNC/device paths, reparse points, hard-linked files, and paths of 240 characters or more. The default protected metadata names are .git, .agents, and .codex. This backend is not policy-equivalent to the Linux whole-host presets.

Filesystem grants are temporary SID-specific ACL entries. Native execution and ACL changes are serialized within a Windows session. Serialize calls with overlapping roots as well, including plan construction and cleanup of missing metadata directories. Keep the trees and ACLs free of concurrent changes by other host processes during a run. Normal completion and run-sandboxed cancellation remove the grants and profile; cleanup failures are reported. Terminating both the Lisp supervisor and helper can leave stale SID entries and a profile. This prototype has no persistent crash-recovery journal. When launching a plan yourself, call sandbox-plan-cleanup after terminating and waiting for its helper.

Run (asdf:test-system :cl-exec-sandbox/windows-tests) after compiling tests/windows-child.c to build/windows-child.exe. CI runs these enforcement tests under a disposable non-administrator account.

Example

(let ((policy
        (cl-exec-sandbox:workspace-write-sandbox-policy
         :workspace-roots (list #P"/work/project/")
         :network :isolated)))
  (cl-exec-sandbox:run-sandboxed
   "/bin/sh"
   '("-c" "git status --short")
   :policy policy
   :working-directory #P"/work/project/"
   :output-limit 65536
   :error-output-limit 65536))

When a limit is exceeded, the returned prefix is available through sandbox-result-output or sandbox-result-error-output. Inspect sandbox-result-output-truncated-p and sandbox-result-error-output-truncated-p before presenting captured output.

Checks

Build the private Linux helper and run the complete test suite:

./check

Seatbelt profile translation is a pure function of a policy, so its tests run on any host. They assert the generated profile text.

Applications which vendor this system should run scripts/build-helper during their build and ship build/cl-exec-sandbox-process-group beside the Lisp sources. Linux applications using restricted networking must also ship build/cl-exec-sandbox-helper. Set CL_EXEC_SANDBOX_PROCESS_GROUP_HELPER or CL_EXEC_SANDBOX_HELPER to alternate absolute helper paths when the installed layout differs. Packaged Bubblewrap installations outside /usr/bin and /bin can set CL_EXEC_SANDBOX_BWRAP to its absolute executable path.

Part of the Lambda Symbolics library shelf.

About

A policy-driven process sandboxing library for Common Lisp

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages