Ruby 4.0.0 has an experimental feature - Ruby Box. It provides in-process isolation for Ruby code execution, by creating a separated spaces within a single Ruby process, preventing code in one box from affecting another or the main application. This article shows a simple use case of the new feature.


Scenario

You are supplying a tool for LLM to execute a piece of Ruby code it generated and return the results of the execution.


The Code

  1. The unsafe version

    def unsafe_call(llm_generated_code)
      eval(llm_generated_code)
    end
    
  2. The Ruby Box version

    def safe_call(llm_generated_code)
      box = Ruby::Box.new
      box.eval(llm_generated_code)
    end
    

Test the Code

  1. Pretend we have a global current_user, like so:

    class User
      attr_accessor :id, :email, :password_hash, :api_key, :role
    
      def initialize(id:, email:, password_hash:, api_key:, role:)
        @id = id
        @email = email
        @password_hash = password_hash
        @api_key = api_key
        @role = role
      end
    end
    
    $current_user = User.new(
      id: 42,
      email: "admin@company.com",
      password_hash: "$2a$12$K8pQXvZ8...",
      api_key: "sk_live_abc123xyz789secretkey",
      role: "admin"
    )
    
  2. Consider the following code provided by LLM:

    llm_malicious_code = <<'RUBY'
      result = 2 + 2
    
      stolen_data = {
        user_id: $current_user.instance_variable_get(:@id),
        email: $current_user.instance_variable_get(:@email),
        password_hash: $current_user.instance_variable_get(:@password_hash),
        api_key: $current_user.instance_variable_get(:@api_key),
        role: $current_user.instance_variable_get(:@role)
      }
    
      stolen_data.each { |k, v| puts "   #{k}: #{v}" }
    
      result
    RUBY
    
  3. Run the test

    puts "unsafe_call"
    unsafe_call(llm_malicious_code)
    
    puts "safe_call"
    safe_call(llm_malicious_code)
    
  4. And we can observe the result - The unsafe version has a credential leaked.

    ruby: warning: Ruby::Box is experimental, and the behavior may change in the future!
    See https://docs.ruby-lang.org/en/4.0/Ruby/Box.html for known issues, etc.
    unsafe_call
       user_id: 42
       email: admin@company.com
       password_hash: $2a$12$K8pQXvZ8...
       api_key: sk_live_abc123xyz789secretkey
       role: admin
    safe_call
       user_id: 
       email: 
       password_hash: 
       api_key: 
       role:
    

Notes

  1. The Ruby box demonstrates a strong potential in many use cases, including potentially a safer LLM function-calling environment. Although additional security measures are still required, such as providing a whitelist to "require" and detecting malicious patterns in code.

  2. Currently, the feature is not recommended to be used in production, as it has several known issues and bugs.