2025-12-16 · Authensor

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:

Ruby's flexibility is a double-edged sword for agent safety. SafeClaw's 446-test gate engine ensures no action executes without explicit policy approval, with hash-chained audit logging for every decision.

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

Try SafeClaw

Action-level gating for AI agents. Set it up in your browser in 60 seconds.

$ npx @authensor/safeclaw