AI Agent Safety for Ruby Developers
SafeClaw by Authensor provides deny-by-default action gating for Ruby AI agents. Every system(), backtick execution, File.read(), and Net::HTTP.get() call is checked against your YAML policy before it runs. Install with npx @authensor/safeclaw and integrate it into your Ruby agent via the local HTTP API — works with Claude and OpenAI, MIT licensed.
Ruby Agent Attack Surface
Ruby gives AI agents multiple ways to interact with the system:
system(), backticks,%x{},exec(),IO.popen()— all execute shell commandsFile.read(),File.write(),FileUtils.rm_rf()— full filesystem accessNet::HTTP,open-uri,Faraday,HTTParty— outbound network requestsKernel.require— load arbitrary code at runtime
Installation
npx @authensor/safeclaw
Policy
version: 1
defaultAction: deny
rules:
- action: file.read
path:
glob: "/app/data/**"
decision: allow
- action: file.write
path:
glob: "/app/output/**"
decision: allow
- action: process.exec
command:
startsWith: "ruby"
decision: allow
- action: process.exec
command:
startsWith: "bundle"
decision: prompt
- action: network.request
host:
in: ["api.openai.com", "api.anthropic.com"]
decision: allow
- action: file.read
path:
glob: "*/.env"
decision: deny
Ruby Integration
require 'net/http'
require 'json'
require 'uri'
class SafeClawGate
def initialize(endpoint = 'http://localhost:9800')
@endpoint = endpoint
end
def check(action:, path: nil, command: nil, host: nil, url: nil, method: nil)
uri = URI("#{@endpoint}/check")
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
req.body = {
action: action,
path: path,
command: command,
host: host,
url: url,
method: method
}.compact.to_json
begin
response = Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(req) }
JSON.parse(response.body, symbolize_names: true)
rescue StandardError
# Fail closed
{ allowed: false, reason: 'SafeClaw unreachable' }
end
end
end
Using the Gate
gate = SafeClawGate.new
Gate a shell command
def safe_exec(gate, command)
decision = gate.check(action: 'process.exec', command: command)
raise SecurityError, "SafeClaw denied: #{decision[:reason]}" unless decision[:allowed]
#{command}
end
Gate a file read
def safe_read(gate, path)
decision = gate.check(action: 'file.read', path: path)
raise SecurityError, "SafeClaw denied: #{decision[:reason]}" unless decision[:allowed]
File.read(path)
end
Gate a network request
def safe_http_get(gate, url)
uri = URI(url)
decision = gate.check(action: 'network.request', host: uri.host, url: url, method: 'GET')
raise SecurityError, "SafeClaw denied: #{decision[:reason]}" unless decision[:allowed]
Net::HTTP.get(uri)
end
Examples
content = safe_read(gate, '/app/data/input.txt')
output = safe_exec(gate, 'ruby scripts/process.rb')
response = safe_http_get(gate, 'https://api.openai.com/v1/models')
Gating Gem Installation
def safe_gem_install(gate, gem_name)
decision = gate.check(action: 'process.exec', command: "gem install #{gem_name}")
raise SecurityError, "SafeClaw denied: #{decision[:reason]}" unless decision[:allowed]
system("gem install #{gem_name}")
end
With the prompt decision, SafeClaw pauses and requires human approval before allowing package installation. Every decision is hash-chained in the audit trail.
Cross-References
- Ruby on Rails Integration
- Deny-by-Default Explained
- Hash-Chained Audit Logs
- Prevent Agent .env File Access
Try SafeClaw
Action-level gating for AI agents. Set it up in your browser in 60 seconds.
$ npx @authensor/safeclaw