Class: Hermetic::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/hermetic/request.rb

Overview

The normalized form of one run call. Backends never see raw kwargs — they see a Request whose invariants already hold: shell-vs-argv resolved, workdir-relative files only, clean default-deny env, integer host-enforced timeout, limits merged over box defaults.

Constant Summary collapse

SHELL =
[ "/bin/sh", "-c" ].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command, files: {}, stdin: nil, env: {}, network: :none, timeout: 30, limits: nil, default_limits: Limits.new, credentials: nil, env_allowlist: [], host_env: ENV) ⇒ Request

Returns a new instance of Request.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/hermetic/request.rb', line 17

def initialize(command, files: {}, stdin: nil, env: {}, network: :none,
               timeout: 30, limits: nil, default_limits: Limits.new,
               credentials: nil, env_allowlist: [], host_env: ENV)
  @command = normalize_command(command)
  @files = normalize_files(files)
  @stdin = stdin&.to_s
  @explicit_env = normalize_env(env)
  @network = NetworkPolicy.from(network)
  @timeout = normalize_timeout(timeout)
  @limits = default_limits.merge(limits)
  @credentials = Credentials.build(credentials)
  @env_allowlist = Array(env_allowlist).map(&:to_s).freeze
  @host_env = host_env
end

Instance Attribute Details

#env_allowlistObject (readonly)

Returns the value of attribute env_allowlist.



13
14
15
# File 'lib/hermetic/request.rb', line 13

def env_allowlist
  @env_allowlist
end

#filesObject (readonly)

Returns the value of attribute files.



13
14
15
# File 'lib/hermetic/request.rb', line 13

def files
  @files
end

#limitsObject (readonly)

Returns the value of attribute limits.



13
14
15
# File 'lib/hermetic/request.rb', line 13

def limits
  @limits
end

#networkObject (readonly)

Returns the value of attribute network.



13
14
15
# File 'lib/hermetic/request.rb', line 13

def network
  @network
end

#stdinObject (readonly)

Returns the value of attribute stdin.



13
14
15
# File 'lib/hermetic/request.rb', line 13

def stdin
  @stdin
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



13
14
15
# File 'lib/hermetic/request.rb', line 13

def timeout
  @timeout
end

Class Method Details

.build(command, **kwargs) ⇒ Object



15
# File 'lib/hermetic/request.rb', line 15

def self.build(command, **kwargs) = new(command, **kwargs)

Instance Method Details

#clean_envObject

The guest environment: host env is default-deny. Precedence (low→high): allowlisted host vars < resolved credentials < explicit env:. Credentials resolve lazily, per call — never cached on the request.



44
45
46
47
48
49
# File 'lib/hermetic/request.rb', line 44

def clean_env
  allowlisted = @env_allowlist.each_with_object({}) do |name, acc|
    acc[name] = @host_env[name].to_s if @host_env[name]
  end
  allowlisted.merge(@credentials.resolve).merge(@explicit_env)
end

#credential_namesObject



51
# File 'lib/hermetic/request.rb', line 51

def credential_names = @credentials.names

#exec_argvObject

Full argv to exec. A String command runs under /bin/sh -c (matches Silas); an Array execs directly — no shell, no injection surface.



36
# File 'lib/hermetic/request.rb', line 36

def exec_argv = shell? ? SHELL + [ @command ] : @command

#exec_stringObject

The command as a single shell string, for hosted APIs that take one.



39
# File 'lib/hermetic/request.rb', line 39

def exec_string = shell? ? @command : Shellwords.join(@command)

#idempotency_keyObject

Deterministic across retries of the same logical run — the executor-side dedup key for the remote in-doubt window (spec §7 risk 2). Credential and host-env VALUES are excluded: short-lived tokens must not change the key (and must never be digested at all).



57
58
59
# File 'lib/hermetic/request.rb', line 57

def idempotency_key
  @idempotency_key ||= Digest::SHA256.hexdigest(JSON.generate(canonical))[0, 32]
end

#inspectObject Also known as: to_s

Values are redacted; keys/command are the model-visible surface anyway.



62
63
64
65
66
# File 'lib/hermetic/request.rb', line 62

def inspect
  "#<Hermetic::Request cmd=#{exec_argv.inspect} files=#{files.keys.inspect} " \
    "env=#{(@explicit_env.keys + credential_names).sort.inspect} " \
    "network=#{@network} timeout=#{@timeout} [values redacted]>"
end

#shell?Boolean

Returns:

  • (Boolean)


32
# File 'lib/hermetic/request.rb', line 32

def shell? = @command.is_a?(String)