How Reasonix Decomposes Bash Commands for Safe Execution: A Three-Layer Permission System
Reasonix protects user sessions and host systems by decomposing every Bash tool call through shell resolution, sandbox-aware command wrapping, and session-data guards before execution.
The DeepSeek-Reasonix project's permission system treats every Bash command as a potential security boundary. Built in Go, the tool implementation in internal/tool/builtin/bash.go orchestrates multiple defensive layers to prevent session corruption, unauthorized privilege escalation, and resource leakage. This article examines exactly how the source code decomposes and sanitizes Bash commands before they reach the operating system.
Shell Resolution and Path Hygiene
Before any command executes, Reasonix establishes a clean execution environment that mirrors the user's interactive shell without inheriting risky configurations.
Lazy Interpreter Resolution
The resolved() method in bash.go#L26-L36 lazily determines which shell interpreter to invoke—bash, pwsh, or others—based on the runtime environment and explicit tool parameters. This resolution happens once per tool instantiation and caches the result for subsequent calls.
Controlled PATH Construction
The bashCommandEnv function at bash.go#L72-L84 probes the login shell via defaultBashShellPATH to extract the user's natural PATH environment. It merges this with the current process PATH while filtering out privileged or unexpected entries. The resulting environment ensures commands execute in a predictable context without ambient privilege leakage from the parent process.
// Simplified illustration of PATH construction logic
func bashCommandEnv() []string {
loginPath := defaultBashShellPATH() // Probe interactive shell
merged := mergeCleanPath(loginPath, os.Getenv("PATH"))
return []string{"PATH=" + merged}
}
Sandbox-Aware Command Wrapping
Once the environment is prepared, Reasonix evaluates whether the command requires OS-level confinement through its sandbox subsystem.
The Sandbox Decision Flow
The bashSandboxCommand function in internal/sandbox/sandbox.go#L41-L56 implements a three-way branching logic:
- Sandbox disabled: Execute raw shell command via
argvdirectly. - Sandbox enforcing + command wrapped: Proceed with sandbox-encapsulated execution.
- Sandbox enforcing + command unwrappable: Trigger user approval workflow.
Escape Approval for Unsafe Commands
When sandbox enforcement is active but a command cannot be safely wrapped, approveBashSandboxEscape at bash.go#L48-L60 pauses execution and surfaces a UI prompt. The user must explicitly approve the escape, or Reasonix aborts with a descriptive error. This prevents silent sandbox bypasses for operations like sudo invocations or system-level modifications.
// Escape approval workflow example
params := []byte(`{"command":"sudo systemctl restart docker","allow_escape":false}`)
out, err := builtin.Bash{
sb: sandbox.Spec{Enforce: true},
}.Execute(ctx, params)
// → Returns error: "sandbox escape required but not approved"
Session-Data Guard and Lexical Warning
The final decomposition layer protects Reasonix's own persistent state from accidental corruption by user commands.
Needle-Based Detection
The SessionDataGuard in session_guard.go#L27-L33 constructs a set of normalized path fragments—"needles"—representing sensitive locations:
$REASONIX_ROOT/sessionsprojects/*/sessions- Runtime ledger files
These needles are lower-cased and slash-normalized to catch case-variant and path-traversal attempts.
Warning Injection Without Blocking
The CommandHint scanner examines both the command string and current working directory. When a needle match occurs, appendSessionDataHint at bash.go#L31-L41 appends a warning to the command's stdout:
WARNING: this command referenced Reasonix's own session/state data under /home/user/.reasonix.
Concurrent modifications may cause conflict-copy loops.
Critically, the guard never blocks read operations. It warns specifically about write patterns that could race with Reasonix's autosave mechanisms, preventing infinite conflict-resolution cycles.
// Example: safe command with session-data warning
params := []byte(`{"command":"cat ~/.reasonix/sessions/current/state.json"}`)
out, _ := builtin.Bash{}.Execute(ctx, params)
// → Output: [file contents] + WARNING: this command referenced Reasonix's own session/state data...
// Example: write operation triggers same warning
params := []byte(`{"command":"echo 'corrupt' > ~/.reasonix/sessions/current/state.json"}`)
out, _ := builtin.Bash{}.Execute(ctx, params)
// → Output: (empty) + WARNING: this command referenced Reasonix's own session/state data...
Additional Runtime Safeguards
Beyond the three main layers, Reasonix implements specialized parsers for specific attack patterns:
| Concern | Implementation | Source Location |
|---|---|---|
| Unquoted command chaining | hasUnquotedSeq rejects && and similar operators in PowerShell contexts to prevent injection |
bash.go#L62-L68 |
| Background daemonization | hasExplicitBackgroundKeepalive parses the command AST using shellparse.ParseBash to detect &, nohup, disown, and setsid, enabling proper process group reaping |
bash.go#L40-L48 |
| Foreground timeouts | runForeground applies bashWaitDelay via cancellable context; timeouts surface as command timed out (> 30s) |
bash.go#L82-L92 |
The shell parser in internal/shellparse/parse.go generates a Bash AST, allowing precise detection of backgrounding constructs without false positives from quoted or commented content.
Code Path Walkthrough
A complete command decomposition traces through these function calls:
Bash.Execute()— entry point in [bash.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/tool/builtin/bash.go)resolved()— shell interpreter selectionbashCommandEnv()— PATH sanitizationhasExplicitBackgroundKeepalive()— AST scan for daemonizationbashSandboxCommand()— sandbox wrap decisionapproveBashSandboxEscape()— user approval (if needed)runForeground()— execution with timeoutappendSessionDataHint()— session-data warning injection
Summary
- Shell resolution lazily selects interpreters and builds clean PATH environments via
resolved()andbashCommandEnv(). - Sandbox wrapping conditionally applies OS-level confinement through
bashSandboxCommand(), with mandatory user approval for escapes. - Session-data guards use needle-based lexical scanning to warn about potential state corruption without blocking legitimate reads.
- Specialized parsers detect PowerShell injection patterns and background keep-alive constructs before they create resource leaks.
- Timeout handling ensures foreground commands cannot hang indefinitely through cancellable contexts.
Frequently Asked Questions
How does Reasonix prevent sandbox bypass attacks?
The approveBashSandboxEscape mechanism requires explicit user confirmation when sandbox enforcement is active but a command cannot be wrapped. There is no automatic fallback to unwrapped execution—either the user approves the escape through the UI, or the tool returns an abort error. This design eliminates silent bypass vulnerabilities.
What happens if a command tries to modify Reasonix's session files?
The SessionDataGuard detects references to protected paths and appends a warning to the command output. The operation proceeds—Reasonix does not block the write—but the warning alerts users that concurrent modifications may trigger conflict-copy loops with the application's autosave system.
Why does the background job detector use an AST parser instead of regex?
The shellparse.ParseBash function in internal/shellparse/parse.go generates a proper abstract syntax tree, distinguishing genuine background operators (cmd &) from quoted strings, comments, and shell-escaped characters. Regex approaches would produce false positives on constructs like echo "run &" or echo '# nohup'.
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 →