Skip to main content

lean_ctx_patterns/
safety_needles.rs

1// SPDX-License-Identifier: Apache-2.0
2
3const SAFETY_NEEDLES: &[&str] = &[
4    "CRITICAL",
5    "FATAL",
6    "panic",
7    "FAILED",
8    "unhealthy",
9    "Exited",
10    "OOMKilled",
11    "DETACHED HEAD",
12    "detached",
13    "vulnerability",
14    "CVE-",
15    "denied",
16    "unauthorized",
17    "forbidden",
18    "error",
19    "ERROR",
20    "WARNING",
21    "WARN",
22    "fail",
23    "segfault",
24    "Segmentation fault",
25    "SIGSEGV",
26    "SIGKILL",
27    "killed",
28    "out of memory",
29    "stack overflow",
30    "permission denied",
31    "certificate",
32    "expired",
33    "corrupt",
34    // Test-runner outcome lines — never drop these during truncation so a large
35    // (even fully-passing) test run keeps every per-suite summary and any buried
36    // failure. Covers Rust, pytest, jest/mocha, go test, dotnet, etc.
37    "test result:", // cargo / rust
38    "passed",       // "5 passed", "0 passed", "Tests: N passed"
39    "passing",      // mocha "5 passing"
40    "panicked",     // rust panic location line
41    "assertion",    // assertion failures across many runners
42    "traceback",    // python tracebacks
43    "tests run",    // junit-style summaries
44    "ran all test", // jest "Ran all test suites"
45];
46
47pub fn is_safety_relevant(line: &str) -> bool {
48    let lower = line.to_ascii_lowercase();
49    SAFETY_NEEDLES
50        .iter()
51        .any(|needle| lower.contains(&needle.to_ascii_lowercase()))
52}
53
54pub fn extract_safety_lines(lines: &[&str], max: usize) -> Vec<String> {
55    lines
56        .iter()
57        .filter(|l| is_safety_relevant(l))
58        .take(max)
59        .map(std::string::ToString::to_string)
60        .collect()
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn detects_critical() {
69        assert!(is_safety_relevant("CRITICAL: disk full"));
70        assert!(is_safety_relevant("container unhealthy"));
71        assert!(is_safety_relevant("CVE-2024-12345 found"));
72        assert!(is_safety_relevant("error: something broke"));
73        assert!(is_safety_relevant("permission denied"));
74    }
75
76    #[test]
77    fn ignores_normal_lines() {
78        assert!(!is_safety_relevant("compiled successfully"));
79        assert!(!is_safety_relevant("200 OK"));
80        assert!(!is_safety_relevant("Downloaded 3 crates"));
81    }
82
83    #[test]
84    fn preserves_test_runner_summaries() {
85        // Critical: test outcome lines must always survive truncation so large
86        // test runs never lose their pass/fail summaries (regression guard).
87        assert!(is_safety_relevant(
88            "test result: ok. 23 passed; 0 failed; 0 ignored"
89        ));
90        assert!(is_safety_relevant(
91            "test result: FAILED. 1 passed; 2 failed"
92        ));
93        assert!(is_safety_relevant("=== 5 passed, 1 warning in 0.42s ==="));
94        assert!(is_safety_relevant(
95            "Tests:       1 failed, 5 passed, 6 total"
96        ));
97        assert!(is_safety_relevant("5 passing (1s)"));
98        assert!(is_safety_relevant(
99            "thread 'main' panicked at src/lib.rs:10:5"
100        ));
101        assert!(is_safety_relevant("Traceback (most recent call last):"));
102    }
103
104    #[test]
105    fn extracts_limited_safety_lines() {
106        let lines = vec![
107            "line 1",
108            "ERROR: something",
109            "line 3",
110            "CRITICAL: disk",
111            "line 5",
112            "WARNING: old version",
113        ];
114        let result = extract_safety_lines(&lines, 2);
115        assert_eq!(result.len(), 2);
116        assert!(result[0].contains("ERROR"));
117        assert!(result[1].contains("CRITICAL"));
118    }
119}