aaif-goose / aaif-goose/goose

Fine-grained shell permissions: regex-based allow/ask/deny rules for commands under the developer extension's shell tool

Abierto
#11,399 7 comentarios 1 reacción 1 asignado Reclamado por @jbg Ver en GitHub
Lenguaje dominante
Rust
Estrellas
54.2k
Forks
6.2k
Merge medio
3 d 2 h
PR fusionados (30 d)
262

Descripción

**What problem would this solve?**

goose permissions currently apply to tools as a whole. The developer extension's shell tool can only be set to *Always Allow*, *Ask Before*, or *Never Allow*:

- *Ask Before* on the shell tool: prompts for **every** command, including trivially safe ones like `git status`, `ls`, `cat`, which makes long-running interactive sessions painful and pushes users toward looser settings.
- *Always Allow* (or relying on `smart_approve`'s LLM read-only classification): no deterministic protection against specific command classes: nothing stops a `git push`, a `rm -rf` variant, or a destructive `docker` command that the classifier happens to consider read-only.

The `smart_approve` has two issues:

- the practical issue: false negatives. For example, the LLM deemed fine to reset my git working tree and lost a few hours of work (not in `git reflog` either, because it just got discarded)
- the theoretical issue: the smart_approve runs using the same llm model. If the model is compromised/trained to allow very specific commands even in adversarial mode, that's a security hole. We could run it with another LLM model and that would solve it, although this can get hardware expensive.

This came up in #11017, where I tried to configure exactly this kind of policy (auto-allow `git log`/`git status`, always ask for other `git` commands) using a `permissions:` block with `extension`/`pattern`/`action` entries (that syntax is not supported and is silently ignored). Issue #9407 does somehow duplicate some of this issue, but I wanted to point out the problems with smart_approve here, and refine the issue further.

**What would a good outcome look like?**

Users can define permission rules scoped to a specific tool (starting with the developer extension's shell tool) that match on the tool's arguments — i.e., the shell command string — using regular expressions, with the same three semantics as existing tool permissions:

- **allow** — the command runs without a prompt, even when the `smart_approve` classifier would flag it
- **ask** — a prompt is always shown for matching commands, even when smart_approve would have auto-approved them
- **deny** — the command is never executed; the tool call is rejected with an explanation.

Also, maybe important - when several rules match, the most restrictive action wins (`deny` > `ask` > `allow`)

Goose could also:

- show which rule matched for a prompt
- `goose info` / config tooling can list the rules
- other tools could later expose argument-level rules (e.g., path patterns for the text editor) as well, although shell commands is a priority

Example config for this:

```yaml
tool_permissions:
developer__shell:
rules:
- pattern: '^git\s+(log|status|diff|show)\b'
action: allow
- pattern: '^git\s+(push|rebase|reset)\b'
action: ask
- pattern: '^\s*(rm\s+-rf\s+/\s*$|mkfs\b|dd\s+if=)'
action: deny
```

**Possible approaches**

- **Extend the existing tool-permission mechanism** rather than introducing a parallel system: the docs describe per-tool levels managed by `goose configure` (stored in `permission.yaml`, runtime decisions in `permissions/tool_permissions.json`). Adding an optional list of argument-matching rules per tool slots into the existing precedence — explicit tool permissions are already evaluated before the `smart_approve` classifier, which is the behavior the issue thread confirmed is already correct at the tool level.
- **Regex dialect**: use a safe, non-backtracking regex engine (e.g., Rust `regex` crate syntax) to avoid ReDoS from user- or model-supplied patterns, and decide on anchoring semantics (whole-command match vs. search).
- **Compound commands**: rules must be evaluated so they cannot be bypassed by chaining — `git log && git push` should be treated as matching the `git push` rule, not only the `^git\s+log` prefix rule. Per-subcommand evaluation with the most-restrictive-wins rule (point 2 above) handles this naturally; this needs explicit design and tests.
- **Scope**: start with the developer extension's shell tool only, as requested; keep the rule shape tool-generic so other extensions can opt in later.
- **Syntax design**: prior art from Claude Code (`Bash(git log:*)`-style allow/deny rules) is a useful reference for what users in this ecosystem expect.
- **Security behavior**:
- The agent modifying rules is the big problem. Even if rules are only reloaded per session, not per tool call, an agent could launch another session and exploit modified rules. Maybe there is a way to prevent the agent from modifying without "ask" this rules yaml block from the config, no matter what?
- Rules evaluation must stay outside LLM control
- Be explicit in the docs that regex matching is a guardrail, not a sandbox: `bash -c 'git push'`, aliases, and quoting can evade string patterns. The smart_approve can then handle obfuscated commands as well. Hard isolation (containers, filesystem restrictions) is a separate capability.

**Additional context**

- Current online documentation:
- Permission modes: https://goose-docs.ai/docs/guides/managing-tools/goose-permissions (auto / approve / smart_approve / chat; `smart_approve` uses best-effort LLM read/write classification).
- Tool permissions: https://goose-docs.ai/docs/guides/managing-tools/tool-permissions (per-tool only: Always Allow / Ask Before / Never Allow, via `goose configure`).
- Reported environment: goose 1.45.0, CLI, Alpine Linux container.

- [x] I have verified this does not duplicate an existing feature request - well there is #9407 which has a some similarity.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.