# Bash Guard

Adversarial security review extension for [Pi](https://github.com/earendil-works/pi). It intercepts agent `bash` tool calls, applies deterministic sensitive-command policy checks, and runs parallel security reviews using fast LLM voters before allowing commands that are not trivially safe.

## How it works

```
LLM calls bash tool
       │
       ▼
  Deterministic policy check
       │
       ├─ safe mutation in /tmp or untracked git files ─▶ Allow instantly
       ├─ sensitive/cloud/out-of-CWD/shell expansion ───▶ Ask user
       │
       ▼
  Strict read-only whitelist? ──yes──▶ Allow instantly
       │ no
       ▼
  Unexpired override in active branch/cwd + UI? ──yes──▶ Allow + notify
       │ no
       ▼
  Fire parallel voter model calls
       │
       ▼
  ┌─────────────────────────────────────────────┐
  │ Quorum YES with no excess abstentions → Allow│
  │ Any NO / no quorum / degraded voters → Block†│
  └─────────────────────────────────────────────┘
  † Interactive users can override. Overrides are
    redacted, hashed, scoped to cwd, and expire.
```

## Security behavior

### Deterministic policy guardrails

Before voters or whitelist checks, Bash Guard requires approval for:

- multiple shell commands: raw newlines, `|`, `;`, `&`, backticks
- shell expansion or disclosure features: `$VAR`, `${VAR}`, `$()`, `<(...)`, input redirection, globs/braces in mutation targets
- sensitive commands: `sudo`, `su`, remote script execution, `chmod 777`, broad recursive deletes, `env`/`printenv`/`set`, `jq -n env`
- sensitive paths: `.env`, `.ssh`, `.aws`, `.config/gcloud`, `.kube`, `.gnupg`, `.git`, `.hg`, `.svn`, `.npmrc`, `.netrc`, lockfiles, `terraform.tfvars`, credential/token/secret paths, and key/cert files
- cloud/resource mutation with `aws`, `gcloud`, `gsutil`, `bq`, `kubectl`, `terraform`, and `terragrunt`
- paths outside the current working directory except `/tmp`/`/private/tmp`

Non-interactive policy approval prompts fail closed.

### Read-only whitelist

Simple read-only commands bypass voter review only after deterministic policy checks pass. Disqualifiers include shell separators, raw newlines, subshell/process substitution, redirects, and environment expansion.

Examples of intentionally omitted commands: `cat`, `env`, `printenv`, `git diff`, `git show`, `git branch`, `git tag`, `git remote`, `find`, `fd`, `awk`, and `sort`. Git branch/tag/remote commands accept mutation arguments and require review even for listing. Only bare `git log` is whitelisted; argument-bearing log commands require review because `--output` can write files.

### Voting quorum

- Default: 5 voters, 5-second timeout each.
- Automatic allow requires at least 3 YES votes and no abstentions by default.
- Any NO, too many timeouts/errors, or no quorum routes to the block/override path.
- Voter prompts receive redacted commands and redacted bounded override history.

### Explainer privacy

The default `minimal` explanation mode does **not** send conversation history to another model. `contextual` mode is available for users who explicitly want richer explanations; it sends only redacted user/assistant text and excludes assistant thinking, tool calls, and tool results.

## Commands

| Command | Description |
|---|---|
| `/guard` | Toggle guard on/off |
| `/guard on` | Enable guard |
| `/guard off` | Disable guard |
| `/guard debug` | Toggle debug mode |

## Optional user shell coverage

By default Bash Guard protects agent-initiated `bash` tool calls. Set `PI_BASH_GUARD_USER_BASH_ENABLED=true`, `--guard-user-bash`, or trusted project config `userBashEnabled: true` to also guard human `!` / `!!` shell commands.

## Configuration

Configuration precedence: defaults/env → CLI flags → trusted project config → persisted `/guard` toggles on the **active session branch**. Reload, session replacement/resume/fork, and tree navigation restore branch-local toggles and overrides, never abandoned branches. Overrides recheck their configured TTL before reuse and before inclusion in voter context. Approvals begun before navigation cannot authorize a command on the new branch.

Trusted project config lives at `${CONFIG_DIR_NAME}/bash-guard.json` (usually `.pi/bash-guard.json`) and is read only when `ctx.isProjectTrusted()` is true. Project config can opt into enabling the guard but cannot set `guardEnabled: false`; disabling remains user/env/CLI authority.

Example:

```json
{
  "guardEnabled": true,
  "policyProfile": "balanced",
  "approvalTimeoutMs": 30000,
  "explainMode": "minimal",
  "userBashEnabled": false,
  "minYesVotes": 3,
  "maxAbstentionsForAutoAllow": 0,
  "overrideTtlMs": 86400000,
  "overrideHistoryLimit": 20
}
```

### Environment variables / flags

| Env var / flag | Default | Description |
|---|---:|---|
| `PI_BASH_GUARD_ENABLED` / `--guard-enabled` | `false` | Initial guard state |
| `PI_BASH_GUARD_DEBUG` / `--guard-debug` | `false` | Initial debug state |
| `PI_BASH_GUARD_POLICY_PROFILE` / `--guard-policy-profile` | `balanced` | `strict`, `balanced`, or `permissive` |
| `PI_BASH_GUARD_VOTES_PER_MODEL` | `5` | Votes per voter model |
| `PI_BASH_GUARD_VOTE_TIMEOUT_MS` | `5000` | Per-voter timeout |
| `PI_BASH_GUARD_MIN_YES_VOTES` | `3` | Minimum YES quorum for auto-allow |
| `PI_BASH_GUARD_MAX_ABSTENTIONS_FOR_AUTO_ALLOW` | `0` | Allowed abstentions for auto-allow |
| `PI_BASH_GUARD_APPROVAL_TIMEOUT_MS` / `--guard-approval-timeout-ms` | `0` | Approval timeout; timeout means deny |
| `PI_BASH_GUARD_EXPLAIN_MODE` / `--guard-explain-mode` | `minimal` | `off`, `minimal`, or `contextual` |
| `PI_BASH_GUARD_USER_BASH_ENABLED` / `--guard-user-bash` | `false` | Guard user `!` / `!!` commands |
| `PI_BASH_GUARD_OFF_STATE_TTL_MS` | `3600000` | How long `/guard off` persists; `0` disables expiry |
| `PI_BASH_GUARD_OVERRIDE_TTL_MS` | `86400000` | Override expiry; `0` disables expiry |
| `PI_BASH_GUARD_OVERRIDE_HISTORY_LIMIT` | `20` | Max override entries sent/restored |
| `PI_BASH_GUARD_VOTER_PROVIDER` / `--guard-voter-provider` | `openai-codex` | Voter provider |
| `PI_BASH_GUARD_VOTER_MODEL` / `--guard-voter-model` | `gpt-5.4-mini` | Voter model |
| `PI_BASH_GUARD_EXPLAINER_PROVIDER` | `anthropic` | Contextual explainer provider |
| `PI_BASH_GUARD_EXPLAINER_MODEL` | `claude-haiku-4-5` | Contextual explainer model |

Models are resolved through Pi's model registry so proxy configurations and custom API keys are respected. If the configured voter is unavailable, Bash Guard can fall back to session-scoped models.

## Status bar and audit trail

- `🔒 guard` — active
- `🔒 guard 🔍` — active with debug
- `🔓 guard off` — disabled; persisted off-state expires by default

User overrides are shown as warnings and persisted as custom entries with a TUI renderer. Stored overrides contain a command hash plus redacted command text; raw commands are not persisted by current versions.

## Security notes

- Bash Guard is **not exposed as an LLM-callable tool**; only user slash commands can toggle it.
- Non-interactive mode blocks policy prompts and non-quorum voting results.
- Exact override repeats are skipped only in UI sessions, on the active branch, and only for the same cwd before expiry.
- Already-aborted operations start no review or approval prompt. Parent cancellation or TUI disposal denies the command and aborts pending review work; late responses cannot record overrides.
- TUI uses native custom components; RPC uses built-in confirmations, not custom terminal UI. Print/JSON modes fail closed when user approval is needed.

### Trusted extensions and execution limits

Bash Guard is a review assistant, **not a sandbox or an execution security boundary**. It checks the named `bash` tool input as it reaches its registered hook. Pi allows later `tool_call` handlers to mutate that input without revalidation. A later extension can therefore rewrite an already-approved command. Load trusted argument-transforming handlers before Bash Guard when possible, but load order alone is not a security boundary: replaced bash tools, spawn hooks, and direct extension subprocesses can change or bypass effective execution. Only install extensions you trust.

Coverage does not include PowerShell, other tools, or arbitrary extension subprocesses. For actual containment, use OS-level/container/VM isolation and limit exposed credentials, files, and network access. Project trust controls resource loading, not command confinement. These compatibility changes do not redesign execution or provide universal interception.
