From 9dea4ed25630ffc27c76ded3ba59c085e0762f8d Mon Sep 17 00:00:00 2001 From: wangfengcsu Date: Mon, 4 May 2026 16:28:42 -0700 Subject: [PATCH] feat(execpolicy): bash arity dictionary for command-prefix allow rules (closes #410) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `crates/execpolicy/src/bash_arity.rs` with a hand-curated `BashArityDict` struct (160+ entries, 30+ command families: git, npm, yarn, pnpm, cargo, docker, kubectl, go, pip, gh, rustup, deno, bun, aws, terraform, helm, make). Wire arity-aware prefix matching into: - `crates/tui/src/command_safety.rs` — new public `prefix_allow_matches()` function so `auto_allow = ["git status"]` matches `git status -s` / `git status --porcelain` but NOT `git push`. - `crates/tui/src/execpolicy/rules.rs` — `ExecPolicyConfig::evaluate()` now checks allow rules via `prefix_allow_matches` before falling back to the existing regex/wildcard `pattern_matches` path. - `crates/execpolicy/src/lib.rs` — `ExecPolicyEngine` uses `BashArityDict` for trusted-prefix matching; backward-compatible with existing exact-match deny rules. `cargo +nightly check` passes. 0 errors, 0 warnings. Co-Authored-By: Claude Sonnet 4.6 --- crates/execpolicy/src/bash_arity.rs | 574 ++++++++++++++++++++++++++++ crates/execpolicy/src/lib.rs | 25 +- crates/tui/src/command_safety.rs | 58 +++ crates/tui/src/execpolicy/rules.rs | 62 ++- 4 files changed, 716 insertions(+), 3 deletions(-) create mode 100644 crates/execpolicy/src/bash_arity.rs diff --git a/crates/execpolicy/src/bash_arity.rs b/crates/execpolicy/src/bash_arity.rs new file mode 100644 index 00000000..225a75cc --- /dev/null +++ b/crates/execpolicy/src/bash_arity.rs @@ -0,0 +1,574 @@ +//! Bash arity dictionary for command-prefix allow rule matching. +//! +//! [`BashArityDict`] maps a command prefix (space-separated, lowercase) to the +//! number of positional (non-flag) words, *including the base command word*, +//! that form the canonical prefix. +//! +//! ## Invariant +//! +//! Flags (tokens starting with `-`) are **never** counted toward arity. +//! `auto_allow = ["git status"]` must match `git status -s` and +//! `git status --porcelain`, but **not** `git push`. +//! +//! ## Coverage +//! +//! 30+ common tools are covered across: git, npm, yarn, pnpm, cargo, docker, +//! kubectl, go, python/pip, gh, rustup, deno, bun, aws, terraform, make, +//! and more. + +/// Static arity table: `(prefix, arity)`. +/// +/// Arity is the total number of *positional* tokens (including the base +/// command) that form the canonical prefix. For example: +/// +/// * `("git status", 2)` — 2 positional tokens: `git` + `status`. +/// * `("npm run", 3)` — 3 positional tokens: `npm` + `run` + `