Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Saiph

Fail-closed native process sandboxing for Ruby

Gem Version Downloads Ruby Version MIT License

Features · Installation · Quick Start · Backends · Security


Saiph applies native operating-system restrictions to child processes. It is a small, pure Ruby boundary for running untrusted plugins without depending on an editor or plugin API.

Saiph fails closed. available? is true only after the native backend passes a local capability probe, and a child exits with status 126 if setup fails. Saiph never substitutes an ordinary unsandboxed process.

Features

  • Native sandbox backends for macOS, Linux, and Windows
  • Immutable allowlist policies for files, networking, processes, and environment variables
  • Fail-closed capability probing and sandbox setup
  • Shell-free process spawning with standard Ruby redirection options
  • Small pure Ruby API over native operating-system facilities

Installation

bundle add saiph

Saiph supports Ruby 3.1 and later.

Quick Start

require "saiph"

unless Saiph.available?
  warn "OS sandbox unavailable"
  return
end

policy = Saiph::Policy.new(
  read_paths: [File.expand_path("plugin.rb", __dir__)],
  write_paths: [File.expand_path("tmp", __dir__)],
  network: false,
  exec: false,
  env: ["LANG"]
)

pid = Saiph.spawn(
  [RbConfig.ruby, File.expand_path("plugin.rb", __dir__)],
  policy: policy,
  env: {"LANG" => "en_US.UTF-8"},
  out: $stdout,
  err: $stderr
)
status = Process.waitpid2(pid).last
raise "sandbox setup failed" if status.exitstatus == 126

Policies

spawn returns a process ID and accepts normal Process.spawn redirections and options. Pass the command as an argument array. A string is treated as one executable name; Saiph never invokes a shell.

Policy fields have these meanings:

  • read_paths: existing absolute files or directory trees the child may read.
  • write_paths: existing absolute files or directory trees the child may read and modify.
  • network: whether network access is allowed.
  • exec: whether the child may create descendant processes.
  • env: names of environment variables visible to the child. Values may be overridden with spawn(..., env: {...}); undeclared names are rejected.

The launcher and system dynamic libraries need a small implicit read-only runtime allowance. On Unix this covers the selected executable, the active Ruby installation, and system library directories. It does not include the home directory or temporary directories.

Saiph.apply!(policy) restricts the current process and is irreversible. Use spawn unless the current process was created solely to become a sandbox.

Backends

Platform backend Enforcement
macOS :seatbelt sandbox_init profile with deny-by-default file, network, and process rules
Linux :seccomp user/mount/network namespaces, Landlock filesystem rules, and seccomp-BPF
Windows :appcontainer ephemeral AppContainer plus a Job Object process limit

Linux reports unavailable unless namespaces, Landlock, and seccomp all work in the current environment. This is common in restricted CI containers and is an expected reason to skip integration tests.

Windows 0.1 supports a no-filesystem-grant, no-network policy. Non-empty path grants and network: true raise Saiph::Unsupported because safely changing host ACLs requires application-owned provisioning. apply! is also unsupported on Windows; AppContainer tokens must be selected at process creation.

Security and cleanup

  • Paths are resolved before launch, so a symlink cannot retarget an allowance.
  • The child environment is rebuilt from its allowlist.
  • Policy transfer uses a private inherited pipe, not a temporary file.
  • Backend probes run in disposable children because Unix restrictions cannot be relaxed.
  • Linux blocks signal delivery to host processes in addition to ptrace and cross-process memory access.
  • AppContainer profiles, native handles, pipes, and Landlock descriptors are closed on both success and failure.

Saiph reduces OS capabilities; it does not validate plugin code or replace application-level authentication and authorization.

Development

bundle install
bundle exec rake test
bundle exec rbs -I sig validate
BUDGET=1 bundle exec rake bench
gem build --strict saiph.gemspec

Contributing

Bug reports and pull requests are welcome at https://github.com/noxdea/saiph.

License

Saiph is available under the MIT License.

About

Fail-closed native process sandboxing for Ruby

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages