Class: LanguageOperator::Agent::Safety::SafeExecutor
- Inherits:
-
Object
- Object
- LanguageOperator::Agent::Safety::SafeExecutor
- Defined in:
- lib/language_operator/agent/safety/safe_executor.rb
Overview
Executes Ruby code in a sandboxed context with method whitelisting Wraps the execution context to prevent dangerous method calls at runtime
Defined Under Namespace
Classes: SandboxProxy, SecurityError
Constant Summary collapse
- ALWAYS_SAFE_METHODS =
Methods that are always safe to call
%i[ nil? == != eql? equal? hash object_id class is_a? kind_of? instance_of? respond_to? send_to? methods public_methods private_methods instance_variables instance_variable_get instance_variable_set to_s to_str inspect to_a to_h to_i to_f to_sym freeze frozen? dup clone ].freeze
Instance Attribute Summary collapse
-
#audit_log ⇒ Object
readonly
Get audit log.
Instance Method Summary collapse
-
#eval(code, file_path = '(eval)') ⇒ Object
Execute code in the sandboxed context.
-
#initialize(context, validator: nil) ⇒ SafeExecutor
constructor
A new instance of SafeExecutor.
-
#log_call(receiver, method_name, args) ⇒ Object
Log method calls for auditing.
Constructor Details
#initialize(context, validator: nil) ⇒ SafeExecutor
Returns a new instance of SafeExecutor.
20 21 22 23 24 |
# File 'lib/language_operator/agent/safety/safe_executor.rb', line 20 def initialize(context, validator: nil) @context = context @validator = validator || ASTValidator.new @audit_log = [] end |
Instance Attribute Details
#audit_log ⇒ Object (readonly)
Get audit log
74 75 76 |
# File 'lib/language_operator/agent/safety/safe_executor.rb', line 74 def audit_log @audit_log end |
Instance Method Details
#eval(code, file_path = '(eval)') ⇒ Object
Execute code in the sandboxed context
30 31 32 33 34 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 |
# File 'lib/language_operator/agent/safety/safe_executor.rb', line 30 def eval(code, file_path = '(eval)') # Step 1: Validate code with AST analysis @validator.validate!(code, file_path) # Step 2: Execute in sandboxed context sandbox = SandboxProxy.new(@context, self) # Step 3: Execute using instance_eval with smart constant injection # Only inject constants that won't conflict with user-defined ones safe_constants = %w[Numeric Integer Float String Array Hash TrueClass FalseClass Time Date ArgumentError TypeError RuntimeError StandardError] # Find which constants user code defines to avoid redefinition warnings user_defined_constants = safe_constants.select { |const| code.include?("#{const} =") } # Only inject constants that user code doesn't define constants_to_inject = safe_constants - user_defined_constants if constants_to_inject.any? # Inject only safe constants that won't conflict safe_setup = constants_to_inject.map { |name| "#{name} = ::#{name}" }.join("\n") # rubocop:disable Style/DocumentDynamicEvalDefinition sandbox.instance_eval("#{safe_setup}\n#{code}", __FILE__, __LINE__) # rubocop:enable Style/DocumentDynamicEvalDefinition else # User defines all constants, just run their code sandbox.instance_eval(code, file_path, 1) end rescue ASTValidator::SecurityError => e # Re-raise validation errors as executor errors for clarity raise SecurityError, "Code validation failed: #{e.}" end |
#log_call(receiver, method_name, args) ⇒ Object
Log method calls for auditing
64 65 66 67 68 69 70 71 |
# File 'lib/language_operator/agent/safety/safe_executor.rb', line 64 def log_call(receiver, method_name, args) @audit_log << { timestamp: Time.now, receiver: receiver.class.name, method: method_name, args: args.map(&:class).map(&:name) } end |