Skip to main content

keyhog_scanner/
error.rs

1//! Specialized error types for the scanner engine.
2
3use thiserror::Error;
4
5/// Convert a caught panic payload into a stable diagnostic without resuming
6/// unwinding across an optional backend boundary.
7pub(crate) fn panic_payload_detail(panic: Box<dyn std::any::Any + Send>) -> String {
8    if let Some(message) = panic.downcast_ref::<String>() {
9        message.clone()
10    } else if let Some(message) = panic.downcast_ref::<&'static str>() {
11        (*message).to_string()
12    } else {
13        "non-string panic payload".to_string()
14    }
15}
16
17#[derive(Debug, Error)]
18/// Errors returned while compiling detector patterns into a scanner.
19pub enum ScanError {
20    #[error(
21        "failed to compile regex for detector {detector_id} pattern {index}: {source}. Fix: correct the detector regex or capture group configuration"
22    )]
23    RegexCompile {
24        detector_id: String,
25        index: usize,
26        source: regex::Error,
27    },
28    #[error(
29        "detector {detector_id} pattern {index} declares capture group {group}, but its compiled regex has only {captures_len} group(s) (valid indices 0..{captures_len}). \
30         An out-of-range group makes the engine fall back to the whole match, capturing the keyword and separator instead of the secret. \
31         Fix: set `group` to a capture-group index that exists in the regex (group 0 is the whole match), or add the missing capture group"
32    )]
33    CaptureGroupOutOfRange {
34        detector_id: String,
35        index: usize,
36        group: usize,
37        captures_len: usize,
38    },
39    #[error(
40        "detector {detector_id} pattern {index} has invalid compiled policy: {reason}. Fix: correct the detector TOML pattern policy"
41    )]
42    DetectorPatternPolicy {
43        detector_id: String,
44        index: usize,
45        reason: String,
46    },
47    #[error(
48        "failed to compile scanner regex set: {0}. Fix: simplify the detector regex set or remove the invalid pattern"
49    )]
50    RegexSetCompile(#[from] regex::Error),
51    #[error(
52        "failed to build Aho-Corasick literal matcher: {0}. Fix: check for empty or invalid detector keywords"
53    )]
54    AhoCorasick(#[from] aho_corasick::BuildError),
55    #[error(
56        "GPU scanner failure: {0}. Fix: rerun with `--backend cpu` to scan on the CPU path, or run `keyhog doctor` to diagnose the GPU stack"
57    )]
58    Gpu(String),
59    #[error(
60        "SIMD scanner failure: {0}. Fix: rerun with `--backend cpu` for the portable scalar path, or run `keyhog doctor` to check CPU feature detection"
61    )]
62    Simd(String),
63    #[error(
64        "compiled scanner invariant violation: {table}[{pattern_index}] references detector_index {detector_index} but only {detectors_len} detector(s) are loaded. Fix: rebuild detector compilation so every compiled pattern keeps its source detector index before scanner construction completes"
65    )]
66    CompiledPatternDetectorIndex {
67        table: &'static str,
68        pattern_index: usize,
69        detector_index: usize,
70        detectors_len: usize,
71    },
72    #[error(
73        "phase-one admission plan identity rejected: {0}. Fix: rebuild the plan for the live chunk batch or use the recovery-aware dispatch API to retain its exact recovery receipt"
74    )]
75    AdmissionPlanIdentity(String),
76    #[error("scanner configuration failure: {0}. Fix: correct the bundled scanner rules")]
77    Config(String),
78}
79
80/// Specialized Result type for scanning operations.
81pub type Result<T> = std::result::Result<T, ScanError>;