ailint_core/rules/security/
tool_confirmation.rs1use crate::parser::{DocumentContent, ParsedDocument};
6use crate::rules::security::{line_of_offset, AIL203};
7use crate::rules::{Rule, RuleContext, RuleId, Severity, Violation};
8
9#[derive(Debug, Default)]
11pub struct ToolConfirmationRequiredRule;
12
13impl Rule for ToolConfirmationRequiredRule {
14 fn id(&self) -> RuleId {
15 AIL203
16 }
17
18 fn default_severity(&self) -> Severity {
19 Severity::Error
20 }
21
22 fn run(&self, doc: &ParsedDocument, ctx: &RuleContext<'_>) -> Vec<Violation> {
23 match &doc.content {
24 DocumentContent::Markdown(_) | DocumentContent::Text => {}
25 _ => return Vec::new(),
26 };
27
28 let destructive_phrases = [
29 "delete data",
30 "rm -rf",
31 "drop table",
32 "truncate table",
33 "irreversible",
34 "destructive action",
35 ];
36 let confirmation_phrases = [
37 "ask",
38 "confirm",
39 "human",
40 "wait",
41 "permission",
42 "consent",
43 "approval",
44 ];
45
46 let lower_raw = doc.raw.to_lowercase();
47
48 let mut violations = Vec::new();
49 for d in &destructive_phrases {
50 if let Some(idx) = lower_raw.find(d) {
51 if !confirmation_phrases.iter().any(|c| lower_raw.contains(c)) {
52 let line = line_of_offset(&doc.raw, idx);
53 let mut v = Violation::new(
54 AIL203,
55 ctx.severity,
56 doc.path.clone(),
57 format!("Document describes destructive actions ('{}') but lacks confirmation constraints.", d),
58 ).at(line, 1);
59 v.fix_hint = Some("Include explicit phrasing like 'wait for human confirmation' or 'ask for permission' before destructive actions.".to_string());
60 violations.push(v);
61
62 break;
63 }
64 }
65 }
66
67 violations
68 }
69}