{"record":{"id":"b5318372f9ccbd81","repo":"Hmbown/CodeWhale","slug":"persistent-command-allow-rules-must-use-exact-matc","errorCode":null,"errorMessage":"persistent command allow rules must use exact matching","messagePattern":"persistent command allow rules must use exact matching","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/config/src/lib.rs","lineNumber":5264,"sourceCode":"    /// `permissions.toml` file.\n    ///\n    /// The caller is responsible for deciding which tool calls are eligible;\n    /// this boundary rejects broad or incorrectly typed records so a UI bug\n    /// cannot persist an unscoped allow grant.\n    pub fn append_allow_rules(&mut self, rules: &[ToolAskRule]) -> Result<usize> {\n        for rule in rules {\n            if rule.action != PermissionAction::Allow {\n                bail!(\"append_allow_rules only accepts action = \\\"allow\\\"\");\n            }\n            let Some(workspace) = rule\n                .workspace\n                .as_deref()\n                .and_then(codewhale_execpolicy::normalize_workspace_scope)\n            else {\n                bail!(\"persistent allow rules must be scoped to a workspace\");\n            };\n            if rule.command.is_some() && !rule.command_exact {\n                bail!(\"persistent command allow rules must use exact matching\");\n            }\n            if rule.command.is_none() && rule.path.is_none() {\n                bail!(\"persistent allow rules must match an exact command or path\");\n            }\n            if let Some(command) = rule.command.as_deref()\n                && command.trim().is_empty()\n            {\n                bail!(\"persistent command allow rules must not be empty\");\n            }\n            if let Some(path) = rule.path.as_deref()\n                && codewhale_execpolicy::normalize_workspace_relative_path(path, &workspace)\n                    .is_none_or(|path| path.is_empty())\n            {\n                bail!(\"persistent path allow rules must stay within the workspace\");\n            }\n        }\n        self.append_permission_rules(rules, PermissionAction::Allow)\n    }","sourceCodeStart":5246,"sourceCodeEnd":5282,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/config/src/lib.rs#L5246-L5282","documentation":"append_allow_rules (crates/config/src/lib.rs:5264) rejects an allow rule that carries a command but has command_exact = false. Persistent command grants must use exact string matching so 'allow npm' can never widen into 'allow npm; curl evil.sh' via prefix/pattern semantics. Inexact matches are allowed only as session rules.","triggerScenarios":"A ToolAskRule with command = Some(\"npm ...\") and command_exact = false passed to append_allow_rules — e.g. a caller reusing a pattern-match session rule for persistence.","commonSituations":"UI persisting a prefix-matched approval, porting rules from a tool that allows glob commands, constructing rules from a deserialized older schema defaulting command_exact to false.","solutions":["Set command_exact = true and store the full exact command string that was approved","If the user approved only a prefix/pattern, keep it session-scoped instead of persisting","For path-based tools, persist a path rule instead of a command rule"],"exampleFix":"// before\nlet rule = ToolAskRule { command: Some(cmd.clone()), command_exact: false, /* ... */ };\n\n// after\nlet rule = ToolAskRule { command: Some(cmd.clone()), command_exact: true, /* ... */ };","handlingStrategy":"validation","validationCode":"assert!(rule.command.is_none() || rule.command_exact); // before append_allow_rules","typeGuard":"fn is_exact_command_rule(rule: &ToolAskRule) -> bool {\n    rule.command.as_deref().is_none_or(|_| rule.command_exact)\n}","tryCatchPattern":null,"preventionTips":["Default command_exact to true in any rule constructor used for persistence","Do not convert pattern/prefix session approvals into persistent rules","Show the exact command string in the 'always allow' prompt so users approve the exact form"],"tags":["rust","permissions","security","validation","command-matching"],"backgroundTag":"permission-rule-validation-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}