safety: permission policy DSL — allow/confirm/deny rule lists (closes #9)
The confirm_cmd boolean was too coarse: true interrupts every harmless
ls; false ungates everything. Most workflows want trust for read-only
ops while still gating writes/network/sudo.
New config:
permissions = {
allow = { "^ls%s", "^cat%s", "^git status" },
confirm = { "^rm%s", "^git push", "^docker%s", "^sudo%s" },
deny = { "^ssh%s+root@", "^curl%s+http[^s]" },
}
Verdict order: deny > confirm > allow. First match in the chosen
category wins. Unmatched defaults to "confirm". Patterns are Lua
patterns (not regex) per PHASE0.md §3 — no compiled extensions.
Verdict behavior in the interactive CMD: loop:
- allow → run without prompt
- deny → status line, skip
- confirm → [y/N] prompt (same UX as legacy confirm_cmd=true)
Backward compat:
- permissions unset + confirm_cmd=true → always confirm
- permissions unset + confirm_cmd=false → always allow
- permissions set → policy table is authoritative
Scope deliberately limited to the interactive AI-suggested CMD: gate.
Norris autonomous mode keeps its own safety.is_destructive machinery
(combining the two would double-gate or replace the LLM probe — both
non-obvious behavioral changes that belong in their own issues).
User-typed shell-routed lines (`router.classify → "shell"`) and
:exec also bypass the policy by design — those are direct user intent.
New introspection:
:perms list — show the configured rule lists
:perms check <cmd> — report verdict + matching rule (debug)
safety.classify_command is exported and unit-tested with 12 cases
covering each category, priority order (deny > allow on overlap),
and both fallback paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+11
@@ -71,6 +71,17 @@ return {
|
||||
-- post_cmd = (os.getenv("HOME") or ".") .. "/.aish/hooks/post-cmd",
|
||||
-- },
|
||||
|
||||
-- Issue #9: permission policy DSL for AI-suggested CMD: lines. When set,
|
||||
-- supersedes shell.confirm_cmd. Patterns are Lua patterns (NOT regex)
|
||||
-- per substrate invariant §3 (no compiled extensions). Priority order:
|
||||
-- deny > confirm > allow; first match in the chosen category wins.
|
||||
-- Unmatched commands default to "confirm". Probe with :perms check <cmd>.
|
||||
-- permissions = {
|
||||
-- allow = { "^ls%s", "^cat%s", "^git status", "^git diff" },
|
||||
-- confirm = { "^rm%s", "^git push", "^docker%s", "^sudo%s" },
|
||||
-- deny = { "^ssh%s+root@", "^curl%s+http[^s]" },
|
||||
-- },
|
||||
|
||||
-- Phase 2 (docs/PHASE2.md): MCP server registry + tool-call policy.
|
||||
-- The block is OFF by default — connect-at-startup happens only when
|
||||
-- `servers` is non-empty. Uncomment + adjust per your fleet.
|
||||
|
||||
Reference in New Issue
Block a user