1.6.1: execute_ruby process-execution hardening
Security patch for the execute_ruby tool. Closes a command-execution path reported by a security researcher, tightens the static filter, and drops the misleading "read-only sandbox" framing.
Security
execute_rubyprocess-execution hardening — closed a command-execution path and tightened the static filter:requirerestricted to a tiny data-lib allowlist:require_relativeand dynamicrequires are refused, and literalrequire "lib"is refused except for a small allowlist of pure-data libraries not always preloaded (csv,tzinfo,date,time). Underbin/rails runnerthe app's models, ActiveRecord, and the stdlib Rails loads on boot are already available, so inspection code needs almost no requires — and every dangerous stdlib escape has to be required first. This closesrequire "pty"(PTY.spawn/PTY.getptystarted a child process outside theKernel#systemguard, giving arbitrary host command execution), along withopen3,fiddle,ffi, andsocket, at the source rather than by enumerating individual APIs. (tzinfopairs with the existing system-timezone read-path allowlist soTime.zonecode keeps working.)- Native/PTY patterns:
PTY,Fiddle, andFFIare added to the forbidden-pattern scan as defense in depth. - Dynamic dispatch to execution sinks hard-blocked:
send/public_send/__send__/const_getaimed by name at an execution or eval sink (system,exec,spawn,fork,eval,popen,Open3,Process,PTY, …) are now rejected outright instead of merely gated behindconfirm_risky. Benign dynamic dispatch (e.g.record.send(:name)) is unaffected. - Honest framing: the tool description and docs no longer call
execute_rubya read-only sandbox. It runs caller-supplied Ruby with the privileges of the server process; the controls are best-effort guardrails, not an isolation boundary.execute_rubyis operator-started, usually local, against the operator's own project — so the practical risk is unintended execution (e.g. a prompt-injected coding agent), which runs with the operator's privileges. For a real boundary, run the server against a read-only database user and/or inside OS-level isolation.
Fixed
- ReDoS in the
execute_rubystatic scan: Rewrote therequire/dynamic-dispatch matchers to remove an ambiguous\s*\(?\s*construct that backtracked in polynomial time on adversarial whitespace input (CodeQL alert). Matching is now linear.
Upgrade notes
Behavior change for execute_ruby callers: snippets that require a non-allowlisted library, use require_relative, or reach an execution sink via send/const_get are now rejected. Normal inspection code (model queries, read_file/list_files, Time.zone, CSV) is unaffected — under bin/rails runner the environment it needs is already loaded.
Full changelog: https://github.com/maquina-app/rails-mcp-server/blob/v1.6.1/CHANGELOG.md