Skip to main content

Module rules

Module rules 

Source
Expand description

P5-1 (COMPOSABLE-HARNESS-DESIGN.md §2 module 11, §2.2 conflict C5, §5.3 risk 1): the rule engine. ONE engine, ONE evaluation order — deny → ask → allow, FIRST-MATCH within that fixed tier priority (the C5 decision: “the engine evaluates deny→ask→allow first-match (CC); OC-style [last-match] sets are translated at preset-import time” — crate::permissions::translate is that translator). This module never implements last-match semantics itself.

Fail-closed is the law. evaluate_command can return Allow ONLY when every extracted sub-command (a) parsed cleanly, (b) is not crate::permissions::canon::CanonSubcommand::opaque, and (c) either matches an explicit allow rule or the caller’s default was itself Allow. Any doubt anywhere in that chain resolves to at least Ask — see the doc comments on each branch below for exactly where.

Structs§

RuleSet
A first-match deny→ask→allow rule set (§2 module 11, C5). Each list holds pattern strings in one of two forms:

Enums§

Decision
The three-way outcome the engine can reach for a tool call. Ordered by strictness for Decision::stricter (Deny strictest, Allow loosest) — NOT by numeric severity in the tier-priority sense (which is a fixed deny→ask→allow scan order, not a totally-ordered scale); the ordering here exists purely to fold multiple sub-command decisions down to “the single worst one wins”, the compound-safety invariant D-3/risk-1 demands.
PathKind
Which access evaluate_path is checking — matches module 11’s read(...)/write(...) pseudo-tool rule pattern names.

Functions§

evaluate_command
Evaluate a (possibly compound) shell command against rules, folding every extracted sub-command’s decision down to the single strictest one (§5.3 risk 1’s compound-safety invariant). tool is the calling tool’s name ("bash", "shell", …) — sub-command patterns match as tool(cmdglob) against tool, e.g. a rule written "bash(rm -rf*)" applies to every sub-command of a bash call, not to a shell call.
evaluate_path
Evaluate a single resolved path against rules, as either a "read" or "write" pseudo-tool (module 11’s “path rules”: read/write globs — see RuleSet’s doc comment). Falls back to default when nothing matches.
evaluate_path_safe
SECURITY (CRITICAL fix, guarantor audit, traced to this file’s former evaluate_path doc comment claiming “no canonicalization … is involved here … no unparseable case to fail closed on” — the flawed assumption that let a traversal payload bypass protected_paths): the safe entry point for evaluating a RAW, model-supplied path tool argument (relative or absolute, exactly as it arrives in args["path"]) as a "read"/"write" pseudo-tool subject. Resolves raw_path against root through crate::safe_path::resolve_for_matching — the SAME dual lexical+symlink-resolved check crate::checkpoint’s P5-9 fix uses — and folds evaluate_path against the RAW subject, the lexically-normalized project-relative form, AND the symlink-resolved project-relative form down to the single strictest Decision (ties broken toward stricter, via Decision::stricter), so a rule can never be satisfied by matching only one of these three views.
evaluate_path_subject_safe
Like evaluate_path_safe, but for an arbitrary tool name subject instead of the read/write pseudo-tool — e.g. a rule authored against the REAL tool name with a path subject (design §4.4’s "read_file(*.env)" syntax, or an apply_patch-targeted rule). evaluate_path_safe is a thin wrapper over this for the pseudo-tool case; callers that need BOTH (the permissions gate always does — see crate::agent’s permissions_gate_denial_impl) call this function a second time with tool set to the real tool name and fold the two results together.
protected_path_deny_rules
Module 13 (permissions.protected_paths): build the read(...) + write(...) deny rules a protected-paths glob list expands to — a protected path is unconditionally denied for BOTH read and write, unlike an ordinary rule (this is the “never auto-approved” floor cc§4 documents for .git/**/.env*/etc, not an ordinary ask/allow-able rule). Callers fold the result into a RuleSet’s deny list (see crate::configfile::materialize_config), which — because deny is always checked first, unconditionally, with no override — makes a protected path exactly as hard a floor as Config::tool_deny_patterns already is (config.rs).