How OpenHuman Classifies Commands and Gates Permissions for Agent Actions
OpenHuman employs a deterministic, fail-closed security architecture that parses every shell command into classified segments, assesses risk levels, and applies tier-based permission gates to prevent unauthorized agent actions.
In the OpenHuman framework—available at tinyhumansai/openhuman—agent autonomy is controlled through a sophisticated policy engine written in Rust. This system treats every shell command as a classified operation, applying structural guards and risk assessments before execution to ensure agents operate within defined safety boundaries.
The Three-Layer Security Model
The permission system operates through three distinct phases defined in the core security policy modules. According to the OpenHuman source code, each command passes through parsing, risk analysis, and gating logic before reaching the execution layer.
| Phase | Core Function | Source File |
|---|---|---|
| Classification | Tokenizes commands, normalizes executables, maps to CommandClass |
src/openhuman/security/policy/policy_command.rs |
| Risk Assessment | Evaluates CommandRiskLevel against catastrophic and medium-risk patterns |
src/openhuman/security/policy/command_checks.rs |
| Permission Gating | Applies AutonomyLevel constraints to produce GateDecision outcomes |
src/openhuman/security/policy/command_checks.rs |
Command Parsing and Classification
The classification pipeline begins in policy_command.rs, where the classify_command function transforms raw shell strings into structured, categorized operations. This process involves several deterministic steps that sanitize and analyze command structure.
Segment Parsing and Normalization
The split_unquoted_segments function tokenizes command lines on unquoted delimiters—including ;, |, &&, ||, and newlines—while respecting single and double quotes. This prevents injection attacks that rely on delimiter collision.
Environment variable assignments are stripped via skip_env_assignments, which removes leading NAME=value fragments. The system treats any leading assignment as suspicious through has_leading_env_assignment and specifically flags dangerous prefixes via has_dangerous_env_prefix.
Executable names undergo normalization through normalized_command_name, which lower-cases the basename and strips trailing .exe extensions to ensure consistent classification across platforms.
Command Class Mapping
The classify_segment function in policy_command.rs maps normalized executables to CommandClass variants using hard-coded allow-lists:
READ_ONLY_BASES→CommandClass::Read(safe inspection tools)NETWORK_BASES→CommandClass::Network(connectivity utilities)DESTRUCTIVE_BASES→CommandClass::Destructive(disk formatting, system shutdown)- Install detection via
is_install_command→CommandClass::Install(package managers) - Interpreter detection via
is_command_executor→CommandClass::Write(shells, scripting languages)
When no pattern matches, the system defaults to CommandClass::Write, implementing a fail-closed posture that assumes potential side effects.
Structural Safety Guards
Before classification completes, has_hidden_execution checks for dangerous shell constructs including backticks, $(...) or <(...) expansions, and unquoted background operators (&). These structural guards prevent command obfuscation techniques that could bypass policy enforcement.
Risk Assessment and Threat Detection
Once classified, commands undergo risk scoring in command_checks.rs through the command_risk_level method. This evaluation determines whether a command represents a catastrophic threat, moderate risk, or safe operation.
High-Risk Pattern Detection
The system flags commands as High risk when they match catastrophic executable names or contain destructive substrings. This includes system-level utilities like mkfs, dd, shutdown, and sudo, as well as known dangerous patterns such as rm -rf / or fork bombs like :(){:|:&};:.
Medium-Risk Classification
State-changing but non-catastrophic operations receive Medium risk ratings. The command_risk_level implementation identifies these through patterns including:
- Version control mutations (
git push) - Package installation (
npm install) - File-system modification utilities (
chmod,chown) - Network data transfer tools (
curl,wget)
All other commands default to Low risk, allowing efficient fast-path processing for read-only operations.
Permission Gating and Autonomy Tiers
The final security layer applies user-configured autonomy constraints through the gate_decision method. OpenHuman defines three AutonomyLevel variants that determine the gating behavior: ReadOnly, Supervised, and Full.
The Gate Decision Matrix
The gate_decision function implements the following permission matrix:
| Autonomy Tier | Allowed Classes | Prompted Classes | Blocked Classes |
|---|---|---|---|
| ReadOnly | Read |
— | Write, Network, Install, Destructive |
| Supervised | Read |
Write, Network, Install, Destructive |
— |
| Full | Read, Write |
Network, Install, Destructive |
— |
In ReadOnly mode, agents can only execute read-classified commands, with all modification attempts blocked immediately. Supervised mode requires explicit user approval for any non-read operation, while Full mode permits write operations automatically but still prompts on network, installation, and destructive classes.
Validation and Enforcement
The check_gated_command method enforces structural constraints for non-Full tiers, invoking has_hidden_execution to prevent subshell escapes. For supervised and read-only modes, is_command_allowed implements strict allow-list validation that rejects unknown executables, dangerous arguments (such as find -exec or git config mutations), unquoted redirections (>), and background operators.
The validate_command_execution function serves as the final checkpoint, respecting configuration flags like require_approval_for_medium_risk and block_high_risk_commands to determine whether execution proceeds or returns a POLICY_BLOCKED_MARKER error.
Practical Implementation Examples
Below are concrete examples demonstrating the classification and gating API as implemented in the OpenHuman crate:
use openhuman::security::policy::{SecurityPolicy, AutonomyLevel};
// Initialize a supervised security policy
let mut policy = SecurityPolicy::default();
policy.autonomy = AutonomyLevel::Supervised;
// Classify a version control command
let cmd = "git push origin main";
let class = policy.classify_command(cmd);
// Returns: CommandClass::Write
// Evaluate gating decision
let decision = policy.gate_decision(class);
// Returns: GateDecision::Prompt (requires user approval in Supervised mode)
The following example demonstrates risk evaluation for system-level commands:
// Assess catastrophic command risk
let dangerous_cmd = "mkfs.ext4 /dev/sda1";
let risk = policy.command_risk_level(dangerous_cmd);
// Returns: CommandRiskLevel::High
// Validation respects block_high_risk_commands default
match policy.validate_command_execution(dangerous_cmd, false) {
Ok(level) => println!("Approved with risk: {:?}", level),
Err(e) => println!("Policy rejection: {}", e),
// Typically rejects unless explicit approval provided
}
Structural guards prevent hidden execution attempts regardless of classification:
// Detects subshell injection
let obfuscated = "echo $(rm -rf /)";
assert!(has_hidden_execution(obfuscated)); // Returns true, triggering block
Summary
OpenHuman's command classification and permission gating system provides deterministic safety through:
- Deterministic classification via
classify_segmentand normalized executable mapping inpolicy_command.rs - Tiered risk assessment categorizing operations as
Low,Medium, orHighthreat levels - Autonomy-based gating using
gate_decisionto enforceReadOnly,Supervised, orFullpermission models - Structural guards that detect hidden subshells, background operators, and environment variable injection
- Fail-closed defaults that treat unrecognized commands as
Writeclass and block high-risk operations unless explicitly approved
Frequently Asked Questions
How does OpenHuman prevent command injection attacks?
The framework prevents injection through multiple layers. The split_unquoted_segments function respects shell quoting rules to prevent delimiter-based injection, while has_hidden_execution detects subshell syntax like $(...) and backticks. Additionally, is_command_allowed rejects dangerous argument patterns such as find -exec and unquoted redirections, ensuring agents cannot bypass classification through command obfuscation.
What is the difference between Supervised and Full autonomy modes?
In Supervised mode, agents can execute Read class commands automatically, but Write, Network, Install, and Destructive classes trigger user prompts. The system also enforces structural guards and allow-list validation. Full mode permits Read and Write commands automatically, only prompting on Network, Install, and Destructive operations, and skips structural guards like hidden execution detection, trusting the agent to run any command safely.
Can I customize which commands are considered high-risk?
While the core DESTRUCTIVE_BASES and high-risk pattern tables are defined in the source code, the SecurityPolicy struct exposes configuration flags such as block_high_risk_commands and require_approval_for_medium_risk. These allow runtime adjustment of risk tolerance without modifying the underlying classification lists. Custom command classes would require modification of the classify_segment logic in policy_command.rs.
What happens when a command violates the security policy?
When validation fails at any layer—whether through gate_decision returning GateDecision::Block, is_command_allowed detecting dangerous arguments, or validate_command_execution identifying high-risk operations—the system returns a POLICY_BLOCKED_MARKER error to the frontend. The command never reaches the execution environment, ensuring that policy violations result in safe failures rather than unauthorized actions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →