Security
Permission system, HITL confirmation, and extensible security traits
Security
A3S Code provides two built-in security layers: Permission Policy and Human-in-the-Loop (HITL) Confirmation. Additional security logic can be implemented via the SecurityProvider trait and HookEngine.
Security Architecture
User Prompt → Agent Loop
├─ Permission Policy ← Deny → Allow → Ask → Default
├─ HITL Confirmation ← Independent of permissions
├─ Tool Execution
└─ SecurityProvider ← Pluggable via trait (taint tracking, sanitization, etc.)Permission Policy
The permission system controls which tools the agent can use. Rules are evaluated in order: Deny → Allow → Ask → Default.
Rule Types
Prop
Type
Pattern Matching
Rules use glob-style pattern matching on tool names and arguments:
Tool(pattern)Prop
Type
Wildcards: * matches any character except /, ** matches including /, :* matches any suffix.
Preset Policies
Prop
Type
Building a Custom Policy
use a3s_code_core::permissions::PermissionPolicy;
let policy = PermissionPolicy::new()
.deny("bash(rm -rf:*)")
.deny("bash(curl:*|sh)")
.allow("read(*)")
.allow("glob(*)")
.allow("grep(*)")
.ask("write(*)")
.ask("bash(*)");
// Apply to session
session.set_permission_policy(policy).await;const { PermissionPolicy } = require('@a3s-lab/code');
const policy = new PermissionPolicy()
.deny('bash(rm -rf:*)')
.deny('bash(curl:*|sh)')
.allow('read(*)')
.allow('glob(*)')
.allow('grep(*)')
.ask('write(*)')
.ask('bash(*)');
await session.setPermissionPolicy(policy);from a3s_code import PermissionPolicy
policy = PermissionPolicy()
policy.deny("bash(rm -rf:*)")
policy.deny("bash(curl:*|sh)")
policy.allow("read(*)")
policy.allow("glob(*)")
policy.allow("grep(*)")
policy.ask("write(*)")
policy.ask("bash(*)")
session.set_permission_policy(policy)Evaluation Order
Rules are evaluated in this order:
Example:
let policy = PermissionPolicy::new()
.deny("bash(rm -rf:*)") // Block destructive commands
.allow("bash(git *)") // Allow git commands
.ask("bash(*)") // Ask for all other bash commands
.allow("read(*)") // Allow all reads
.ask("write(*)"); // Ask for all writesHuman-in-the-Loop (HITL)
HITL confirmation is independent of the permission system. Even if a tool is allowed by permissions, HITL can still require user confirmation.
Configuration
use a3s_code_core::hitl::{ConfirmationPolicy, TimeoutAction};
let hitl = ConfirmationPolicy {
enabled: true,
default_timeout_ms: 30_000,
timeout_action: TimeoutAction::Reject,
..Default::default()
};
let session = agent.session("/project", Some(
SessionOptions::new().with_confirmation_policy(hitl)
))?;const session = agent.session('/project', {
confirmationPolicy: {
enabled: true,
timeoutMs: 30000,
timeoutAction: 'reject',
},
});from a3s_code import ConfirmationPolicy
session = agent.session("/project",
confirmation_policy=ConfirmationPolicy(
enabled=True,
timeout_ms=30000,
timeout_action="reject",
))Handling Confirmation Events
let (mut rx, _handle) = session.stream("Clean up old logs").await?;
while let Some(event) = rx.recv().await {
match event {
AgentEvent::ConfirmationRequired { tool_id, tool_name, args, .. } => {
println!("🔒 Approve '{tool_name}'? {args:?}");
// Approve or reject
session.respond_confirmation(&tool_id, true, None).await;
}
AgentEvent::PermissionDenied { tool_name, reason, .. } => {
println!("🚫 {tool_name} blocked: {reason}");
}
AgentEvent::TextDelta { text } => print!("{text}"),
AgentEvent::End { .. } => break,
_ => {}
}
}for await (const event of session.stream('Clean up old logs')) {
if (event.type === 'confirmation_required') {
console.log(`🔒 Approve '${event.toolName}'?`, event.toolArgs);
await session.respondConfirmation(event.toolId, { approved: true });
} else if (event.type === 'permission_denied') {
console.log(`🚫 ${event.toolName} blocked: ${event.reason}`);
} else if (event.type === 'text_delta') {
process.stdout.write(event.text);
}
}for event in session.stream("Clean up old logs"):
if event.event_type == "confirmation_required":
print(f"🔒 Approve '{event.tool_name}'? {event.tool_args}")
session.respond_confirmation(event.tool_id, approved=True)
elif event.event_type == "permission_denied":
print(f"🚫 {event.tool_name} blocked: {event.reason}")
elif event.event_type == "text_delta":
print(event.text, end="", flush=True)Timeout Behavior
Prop
Type
Workspace Boundaries
All file operations are restricted to the session's workspace directory. Attempts to access files outside the workspace are blocked.
let session = agent.session("/my-project", None)?;
// ✅ Allowed: within workspace
session.read_file("src/main.rs").await?;
// ❌ Blocked: outside workspace
session.read_file("../other-project/secret.txt").await?;Extensible Security via Traits
A3S Code provides a SecurityProvider trait for implementing custom security logic:
pub trait SecurityProvider: Send + Sync {
/// Classify and register sensitive data found in input text
fn taint_input(&self, text: &str) {}
/// Sanitize output text by redacting sensitive data
fn sanitize_output(&self, text: &str) -> String {
text.to_string()
}
/// Securely wipe all session security state
fn wipe(&self) {}
/// Register security hooks with the given engine
fn register_hooks(&self, hook_engine: &HookEngine) {}
/// Unregister all hooks from the engine
fn teardown(&self, hook_engine: &HookEngine) {}
}Example: Custom Security Provider
use a3s_code_core::security::SecurityProvider;
use a3s_code_core::hooks::{HookEngine, HookEvent, HookResult};
struct MySecurityProvider {
// Your security state
}
impl SecurityProvider for MySecurityProvider {
fn taint_input(&self, text: &str) {
// Detect and track sensitive data (SSN, API keys, etc.)
}
fn sanitize_output(&self, text: &str) -> String {
// Redact sensitive data from LLM output
text.replace("sk-ant-", "[REDACTED]")
}
fn register_hooks(&self, hook_engine: &HookEngine) {
// Register pre/post tool use hooks for security checks
hook_engine.register(/* your hooks */);
}
}
// Use in session
let security = Arc::new(MySecurityProvider { /* ... */ });
let session = agent.session("/project", Some(
SessionOptions::new().with_security_provider(security)
))?;See Hooks for lifecycle event integration.
Best Practices
PermissionPolicy::strict() and explicitly allow only needed toolsbash(rm -rf:*), bash(curl:*|sh)Security Events
The agent emits security-related events during streaming:
Prop
Type
See Sessions for full event reference.
API Reference
PermissionPolicy
Prop
Type
Pattern syntax
Prop
Type
ConfirmationPolicy
Prop
Type
SecurityProvider trait (Rust)
Prop
Type