Class: RobotLab::Sandbox::Seatbelt
- Inherits:
-
Object
- Object
- RobotLab::Sandbox::Seatbelt
- Defined in:
- lib/robot_lab/sandbox/seatbelt.rb
Overview
macOS strategy: generates a deny-by-default sandbox-exec profile from the
effective grant and wraps the command as
sandbox-exec -f
The profile allows process exec plus reads of system locations (so the interpreter can load), reads of the skill bundle and granted paths, writes only to granted paths (and the standard /dev sinks), and network only when granted. Everything else -- notably $HOME, so SSH/cloud credentials -- is denied. Interpreters installed under $HOME (e.g. rbenv) are not visible; declare them in fs_read or mark the skill trust: core.
Constant Summary collapse
- SYSTEM_READ =
System locations a typical interpreter needs to read to start up.
%w[/usr /bin /sbin /System /Library /opt /private/etc /dev /var/select].freeze
- DEV_WRITE =
%w[/dev/null /dev/stdout /dev/stderr /dev/dtracehelper /dev/tty].freeze
Instance Method Summary collapse
- #cleanup ⇒ Object
-
#initialize(grant, skill_dir:) ⇒ Seatbelt
constructor
A new instance of Seatbelt.
-
#profile_text ⇒ Object
The generated Seatbelt profile text (public for testing).
- #wrap(cmd) ⇒ Object
Constructor Details
#initialize(grant, skill_dir:) ⇒ Seatbelt
Returns a new instance of Seatbelt.
22 23 24 25 26 |
# File 'lib/robot_lab/sandbox/seatbelt.rb', line 22 def initialize(grant, skill_dir:) @grant = grant @skill_dir = File.(skill_dir.to_s) @profile = nil end |
Instance Method Details
#cleanup ⇒ Object
33 34 35 36 37 |
# File 'lib/robot_lab/sandbox/seatbelt.rb', line 33 def cleanup File.unlink(@profile) if @profile && File.exist?(@profile) rescue StandardError nil end |
#profile_text ⇒ Object
The generated Seatbelt profile text (public for testing).
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/robot_lab/sandbox/seatbelt.rb', line 40 def profile_text reads = canonicalize(SYSTEM_READ + [@skill_dir] + @grant.fs_read) writes = canonicalize(@grant.fs_write) lines = [ "(version 1)", # bsd.sb supplies the base rules a process needs to start (dyld, mach # bootstrap, etc.); without it a deny-default profile aborts the binary. '(import "bsd.sb")', "(deny default)", "(allow process-fork)", "(allow process-exec)", "(allow sysctl-read)", "(allow mach-lookup)", # Metadata (stat/lookup) on any path so the interpreter can traverse to # granted files; reading file *contents* stays restricted below. "(allow file-read-metadata)", read_rule(reads), write_rule(DEV_WRITE.map { |p| [:literal, p] } + writes.map { |p| [:subpath, p] }) ] lines << "(allow network*)" if @grant.network "#{lines.compact.join("\n")}\n" end |
#wrap(cmd) ⇒ Object
28 29 30 31 |
# File 'lib/robot_lab/sandbox/seatbelt.rb', line 28 def wrap(cmd) @profile = write_profile ["sandbox-exec", "-f", @profile, *cmd] end |