Class: LittleGhost::Sandbox::Policy

Inherits:
Object
  • Object
show all
Defined in:
lib/little_ghost/sandbox/policy.rb

Overview

Normalizes requested filesystem, process, environment, and child-network controls into one immutable policy. Policy is a declaration, not proof of isolation; the selected backend exposes #effective_policy and rejects controls it cannot enforce.

Constant Summary collapse

COMMON_KEYS =
%i[
  files runtime_paths root_filesystem environment network
].freeze
ACCESS_MODES =

:nodoc:

%i[read_only read_write].freeze
ROOT_FILESYSTEM_MODES =

:nodoc:

%i[isolated read_only read_write].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(files: {root: :read_only}, runtime_paths: {}, root_filesystem: :isolated, environment: {}, network: nil) ⇒ Policy

Builds a backend-independent policy from named Workspace paths.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/little_ghost/sandbox/policy.rb', line 25

def initialize(
  files: {root: :read_only},
  runtime_paths: {},
  root_filesystem: :isolated,
  environment: {},
  network: nil
)
  @files = normalize_paths(files, "files")
  @runtime_paths = normalize_paths(runtime_paths, "runtime_paths")
  @root_filesystem = enum!(root_filesystem, ROOT_FILESYSTEM_MODES, "root filesystem")
  @environment = EnvironmentPolicy.coerce(environment)
  @network = NetworkPolicy.coerce(network)
  freeze
end

Instance Attribute Details

#environmentObject (readonly)

Environment inheritance and explicit values.



47
48
49
# File 'lib/little_ghost/sandbox/policy.rb', line 47

def environment
  @environment
end

#filesObject (readonly)

Named Workspace paths visible to tools and child processes.



41
42
43
# File 'lib/little_ghost/sandbox/policy.rb', line 41

def files
  @files
end

#networkObject (readonly)

Network policy, or nil for a backend-specific secure default.



49
50
51
# File 'lib/little_ghost/sandbox/policy.rb', line 49

def network
  @network
end

#root_filesystemObject (readonly)

Requested host-root access: :isolated, :read_only, or :read_write.



45
46
47
# File 'lib/little_ghost/sandbox/policy.rb', line 45

def root_filesystem
  @root_filesystem
end

#runtime_pathsObject (readonly)

Named workspace paths visible only to sandboxed processes.



43
44
45
# File 'lib/little_ghost/sandbox/policy.rb', line 43

def runtime_paths
  @runtime_paths
end

Class Method Details

.coerce(value = nil, **options) ⇒ Object

Returns an existing policy or builds one from a Hash and keyword options.

Raises:



16
17
18
19
20
21
22
# File 'lib/little_ghost/sandbox/policy.rb', line 16

def self.coerce(value = nil, **options)
  return value if value.is_a?(self) && options.empty?
  values = value.nil? ? {} : value
  raise PolicyError, "sandbox policy must be a Hash or Sandbox::Policy" unless values.is_a?(Hash)

  new(**values.transform_keys(&:to_sym).merge(options))
end

Instance Method Details

#process_grants(workspace) ⇒ Object

Builds internal identity grants for a concrete Workspace.



54
55
56
57
58
59
60
61
62
63
64
# File 'lib/little_ghost/sandbox/policy.rb', line 54

def process_grants(workspace) # :nodoc:
  file_mounts = files.map do |name, access|
    source = workspace_directory(workspace, name)
    Mount.new(source:, target: source, access:, protect_aliases: true)
  end
  process_mounts = runtime_paths.map do |name, access|
    source = workspace_directory(workspace, name)
    Mount.new(source:, target: source, access:, protect_aliases: true, tools: false)
  end
  (file_mounts + process_mounts).sort_by { |mount| -mount.target.length }.freeze
end

#workspace_writable?Boolean

Whether the :root entry in files requests :read_write access.

Returns:

  • (Boolean)


51
# File 'lib/little_ghost/sandbox/policy.rb', line 51

def workspace_writable? = files.fetch(:root, :read_only) == :read_write