josephrw's picture
Deploy SystemLake Underwriter telemetry app
5b0eab6 verified
Raw
History Blame Contribute Delete
11.2 kB
"""RECEPT Safety Bridge — Executes RECEPT programs through TerminalSafetyBroker.
RECEPT's run() and rm() calls are routed through the TerminalSafetyBroker
instead of raw subprocess. This means:
- Destructive commands are blocked without @approved
- All commands are logged
- Violations are tracked
- No arbitrary shell execution
"""
import os
import re
import subprocess
import hashlib
import json
import uuid
from datetime import datetime
from typing import Dict, List, Optional, Any
class TerminalSafetyError(Exception):
pass
class SafeExecutionBroker:
"""Safety broker for RECEPT execution.
Checks every command against blocked patterns before execution.
Tracks violations and logs all commands.
This replaces raw subprocess.run() in the RECEPT interpreter.
"""
BLOCKED_PATTERNS = [
r'\brm\s+-rf?\b',
r'\brm\s+-r\b',
r'\bdd\b',
r'\bmkfs\b',
r'\bchmod\s+777\b',
r'\bchown\b',
r'\bsudo\b',
r'\bkill\s+-9\b',
r'\bpkill\b',
r'>\s*/dev/',
r'curl\s+.*\|\s*(bash|sh)',
r'wget\s+.*\|\s*(bash|sh)',
r'\bpip\s+install\b', # No arbitrary package installs
r'\bnpm\s+install\b',
r'\bbrew\s+install\b',
]
SAFE_PATTERNS = [
r'^ls\b', r'^cat\b', r'^head\b', r'^tail\b',
r'^grep\b', r'^find\b', r'^echo\b', r'^mkdir\b',
r'^test\b', r'^python3\s+-m\s+pytest\b',
r'^python3\s+-c\b', r'^git\s+(status|log|diff)\b',
r'^which\b', r'^wc\b', r'^sort\b', r'^uniq\b',
r'^diff\b', r'^file\b', r'^stat\b',
r'^screencapture\b', r'^shasum\b',
]
def __init__(self, approved: bool = False):
self.approved = approved
self.violation_count = 0
self.command_log: List[Dict] = []
self.work_dir: Optional[str] = None
def set_work_dir(self, path: str):
self.work_dir = path
def check(self, command: str) -> Dict:
"""Check command against safety rules."""
classification = 'unknown'
for pattern in self.SAFE_PATTERNS:
if re.match(pattern, command):
classification = 'safe'
return {'allowed': True, 'reason': 'auto-approved', 'needs_approval': False}
for pattern in self.BLOCKED_PATTERNS:
if re.search(pattern, command):
classification = 'blocked'
if self.approved:
return {'allowed': True, 'reason': 'pre-approved by @approved',
'needs_approval': False}
return {'allowed': False, 'reason': f'blocked: {pattern}',
'needs_approval': True}
classification = 'unknown'
return {'allowed': True, 'reason': 'unknown, allowed by default',
'needs_approval': False}
def execute(self, command: str, timeout: int = 30) -> Dict:
"""Execute a command safely. Raises TerminalSafetyError if blocked."""
check = self.check(command)
self.command_log.append({
'command': command,
'timestamp': datetime.now().isoformat(),
'classification': check['reason'],
'allowed': check['allowed'],
})
if not check['allowed']:
self.violation_count += 1
raise TerminalSafetyError(
f"Command blocked: {check['reason']}. "
f"Add @approved annotation to allow. Command: {command[:80]}"
)
try:
r = subprocess.run(
command, shell=True,
cwd=self.work_dir,
capture_output=True, text=True, timeout=timeout
)
return {
'returncode': r.returncode,
'stdout': r.stdout,
'stderr': r.stderr,
'blocked': False,
}
except subprocess.TimeoutExpired:
return {
'returncode': -1,
'stdout': '',
'stderr': f'TIMEOUT after {timeout}s',
'blocked': False,
}
def safe_rm(self, path: str) -> Dict:
"""Safely remove a file. Only works if @approved."""
if not self.approved:
raise TerminalSafetyError(
f"rm() requires @approved annotation. Path: {path}"
)
# Prevent path traversal
if '..' in path or path.startswith('/'):
raise TerminalSafetyError(
f"rm() blocked: path traversal or absolute path not allowed: {path}"
)
full_path = os.path.join(self.work_dir or '.', path)
try:
os.remove(full_path)
return {'returncode': 0, 'stdout': f'removed: {path}', 'stderr': '', 'blocked': False}
except Exception as e:
return {'returncode': 1, 'stdout': '', 'stderr': str(e), 'blocked': False}
def audit(self) -> List[Dict]:
return self.command_log
def violation_count(self) -> int:
return self.violation_count
class RECEPTSafeRunner:
"""Runs RECEPT programs with safe execution.
Integrates RECEPT interpreter with:
- SafeExecutionBroker (replaces raw subprocess)
- SQLiteReceiptStore (durable receipts)
- SelfImprovementLedger (false positive tracking)
The terminal is the primary control channel.
Screen vision is observation only.
"""
def __init__(self, receipts_db: str = None, improvement_db: str = None):
from .receipt_store import SQLiteReceiptStore
from .improvement import SelfImprovementLedger
self.receipt_store = SQLiteReceiptStore(receipts_db)
self.improvement_ledger = SelfImprovementLedger(improvement_db)
self.broker: Optional[SafeExecutionBroker] = None
def run(self, source: str, work_dir: str = None) -> Dict:
"""Run a RECEPT program safely.
Args:
source: RECEPT source code
work_dir: Working directory for file operations
Returns:
Execution result with receipts
"""
from membra_gpt.recept.interpreter import Interpreter, ReceptRuntimeError
work_dir = work_dir or os.path.join(os.getcwd(), 'questionos', 'recept_runs')
os.makedirs(work_dir, exist_ok=True)
# Create safety broker
self.broker = SafeExecutionBroker()
self.broker.set_work_dir(work_dir)
# Create a patched interpreter that uses our broker
interp = Interpreter(receipts_dir=os.path.join(work_dir, 'receipts'))
# Monkey-patch the interpreter's call_builtin to use safe execution
original_call_builtin = interp.call_builtin
def safe_call_builtin(name: str, args: List[Any]) -> Any:
if name == 'run':
cmd = args[0] if args else ""
result = self.broker.execute(cmd)
return result.get('stdout', '')[:1000]
elif name == 'write_file':
filename = args[0] if args else ""
content = args[1] if len(args) > 1 else ""
filepath = os.path.join(work_dir, filename)
os.makedirs(os.path.dirname(filepath), exist_ok=True) if os.path.dirname(filename) else None
with open(filepath, 'w') as f:
f.write(content)
return f"wrote {len(content)} bytes to {filename}"
elif name == 'read_file':
filename = args[0] if args else ""
filepath = os.path.join(work_dir, filename)
try:
with open(filepath) as f:
return f.read()
except Exception as e:
return f"read error: {e}"
elif name == 'rm':
path = args[0] if args else ""
result = self.broker.safe_rm(path)
return result.get('stdout', '')
elif name == 'rmdir':
path = args[0] if args else ""
if not self.broker.approved:
raise TerminalSafetyError(
f"rmdir() requires @approved annotation. Path: {path}"
)
try:
os.rmdir(os.path.join(work_dir, path))
return f"removed dir: {path}"
except Exception as e:
return f"rmdir error: {e}"
else:
return original_call_builtin(name, args)
interp.call_builtin = safe_call_builtin
interp.call_function = self._safe_call_function(interp)
# Run the program
try:
result = interp.run(source)
# Write a durable receipt to SQLite
receipt = self.receipt_store.write(
agent='RECEPT',
action='program_executed',
artifact_path=work_dir,
commands_run=[cmd['command'] for cmd in self.broker.command_log],
result='success' if not result.get('errors') else 'partial',
details={
'capsule': result.get('capsule', 'unknown'),
'endpoints': result.get('endpoints', []),
'workflows': result.get('workflows', []),
'functions': result.get('functions', []),
'errors': result.get('errors', []),
'broker_violations': self.broker.violation_count,
'broker_log': self.broker.command_log[:20],
},
)
result['receipt'] = receipt
result['broker_audit'] = self.broker.audit()
result['work_dir'] = work_dir
return result
except TerminalSafetyError as e:
receipt = self.receipt_store.write(
agent='RECEPT',
action='program_blocked',
artifact_path=None,
commands_run=[cmd['command'] for cmd in self.broker.command_log],
result='blocked',
details={'error': str(e), 'violations': self.broker.violation_count},
)
return {
'errors': [str(e)],
'receipt': receipt,
'broker_audit': self.broker.audit(),
'blocked': True,
}
def _safe_call_function(self, interp):
"""Wrap interpreter's call_function to respect @approved."""
original = interp.call_function
def safe_call(name: str, args: List[Any]) -> Any:
if name in interp.functions:
fn = interp.functions[name]
if name in interp.DESTRUCTIVE_FUNCS and not fn.approved:
raise TerminalSafetyError(
f"Function '{name}' is destructive but not @approved."
)
# Set broker approved state
if self.broker:
old_approved = self.broker.approved
self.broker.approved = fn.approved
result = original(name, args)
self.broker.approved = old_approved
return result
return original(name, args)
return safe_call