This document designs a universal security wrapper that applies Auto Code's security model to the OpenAI provider. The wrapper replicates Claude SDK's multi-layered security (sandbox, permissions, hooks) for OpenAI's function calling API.
Context: OpenAI SDK lacks built-in security features. This wrapper bridges that gap by porting Claude's security model to OpenAI.
# From apps/backend/core/client.py (lines 650-656)
"""
Security layers (defense in depth):
1. Sandbox - OS-level bash command isolation prevents filesystem escape
2. Permissions - File operations restricted to project_dir only
3. Security hooks - Bash commands validated against an allowlist
(see security.py for allowed commands)
4. Tool filtering - Each agent type only sees relevant tools (prevents misuse)
"""Implementation (Claude SDK):
# From core/client.py line 770
security_settings = {
"sandbox": {
"enabled": True,
"autoAllowBashIfSandboxed": True
}
}Purpose: OS-level process isolation for Bash commands
- Prevents filesystem escape via shell exploits
- Runs commands in isolated subprocess
- SDK handles this internally
Feasibility for OpenAI: LOW
- OpenAI SDK has no sandbox concept
- Would require OS-level integration (containers, chroot, namespaces)
- Recommendation: Defer to later phase, focus on higher-layer security first
Implementation (Claude SDK):
# From core/client.py lines 771-819
security_settings = {
"permissions": {
"defaultMode": "acceptEdits",
"allow": [
# Allow all file operations within the project directory
"Read(./**)",
"Write(./**)",
"Edit(./**)",
"Glob(./**)",
"Grep(./**)",
# Also allow absolute paths
f"Read({project_path_str}/**)",
f"Write({project_path_str}/**)",
# Bash permission granted here, but actual commands validated by bash_security_hook
"Bash(*)",
# Allow web tools
"WebFetch(*)",
"WebSearch(*)",
# Allow MCP tools
*[f"{tool}(*)" for tool in CONTEXT7_TOOLS],
# ... more MCP tools
]
}
}Purpose: Restrict file operations to allowed paths
- Uses allowlist pattern:
Operation(path_pattern) - Supports relative (
./**) and absolute paths - Wildcard matching for subdirectories
Feasibility for OpenAI: HIGH
- Can implement in wrapper layer
- Intercept file-related function calls
- Validate paths before execution
- Implementation strategy: Pre-validation hook for file operations
Implementation (Claude SDK):
# From core/client.py lines 972-975
"hooks": {
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[bash_security_hook])
]
}Hook Implementation (from security/hooks.py):
async def bash_security_hook(
input_data: dict[str, Any],
tool_use_id: str | None = None,
context: Any | None = None,
) -> dict[str, Any]:
"""
Pre-tool-use hook that validates bash commands using dynamic allowlist.
Returns:
Empty dict to allow, or {"decision": "block", "reason": "..."} to block
"""
# 1. Validate tool_input structure
tool_input = input_data.get("tool_input")
if not isinstance(tool_input, dict):
return {"decision": "block", "reason": "Invalid tool_input"}
# 2. Extract command
command = tool_input.get("command", "")
if not command:
return {}
# 3. Get security profile for project
profile = get_security_profile(Path(cwd))
# 4. Extract all commands from command string
commands = extract_commands(command)
# 5. Check each command against allowlist
for cmd in commands:
is_allowed, reason = is_command_allowed(cmd, profile)
if not is_allowed:
return {"decision": "block", "reason": reason}
# 6. Additional validation for sensitive commands
if cmd in VALIDATORS:
validator = VALIDATORS[cmd]
validation_ok, validation_reason = validator(cmd_segment)
if not validation_ok:
return {"decision": "block", "reason": validation_reason}
return {} # AllowSecurity Profile (from project_analyzer.py):
class SecurityProfile:
"""Dynamic security profile based on detected project stack"""
base_commands: set[str] # Always allowed (ls, cat, cd, etc.)
stack_commands: set[str] # Detected from project (npm, python, git)
custom_commands: set[str] # User-defined allowlist
def get_all_allowed_commands(self) -> set[str]:
"""Union of all command sources"""
return self.base_commands | self.stack_commands | self.custom_commandsCommand Validation Flow:
- Base commands: Core shell utilities (ls, cat, cd, grep, etc.) - always allowed
- Stack commands: Detected from project structure (npm for Node.js, python for Python, etc.)
- Custom commands: User-defined allowlist (
.auto-claude-commands.json) - Sensitive validators: Extra validation for dangerous commands (git commit scans for secrets, rm checks paths, etc.)
Feasibility for OpenAI: HIGH
- Can replicate hook pattern in wrapper
- Reuse existing
bash_security_hookimplementation - Intercept function calls before execution
- Implementation strategy: Wrapper-level validation middleware
Implementation (from agents/tools_pkg/models.py):
AGENT_CONFIGS = {
"coder": {
"allowed_tools": ["Read", "Write", "Edit", "Bash", "Glob", "Grep", ...],
"mcp_servers": ["context7", "auto-claude"]
},
"qa_reviewer": {
"allowed_tools": ["Read", "Bash", ...], # More restrictive
"mcp_servers": ["context7", "electron", "puppeteer"]
},
# ... per-agent configurations
}Purpose: Each agent type only sees relevant tools
- Prevents misuse (e.g., Coder agent shouldn't use Electron MCP tools)
- Reduces context window bloat
- Phase-aware tool configuration
Feasibility for OpenAI: HIGH
- Can implement in wrapper
- Filter function definitions passed to OpenAI
- Reuse existing
AGENT_CONFIGSlogic - Implementation strategy: Function filtering at API call time
┌─────────────────────────────────────────────────────────────────┐
│ Agent Layer │
│ (agents/planner.py, agents/coder.py, agents/qa_reviewer.py) │
└────────────────────────────┬────────────────────────────────────┘
│ calls create_client(provider="openai")
▼
┌─────────────────────────────────────────────────────────────────┐
│ Security Wrapper Layer │
│ (NEW: core/providers/openai/security_wrapper.py) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Pre-Execution Hook (openai_function_guard) │ │
│ │ - Validates function inputs before calling OpenAI │ │
│ │ - Checks file permissions │ │
│ │ - Validates bash commands │ │
│ │ - Enforces tool allowlist │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Post-Execution Hook (openai_result_filter) │ │
│ │ - Validates function outputs │ │
│ │ - Sanitizes file paths │ │
│ │ - Checks for blocked operations │ │
│ └────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ OpenAI SDK │
│ (openai Python package - chat completions API) │
└─────────────────────────────────────────────────────────────────┘
"""
Security Wrapper for OpenAI Provider
====================================
Applies Auto Code's security model to OpenAI's function calling API.
Replicates Claude SDK's security layers (permissions, hooks, filtering).
This wrapper:
1. Intercepts function calls before OpenAI execution
2. Validates inputs against security policies
3. Enforces file permissions
4. Validates bash commands via allowlist
5. Filters tools based on agent type
"""
from pathlib import Path
from typing import Any, Callable
from security import (
SecurityProfile,
bash_security_hook,
get_security_profile,
)
from agents.tools_pkg import get_allowed_tools
class OpenAISecurityWrapper:
"""
Wraps OpenAI client with security validation.
This class provides pre/post execution hooks that replicate
Claude SDK's security model for OpenAI's function calling API.
"""
def __init__(
self,
project_dir: Path,
spec_dir: Path,
agent_type: str = "coder",
):
"""
Initialize security wrapper.
Args:
project_dir: Project root directory (for permissions)
spec_dir: Spec directory (for settings)
agent_type: Agent type for tool filtering
"""
self.project_dir = project_dir.resolve()
self.spec_dir = spec_dir.resolve()
self.agent_type = agent_type
# Get security profile for command allowlisting
self.security_profile = get_security_profile(project_dir)
# Get allowed tools for this agent type
self.allowed_tools = get_allowed_tools(
agent_type,
project_capabilities={}, # From project_analyzer
linear_enabled=False,
mcp_config={},
)
async def validate_function_call(
self,
function_name: str,
function_arguments: dict[str, Any],
) -> tuple[bool, str | None]:
"""
Pre-execution validation for OpenAI function calls.
This replicates Claude SDK's PreToolUse hook behavior.
Args:
function_name: Name of the function being called
function_arguments: Arguments passed to the function
Returns:
(is_allowed, error_message) tuple
- (True, None) if allowed
- (False, reason) if blocked
"""
# 1. Tool filtering - check if function is in allowed list
if function_name not in self.allowed_tools:
return False, f"Tool '{function_name}' not allowed for {self.agent_type} agent"
# 2. File permissions - validate file operations
if function_name in ("Read", "Write", "Edit", "Glob", "Grep"):
is_allowed, reason = self._validate_file_operation(
function_name, function_arguments
)
if not is_allowed:
return False, reason
# 3. Command allowlisting - validate bash commands
if function_name == "Bash":
is_allowed, reason = await self._validate_bash_command(
function_arguments
)
if not is_allowed:
return False, reason
# 4. Web tools - no additional validation needed
# (Claude SDK allows WebFetch/WebSearch without restrictions)
if function_name in ("WebFetch", "WebSearch"):
return True, None
# 5. MCP tools - validate server is allowed
# (MCP tools are prefixed with server name, e.g., "context7_query")
if function_name.startswith(("context7_", "linear_", "graphiti_")):
return True, None # Already filtered by allowed_tools
return True, None
def _validate_file_operation(
self,
operation: str,
arguments: dict[str, Any],
) -> tuple[bool, str | None]:
"""
Validate file operation paths against security policy.
Ensures file paths are within the allowed project directory.
Args:
operation: File operation type (Read, Write, Edit, Glob, Grep)
arguments: Function arguments containing file paths
Returns:
(is_allowed, error_message) tuple
"""
# Extract file path from arguments
# Path may be in 'file_path', 'path', 'pattern', etc.
file_path = arguments.get("file_path") or arguments.get("path")
if not file_path:
return True, None # No path to validate
# Convert to Path object
path_obj = Path(file_path)
# Resolve to absolute path
try:
absolute_path = path_obj.resolve()
except Exception:
return False, f"Invalid file path: {file_path}"
# Check if path is within allowed directories
# Allow: project_dir, spec_dir, and worktree parent directories
allowed_dirs = [self.project_dir, self.spec_dir]
# Check if path starts with any allowed directory
is_allowed = any(
str(absolute_path).startswith(str(allowed_dir.resolve()))
for allowed_dir in allowed_dirs
)
if not is_allowed:
return False, (
f"File access denied: {file_path} is outside allowed directories. "
f"Filesystem access is restricted to the project directory."
)
return True, None
async def _validate_bash_command(
self,
arguments: dict[str, Any],
) -> tuple[bool, str | None]:
"""
Validate bash command against security allowlist.
Reuses the existing bash_security_hook from security.py.
Args:
arguments: Bash function arguments (must contain 'command')
Returns:
(is_allowed, error_message) tuple
"""
command = arguments.get("command", "")
if not command:
return True, None # Empty command, nothing to validate
# Build input_data for bash_security_hook
input_data = {
"tool_name": "Bash",
"tool_input": arguments,
"cwd": str(self.project_dir),
}
# Call existing security hook
result = await bash_security_hook(input_data)
# bash_security_hook returns {} to allow, or {"decision": "block", "reason": "..."}
if result.get("decision") == "block":
return False, result.get("reason", "Command blocked by security policy")
return True, None
def filter_function_definitions(
self,
functions: list[dict[str, Any]],
) -> list[dict[str, Any]]:
"""
Filter function definitions to only include allowed tools.
This replicates Claude SDK's tool filtering at the provider level.
Args:
functions: All available function definitions
Returns:
Filtered list of function definitions
"""
return [
func for func in functions
if func.get("name") in self.allowed_tools
]"""
OpenAI Provider with Security Wrapper
=====================================
Wraps OpenAI SDK with security validation layer.
"""
from openai import AsyncOpenAI
from .security_wrapper import OpenAISecurityWrapper
class OpenAIProvider:
"""
OpenAI provider with security wrapper.
This class wraps the OpenAI SDK to provide:
- Agent session management
- Tool/function calling with security validation
- Message streaming
"""
def __init__(
self,
api_key: str,
project_dir: Path,
spec_dir: Path,
agent_type: str = "coder",
model: str = "gpt-5.2",
):
"""
Initialize OpenAI provider.
Args:
api_key: OpenAI API key
project_dir: Project root directory
spec_dir: Spec directory
agent_type: Agent type for tool filtering
model: Model to use
"""
self.client = AsyncOpenAI(api_key=api_key)
self.model = model
self.project_dir = project_dir
self.spec_dir = spec_dir
# Initialize security wrapper
self.security = OpenAISecurityWrapper(
project_dir=project_dir,
spec_dir=spec_dir,
agent_type=agent_type,
)
async def create_agent_session(
self,
name: str,
starting_message: str,
) -> str:
"""Create an agent session (returns session_id)"""
# OpenAI is stateless, so we manage session state manually
session_id = f"openai_{name}_{int(time.time())}"
# Store session context (messages, history)
return session_id
async def send_message(
self,
session_id: str,
message: str,
) -> AsyncIterator[dict]:
"""
Send message and stream response.
This method:
1. Calls OpenAI API with function definitions
2. When model requests a function call, validates it via security wrapper
3. Executes validated function calls
4. Returns results to model
5. Streams final response
"""
while True:
# Call OpenAI API
response = await self.client.chat.completions.create(
model=self.model,
messages=session_history[session_id],
functions=self._get_function_definitions(),
function_call="auto", # Let model decide when to call functions
)
# Check if model requested a function call
if response.choices[0].finish_reason == "function_call":
function_call = response.choices[0].message.function_call
# SECURITY: Validate function call before execution
is_allowed, error_msg = await self.security.validate_function_call(
function_name=function_call.name,
function_arguments=json.loads(function_call.arguments),
)
if not is_allowed:
# Block the function call and inform model
yield {
"type": "error",
"content": f"Function call blocked: {error_msg}",
}
break
# Execute the validated function call
result = await self._execute_function(
function_call.name,
json.loads(function_call.arguments),
)
# Add function result to conversation
session_history[session_id].append({
"role": "assistant",
"content": None,
"function_call": function_call,
})
session_history[session_id].append({
"role": "function",
"name": function_call.name,
"content": json.dumps(result),
})
# Continue loop to get next response from model
continue
# Stream final response
yield {
"type": "text",
"content": response.choices[0].message.content,
}
break
def _get_function_definitions(self) -> list[dict]:
"""
Get function definitions for OpenAI API.
Filters functions to only include allowed tools.
"""
all_functions = [
# File operations
{"name": "Read", "description": "...", "parameters": {...}},
{"name": "Write", "description": "...", "parameters": {...}},
# Bash
{"name": "Bash", "description": "...", "parameters": {...}},
# Web tools
{"name": "WebSearch", "description": "...", "parameters": {...}},
# MCP tools
{"name": "context7_query", "description": "...", "parameters": {...}},
# ... more tools
]
# SECURITY: Filter to only allowed tools for this agent type
return self.security.filter_function_definitions(all_functions)Purpose: Pre-execution validation for all function calls
Validation flow:
- Tool filtering: Check if function is in
allowed_toolsfor agent type - File permissions: Validate file paths are within
project_dirandspec_dir - Command allowlisting: Validate bash commands via
bash_security_hook() - Return:
(True, None)to allow,(False, reason)to block
Reuses existing components:
bash_security_hook()fromsecurity/hooks.pyget_security_profile()fromsecurity/profile.pyget_allowed_tools()fromagents/tools_pkg/models.py
Purpose: Enforce file permissions
Validation logic:
- Extract file path from function arguments
- Resolve to absolute path
- Check if path starts with allowed directories:
project_dir(main project directory)spec_dir(spec directory)- Original project's
.auto-claude/(for worktree support)
- Block with error message if outside allowed directories
Replica of: Claude SDK's permissions.allow configuration
Purpose: Validate bash commands against allowlist
Validation logic:
- Extract command from arguments
- Build
input_datadict forbash_security_hook() - Call existing
bash_security_hook()function - Parse result (empty dict = allow, block dict = block)
Reuses: bash_security_hook() from security/hooks.py
This is the key integration point - the wrapper bridges OpenAI's function calling to Auto Code's existing security system.
Purpose: Filter tools at API call time
Validation logic:
- Receive all available function definitions
- Filter to only include functions in
allowed_tools - Return filtered list to OpenAI API
Replica of: Claude SDK's allowed_tools parameter in create_client()
The wrapper reuses existing security components:
security/bash_security_hook- Command validation logicsecurity/profile.py- Security profile cachingproject_analyzer.py- Stack detection and allowlist generationagents/tools_pkg/models.py- Agent tool configurations
No modifications needed - the wrapper calls these as-is.
Current code (Claude-only):
def create_client(
project_dir: Path,
spec_dir: Path,
model: str,
agent_type: str = "coder",
...
) -> ClaudeSDKClient:
# Create Claude SDK client with security
return ClaudeSDKClient(options=ClaudeAgentOptions(...))Enhanced code (Multi-provider):
def create_client(
project_dir: Path,
spec_dir: Path,
model: str,
agent_type: str = "coder",
provider: str = "claude", # NEW parameter
...
) -> ClaudeSDKClient | OpenAIProvider:
"""
Create AI client with security.
Args:
provider: "claude" or "openai"
...
"""
if provider == "claude":
# Existing logic - Claude SDK has native security
return ClaudeSDKClient(options=ClaudeAgentOptions(...))
elif provider == "openai":
# NEW - OpenAI provider with security wrapper
from core.providers.openai import OpenAIProvider
return OpenAIProvider(
api_key=os.environ.get("OPENAI_API_KEY"),
project_dir=project_dir,
spec_dir=spec_dir,
agent_type=agent_type,
model=model,
)
else:
raise ValueError(f"Unknown provider: {provider}")Agents continue to use the same interface:
# In agents/coder.py, agents/planner.py, etc.
from core.client import create_client
client = create_client(
project_dir=project_dir,
spec_dir=spec_dir,
model="gpt-5.2", # or "claude-sonnet-4.5-20250929"
agent_type="coder",
provider=os.environ.get("DEFAULT_PROVIDER", "claude"), # NEW
)
# Agent code unchanged - client abstraction handles provider differences
response = await client.create_agent_session(...)| Security Feature | Claude SDK | OpenAI with Wrapper | Status |
|---|---|---|---|
| File Permissions | ✅ Native (permissions.allow) | ✅ Wrapper (_validate_file_operation) | Full parity |
| Command Allowlisting | ✅ Native (PreToolUse hook) | ✅ Wrapper (_validate_bash_command) | Full parity |
| Tool Filtering | ✅ Native (allowed_tools) | ✅ Wrapper (filter_function_definitions) | Full parity |
| Sandbox | ✅ Native (sandbox.enabled) | ❌ Not implemented | Deferred |
| Bash Isolation | ✅ OS-level subprocess isolation | Partial parity | |
| PreToolUse Hooks | ✅ Native (hooks.PreToolUse) | ✅ Wrapper (validate_function_call) | Full parity |
| PostToolUse Hooks | ✅ Native (hooks.PostToolUse) | Implementable |
-
File Permissions - Fully implementable in wrapper
- Path validation logic is straightforward
- Reuses existing project directory detection
- Estimated effort: 1-2 days
-
Command Allowlisting - Fully implementable via wrapper
- Reuses existing
bash_security_hookfunction - No changes to security module needed
- Estimated effort: 1 day
- Reuses existing
-
Tool Filtering - Fully implementable
- Reuses existing
AGENT_CONFIGSlogic - Simple list filtering
- Estimated effort: 0.5 day
- Reuses existing
- Sandbox - Requires OS-level integration
- Would need containerization (Docker, Firecracker)
- Or OS namespaces (Linux only)
- Recommendation: Defer to later phase, document as limitation
- Estimated effort: 1-2 weeks (if implemented)
- Bash Process Isolation - Limited by Python's subprocess module
- Python subprocess provides some isolation but not OS-level sandbox
- Windows: No equivalent to Linux's chroot/namespaces
- Recommendation: Accept as limitation, rely on allowlist for security
- Estimated effort: Not feasible without OS-level changes
Goal: Implement high-feasibility security features
Tasks:
- Create
core/providers/openai/security_wrapper.py - Implement
OpenAISecurityWrapperclass - Implement
_validate_file_operation() - Implement
_validate_bash_command()(reusingbash_security_hook) - Implement
filter_function_definitions() - Create unit tests for validation logic
Deliverable: Security wrapper with file permissions, command allowlisting, and tool filtering
Goal: Integrate wrapper with OpenAI SDK
Tasks:
- Create
core/providers/openai/client.py - Implement
OpenAIProviderclass - Integrate
validate_function_call()into function calling loop - Implement session management (stateless API)
- Create integration tests
Deliverable: Working OpenAI provider with security wrapper
Goal: Multi-provider support in create_client()
Tasks:
- Modify
core/client.pyto supportproviderparameter - Route to Claude or OpenAI based on provider
- Backward compatibility testing (Claude-only workflows)
- Update documentation
Deliverable: Unified create_client() supporting both providers
Goal: OS-level process isolation for OpenAI
Tasks:
- Research sandboxing options (Docker, Firecracker, namespaces)
- Design sandbox architecture
- Implement sandbox wrapper
- Platform-specific handling (Windows, macOS, Linux)
Deliverable: Optional sandbox feature for OpenAI provider
# tests/providers/openai/test_security_wrapper.py
async def test_file_permission_validation():
"""Test file path validation"""
wrapper = OpenAISecurityWrapper(project_dir, spec_dir)
# Should allow: project directory
is_allowed, _ = wrapper._validate_file_operation(
"Read",
{"file_path": "./src/main.py"}
)
assert is_allowed is True
# Should block: outside project directory
is_allowed, reason = wrapper._validate_file_operation(
"Read",
{"file_path": "/etc/passwd"}
)
assert is_allowed is False
assert "outside allowed directories" in reason
async def test_bash_command_validation():
"""Test bash command validation via security hook"""
wrapper = OpenAISecurityWrapper(project_dir, spec_dir)
# Should allow: base command (ls)
is_allowed, _ = await wrapper._validate_bash_command(
{"command": "ls -la"}
)
assert is_allowed is True
# Should block: disallowed command (rm /)
is_allowed, reason = await wrapper._validate_bash_command(
{"command": "rm -rf /"}
)
assert is_allowed is False
async def test_tool_filtering():
"""Test function definition filtering"""
wrapper = OpenAISecurityWrapper(project_dir, spec_dir, agent_type="coder")
all_functions = [
{"name": "Read", ...},
{"name": "Write", ...},
{"name": "Bash", ...},
{"name": "ElectronTakeScreenshot", ...}, # Not allowed for coder
]
filtered = wrapper.filter_function_definitions(all_functions)
assert len(filtered) == 3 # Electron tool filtered out
assert all(f["name"] in wrapper.allowed_tools for f in filtered)# tests/providers/openai/test_openai_provider_integration.py
async def test_openai_provider_blocks_disallowed_commands():
"""Test that OpenAI provider blocks disallowed bash commands"""
provider = OpenAIProvider(
api_key="test-key",
project_dir=test_project_dir,
spec_dir=test_spec_dir,
agent_type="coder",
)
# Simulate agent requesting a disallowed command
session_id = await provider.create_agent_session("test", "Run rm -rf /")
responses = []
async for response in provider.send_message(
session_id,
"Delete everything using rm -rf /"
):
responses.append(response)
# Should receive error, not execute command
assert any("blocked" in r.get("content", "").lower() for r in responses)
async def test_openai_provider_allows_allowed_commands():
"""Test that OpenAI provider allows allowed bash commands"""
provider = OpenAIProvider(...)
session_id = await provider.create_agent_session("test", "List files")
responses = []
async for response in provider.send_message(
session_id,
"List files using ls -la"
):
responses.append(response)
# Should execute command and return results
assert any("total" in r.get("content", "") for r in responses)# tests/security/test_openai_wrapper_security.py
async def test_cannot_escape_project_directory():
"""Test that file operations cannot escape project directory"""
wrapper = OpenAISecurityWrapper(project_dir, spec_dir)
# Try to read file outside project
is_allowed, _ = wrapper._validate_file_operation(
"Read",
{"file_path": "../../../etc/passwd"}
)
assert is_allowed is False
async def test_cannot_execute_disallowed_commands():
"""Test that disallowed commands are blocked"""
wrapper = OpenAISecurityWrapper(project_dir, spec_dir)
# Try to execute disallowed command (assuming stack doesn't include docker)
is_allowed, _ = await wrapper._validate_bash_command(
{"command": "docker run -it alpine sh"}
)
assert is_allowed is False
async def test_agent_type_tool_filtering():
"""Test that agent types only see their allowed tools"""
coder_wrapper = OpenAISecurityWrapper(project_dir, spec_dir, agent_type="coder")
qa_wrapper = OpenAISecurityWrapper(project_dir, spec_dir, agent_type="qa_reviewer")
# Coder should not have Electron tools
assert "electron_take_screenshot" not in coder_wrapper.allowed_tools
# QA reviewer should have Electron tools (if Electron project detected)
# (assuming project has Electron capabilities)
assert "electron_take_screenshot" in qa_wrapper.allowed_tools-
No OS-level Sandbox
- OpenAI wrapper cannot provide OS-level process isolation
- Mitigation: Rely on defense-in-depth (allowlist + permissions + validation)
- Risk: Sophisticated exploits might bypass Python-level validation
-
Stateless API
- OpenAI API is stateless (unlike Claude SDK's agent sessions)
- Mitigation: Manual session management in wrapper
- Risk: More complex session handling, potential for state bugs
-
Function Calling Overhead
- Pre-validation adds latency to every function call
- Mitigation: Cache security profiles, optimize validation logic
- Risk: Slower execution compared to Claude SDK
-
Security Wrapper Bugs
- Bugs in wrapper logic could allow unauthorized operations
- Mitigation: Comprehensive testing, security audit, defense-in-depth
- Severity: HIGH
-
Incomplete Parity
- Wrapper might miss edge cases that Claude SDK handles
- Mitigation: Extensive testing, real-world validation, phased rollout
- Severity: MEDIUM
-
Performance Degradation
- Additional validation layers slow down OpenAI provider
- Mitigation: Profile and optimize, set performance budgets
- Severity: LOW
The security wrapper is complete when:
- ✅ File permissions enforced - All file operations restricted to project directory
- ✅ Command allowlisting working - Bash commands validated against security profile
- ✅ Tool filtering implemented - Agent types only see allowed tools
- ✅ Existing components reused - No changes to
security/module required - ✅ Unit tests passing - All validation logic tested
- ✅ Integration tests passing - OpenAI provider blocks disallowed operations
- ✅ Backward compatibility maintained - Existing Claude-only workflows unaffected
⚠️ Sandbox optional - Documented as limitation (can be added later)
- Review this design with security architect
- Approve implementation phases with stakeholders
- Create implementation tasks in project tracker
- Begin Phase 1 - Core security wrapper implementation
- Security audit before production use
Document Status: Design Complete Next Phase: Implementation (pending approval) Estimated Effort: 2-3 weeks for Phases 1-3 (excluding optional sandbox)