Module: LanguageOperator::Dsl::Shell
- Defined in:
- lib/language_operator/dsl/shell.rb
Overview
Safe shell command execution for MCP tools
Provides methods for executing shell commands safely with automatic argument escaping to prevent injection attacks. All methods are class methods.
Class Method Summary collapse
-
.build(cmd, *args) ⇒ Object
Safely build a command string with escaped arguments Useful when you need to construct a command but not execute it yet.
-
.capture(cmd) ⇒ Object
Run a command and return only stdout (like backticks) Returns nil if the command fails.
-
.capture!(cmd) ⇒ Object
Run a command and return stdout, raising on failure.
-
.command_exists?(cmd) ⇒ Boolean
Check if a command exists in PATH.
-
.escape(arg) ⇒ Object
Escape a single argument for shell usage.
-
.raw(_command, env: {}, chdir: nil, timeout: 30) ⇒ Object
deprecated
Deprecated.
This method has been removed for security reasons
-
.run(cmd, *args, env: {}, chdir: nil, timeout: 30) ⇒ Hash
Run a shell command with properly escaped arguments.
-
.spawn(_cmd, *_args, env: {}, chdir: nil) ⇒ Object
deprecated
Deprecated.
This method has been removed for security reasons
Class Method Details
.build(cmd, *args) ⇒ Object
Safely build a command string with escaped arguments Useful when you need to construct a command but not execute it yet
114 115 116 117 |
# File 'lib/language_operator/dsl/shell.rb', line 114 def self.build(cmd, *args) escaped_args = args.map { |arg| Shellwords.escape(arg.to_s) } "#{cmd} #{escaped_args.join(' ')}" end |
.capture(cmd) ⇒ Object
Run a command and return only stdout (like backticks) Returns nil if the command fails
77 78 79 80 |
# File 'lib/language_operator/dsl/shell.rb', line 77 def self.capture(cmd, *, **) result = run(cmd, *, **) result[:success] ? result[:output] : nil end |
.capture!(cmd) ⇒ Object
Run a command and return stdout, raising on failure
83 84 85 86 87 88 |
# File 'lib/language_operator/dsl/shell.rb', line 83 def self.capture!(cmd, *, **) result = run(cmd, *, **) raise "Command failed (exit #{result[:exitcode]}): #{result[:error]}" unless result[:success] result[:output] end |
.command_exists?(cmd) ⇒ Boolean
Check if a command exists in PATH
91 92 93 94 |
# File 'lib/language_operator/dsl/shell.rb', line 91 def self.command_exists?(cmd) result = run('which', cmd) result[:success] end |
.escape(arg) ⇒ Object
Escape a single argument for shell usage
120 121 122 |
# File 'lib/language_operator/dsl/shell.rb', line 120 def self.escape(arg) Shellwords.escape(arg.to_s) end |
.raw(_command, env: {}, chdir: nil, timeout: 30) ⇒ Object
This method has been removed for security reasons
Execute raw shell command (REMOVED FOR SECURITY) This method has been removed as it allowed arbitrary shell execution with pipes, redirects, etc. which is a security risk in synthesized code.
108 109 110 |
# File 'lib/language_operator/dsl/shell.rb', line 108 def self.raw(_command, env: {}, chdir: nil, timeout: 30) raise SecurityError, 'Shell.raw has been removed for security reasons. Use Shell.run with explicit arguments instead.' end |
.run(cmd, *args, env: {}, chdir: nil, timeout: 30) ⇒ Hash
Run a shell command with properly escaped arguments
This is safer than using backticks as it prevents shell injection. Arguments are automatically escaped using Shellwords.
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/language_operator/dsl/shell.rb', line 35 def self.run(cmd, *args, env: {}, chdir: nil, timeout: 30) # Escape all arguments escaped_args = args.map { |arg| Shellwords.escape(arg.to_s) } full_cmd = "#{cmd} #{escaped_args.join(' ')}" # Execute with timeout stdout = nil stderr = nil status = nil begin Timeout.timeout(timeout) do stdout, stderr, status = Open3.capture3(env, full_cmd, chdir: chdir) end rescue Timeout::Error return { success: false, output: '', error: "Command timed out after #{timeout} seconds", exitcode: -1, timeout: true } rescue StandardError => e return { success: false, output: '', error: e., exitcode: -1 } end { success: status.success?, output: stdout, error: stderr, exitcode: status.exitstatus, timeout: false } end |
.spawn(_cmd, *_args, env: {}, chdir: nil) ⇒ Object
This method has been removed for security reasons
Run a command in the background and return immediately
99 100 101 |
# File 'lib/language_operator/dsl/shell.rb', line 99 def self.spawn(_cmd, *_args, env: {}, chdir: nil) raise SecurityError, 'Shell.spawn has been removed for security reasons. Background process execution is not allowed in synthesized code.' end |