Module: KairosMcp::Daemon::RestrictedShell::BinaryResolver
- Defined in:
- lib/kairos_mcp/daemon/restricted_shell/binary_resolver.rb
Overview
BinaryResolver — resolves short names to absolute paths at exec time. R2 residual: uses File.realpath for homebrew symlinks + prefix check.
Constant Summary collapse
- ALLOWED_BINS =
{ 'git' => { candidates: %w[/usr/bin/git /opt/homebrew/bin/git /usr/local/bin/git], validator: 'KairosMcp::Daemon::RestrictedShell::GitArgvValidator' }, 'pandoc' => { candidates: %w[/opt/homebrew/bin/pandoc /usr/local/bin/pandoc /usr/bin/pandoc], validator: 'KairosMcp::Daemon::RestrictedShell::PandocArgvValidator' }, 'xelatex' => { candidates: %w[/Library/TeX/texbin/xelatex /usr/bin/xelatex /opt/homebrew/bin/xelatex], validator: 'KairosMcp::Daemon::RestrictedShell::XelatexArgvValidator' } }.freeze
- FORBIDDEN_BINS =
{ 'ruby' => 'interpreter = arbitrary code exec', 'sh' => 'shell expansion', 'bash' => 'shell expansion', 'zsh' => 'shell expansion', 'curl' => 'network exfil — use safe_http_*', 'wget' => 'network exfil', 'ssh' => 'lateral movement', 'scp' => 'exfil', 'rsync' => 'exfil; use safe_file_*' }.freeze
- TRUSTED_PREFIXES =
Trusted prefixes for resolved binary paths.
%w[ /usr/bin/ /usr/local/bin/ /opt/homebrew/bin/ /opt/homebrew/Cellar/ /Library/TeX/texbin/ ].freeze
Class Method Summary collapse
Class Method Details
.resolve!(short_name) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/kairos_mcp/daemon/restricted_shell/binary_resolver.rb', line 42 def self.resolve!(short_name) name = short_name.to_s if FORBIDDEN_BINS.key?(name) raise PolicyViolation, "binary forbidden: #{name} (#{FORBIDDEN_BINS[name]})" end spec = ALLOWED_BINS[name] raise PolicyViolation, "binary not in allowlist: #{name}" unless spec # R2 residual: resolve symlinks via realpath + verify trusted prefix path = spec[:candidates].find do |candidate| next false unless File.exist?(candidate) real = File.realpath(candidate) rescue nil next false unless real File.executable?(real) && File.file?(real) && trusted_path?(real) end raise ResolverError, "no candidate for #{name}: tried #{spec[:candidates]}" unless path real_path = File.realpath(path) { short: name, path: real_path, validator: Object.const_get(spec[:validator]) } end |
.trusted_path?(path) ⇒ Boolean
64 65 66 |
# File 'lib/kairos_mcp/daemon/restricted_shell/binary_resolver.rb', line 64 def self.trusted_path?(path) TRUSTED_PREFIXES.any? { |prefix| path.start_with?(prefix) } end |