Class: OllamaAgent::Tools::RunShell

Inherits:
Base
  • Object
show all
Defined in:
lib/ollama_agent/tools/shell_tools.rb

Overview

Safe shell command execution tool.

Safety controls:

- allowlist:  commands must match at least one allowlist pattern
- denylist:   commands are rejected if they match any denylist pattern
- dry_run:    if true, returns the command without executing
- timeout:    kills the process after N seconds
- redaction:  scrubs secret-like values from output
- sandbox:    restricts working directory to project root

rubocop:disable Metrics/ClassLength – single tool: policy tables + process I/O loop

Constant Summary collapse

DEFAULT_TIMEOUT =
30
MAX_OUTPUT_BYTES =

64 KB

65_536
DEFAULT_ALLOWLIST =

Default allowlist: common development workflows

[
  /\Agit\s/,
  /\Abundle\s/,
  /\Arspec\b/,
  /\Arubocop\b/,
  /\Aruby\s/,
  /\Aecho\s/,
  /\Aprintf\s/,
  /\Acat\s/,
  /\Als\b/,
  /\Apwd\b/,
  /\Amkdir\s/,
  /\Acp\s/,
  /\Amv\s/,
  /\Awk\s/,
  /\Ased\s/,
  /\Agrep\s/,
  /\Afind\s/,
  /\Ayarn\s/,
  /\Anpm\s/,
  /\Amake\s/
].freeze
DEFAULT_DENYLIST =

Default denylist: dangerous patterns regardless of allowlist

[
  /rm\s+(-[a-z]*r[a-z]*f|-[a-z]*f[a-z]*r|--recursive.*--force|--force.*--recursive)/i,
  /sudo\s/,
  /chmod\s+777/,
  /:\(\)\s*\{.*\}/, # fork bomb
  /curl[^|]*\|[^|]*sh/i,              # curl | bash
  /wget[^|]*\|[^|]*sh/i,              # wget | bash
  %r{>\s*/etc/},                       # write to /etc
  %r{>\s*/usr/},                       # write to /usr
  %r{dd\s+.*of=/dev/}i, # write to devices
  /mkfs\b/,                            # format filesystem
  /passwd\b/,                          # change passwords
  /visudo\b/,                          # edit sudoers
  /crontab\s+-[re]/i                   # modify crontabs
].freeze
SECRET_PATTERNS =

Patterns that look like secrets — redacted from output

[
  /(?:password|passwd|secret|api.?key|token|bearer)\s*[=:]\s*\S+/i,
  /AKIA[0-9A-Z]{16}/, # AWS access key
  /sk-[A-Za-z0-9]{32,}/, # OpenAI key pattern
  %r{eyJ[A-Za-z0-9+/=]{40,}} # JWT-ish
].freeze

Constants inherited from Base

Base::RISK_LEVELS

Instance Attribute Summary

Attributes inherited from Base

#description, #input_schema, #name, #output_schema, #requires_approval, #risk_level

Instance Method Summary collapse

Methods inherited from Base

#to_anthropic_schema, #to_ollama_schema, #to_s, tool_description, tool_name, tool_output_schema, tool_requires_approval, tool_risk, tool_schema

Constructor Details

#initialize(allowlist: nil, denylist: nil, timeout: DEFAULT_TIMEOUT, dry_run: false, redact_secrets: true, **_opts) ⇒ RunShell

rubocop:disable Metrics/ParameterLists – explicit knobs for CLI/embedders



98
99
100
101
102
103
104
105
106
# File 'lib/ollama_agent/tools/shell_tools.rb', line 98

def initialize(allowlist: nil, denylist: nil, timeout: DEFAULT_TIMEOUT,
               dry_run: false, redact_secrets: true, **_opts)
  super()
  @allowlist       = allowlist      || DEFAULT_ALLOWLIST
  @denylist        = denylist       || DEFAULT_DENYLIST
  @timeout         = timeout
  @dry_run         = dry_run
  @redact_secrets  = redact_secrets
end

Instance Method Details

#call(args, context: {}) ⇒ Object

rubocop:enable Metrics/ParameterLists



109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ollama_agent/tools/shell_tools.rb', line 109

def call(args, context: {})
  cmd = args["command"].to_s.strip
  return "Error: empty command" if cmd.empty?

  cwd     = resolve_cwd(args["working_dir"], context[:root])
  timeout = [args["timeout_seconds"]&.to_i || @timeout, 120].min

  check_allowlist!(cmd)
  check_denylist!(cmd)

  return { dry_run: true, command: cmd, cwd: cwd } if @dry_run || context[:read_only]

  run_command(cmd, cwd: cwd, timeout: timeout)
end