Skip to main content

ailint_core/rules/security/
mod.rs

1//! Security rules: prompt-injection surface, dangerous permissions, secrets.
2//! Range: **AIL200 – AIL299**.
3
4pub mod prompt_injection;
5pub mod sensitive_data;
6pub mod tool_confirmation;
7pub mod unrestricted_tool;
8
9pub use prompt_injection::NoPromptInjectionMarkerRule;
10pub use sensitive_data::NoSensitiveDataInInstructionsRule;
11pub use tool_confirmation::ToolConfirmationRequiredRule;
12pub use unrestricted_tool::NoUnrestrictedToolGrantRule;
13
14use crate::rules::{Rule, RuleId};
15
16/// AIL200: text contains a known prompt-injection marker.
17pub const AIL200: RuleId = RuleId::new(200, "no-prompt-injection-marker");
18/// AIL201: guidance grants a tool unrestricted or auto-approved access.
19pub const AIL201: RuleId = RuleId::new(201, "no-unrestricted-tool-grant");
20/// AIL202: instructions embed secrets or other sensitive data.
21pub const AIL202: RuleId = RuleId::new(202, "no-sensitive-data-in-instructions");
22/// AIL203: destructive actions described without a confirmation step.
23pub const AIL203: RuleId = RuleId::new(203, "tool-confirmation-required");
24
25/// All security rules, in registration order.
26pub fn all_rules() -> Vec<Box<dyn Rule>> {
27    vec![
28        Box::new(NoPromptInjectionMarkerRule),
29        Box::new(NoUnrestrictedToolGrantRule),
30        Box::new(NoSensitiveDataInInstructionsRule),
31        Box::new(ToolConfirmationRequiredRule),
32    ]
33}
34
35// 1-based line number containing the byte at `offset` in `raw`.
36pub(crate) fn line_of_offset(raw: &str, offset: usize) -> usize {
37    let cap = offset.min(raw.len());
38    raw.as_bytes()[..cap]
39        .iter()
40        .filter(|&&b| b == b'\n')
41        .count()
42        + 1
43}
44
45// Trimmed content of the line containing byte `offset`.
46pub(crate) fn line_containing(raw: &str, offset: usize) -> String {
47    let bytes = raw.as_bytes();
48    let cap = offset.min(bytes.len());
49    let start = bytes[..cap]
50        .iter()
51        .rposition(|&b| b == b'\n')
52        .map(|i| i + 1)
53        .unwrap_or(0);
54    let end = bytes[cap..]
55        .iter()
56        .position(|&b| b == b'\n')
57        .map(|i| cap + i)
58        .unwrap_or(bytes.len());
59    raw[start..end].trim().to_string()
60}
61
62// Char-safe truncation to at most `max` characters.
63pub(crate) fn truncate_chars(s: &str, max: usize) -> String {
64    if s.chars().count() <= max {
65        return s.to_string();
66    }
67    s.chars().take(max).collect()
68}