Skip to main content

keyhog_scanner/context/
mod.rs

1//! Structural context analysis: understand WHERE in code a potential secret appears.
2//!
3//! Instead of treating code as flat text, we infer the structural context of
4//! each match (assignment, comment, test code, encrypted block, documentation)
5//! and adjust confidence accordingly. Not an AST parser - just fast,
6//! language-agnostic structural inference.
7
8mod documentation;
9mod false_positive;
10mod inference;
11mod placeholder;
12
13pub(crate) use documentation::documentation_line_flags;
14#[cfg(test)]
15pub(crate) use false_positive::parse_disclaimer_phrases;
16pub(crate) use false_positive::{
17    has_disclaimer_comment_bytes, is_false_positive_context, is_false_positive_match_context,
18    is_integrity_hash_bytes, is_public_pem_block_at,
19};
20pub use inference::infer_context;
21pub(crate) use inference::infer_context_with_documentation;
22#[cfg(test)]
23pub(crate) use inference::parse_test_path_rules;
24pub(crate) use inference::{is_in_test_function, is_rust_fn_signature, strip_comment_prefix};
25pub(crate) use placeholder::is_known_example_credential;
26#[cfg(feature = "entropy")]
27pub(crate) use placeholder::is_monotonic_sequence_placeholder;
28#[cfg(test)]
29pub(crate) use placeholder::is_sequential_placeholder;
30
31const ASSIGNMENT_CONFIDENCE_MULTIPLIER: f64 = 1.0;
32const STRING_LITERAL_CONFIDENCE_MULTIPLIER: f64 = 0.9;
33const UNKNOWN_CONFIDENCE_MULTIPLIER: f64 = 0.8;
34const DOCUMENTATION_CONFIDENCE_MULTIPLIER: f64 = 0.3;
35const COMMENT_CONFIDENCE_MULTIPLIER: f64 = 0.4;
36const TEST_CODE_CONFIDENCE_MULTIPLIER: f64 = 0.3;
37const ENCRYPTED_CONFIDENCE_MULTIPLIER: f64 = 0.05;
38const SOFT_CONTEXT_HARD_SUPPRESSION_THRESHOLD: f64 = 0.5;
39const ENCRYPTED_CONTEXT_HARD_SUPPRESSION_THRESHOLD: f64 = 0.8;
40
41/// The structural context of a code location.
42#[derive(Debug, Clone, Copy, PartialEq)]
43pub enum CodeContext {
44    /// Direct assignment: `key = value`, `key: value`, `KEY=value`.
45    Assignment,
46    /// Inside a comment (`//`, `#`, `/*`, `--`, and similar).
47    Comment,
48    /// Inside a test function or test file.
49    TestCode,
50    /// Inside an encrypted/sealed block.
51    Encrypted,
52    /// Inside documentation (docstring, markdown code fence).
53    Documentation,
54    /// Inside a string literal in ordinary code.
55    StringLiteral,
56    /// Unknown or unstructured context.
57    Unknown,
58}
59
60impl CodeContext {
61    /// Legacy baseline multiplier for callers that classify context without a
62    /// detector plan. Production candidate scoring uses the active detector's
63    /// compiled `match_confidence` multipliers instead.
64    pub fn confidence_multiplier(&self) -> f64 {
65        match self {
66            Self::Assignment => ASSIGNMENT_CONFIDENCE_MULTIPLIER,
67            Self::StringLiteral => STRING_LITERAL_CONFIDENCE_MULTIPLIER,
68            Self::Unknown => UNKNOWN_CONFIDENCE_MULTIPLIER,
69            Self::Documentation => DOCUMENTATION_CONFIDENCE_MULTIPLIER,
70            Self::Comment => COMMENT_CONFIDENCE_MULTIPLIER,
71            Self::TestCode => TEST_CODE_CONFIDENCE_MULTIPLIER,
72            Self::Encrypted => ENCRYPTED_CONFIDENCE_MULTIPLIER,
73        }
74    }
75
76    /// Legacy baseline hard-suppression decision for callers without a detector
77    /// plan. Production finalization uses the active detector's compiled
78    /// context thresholds.
79    pub fn should_hard_suppress(&self, confidence: f64) -> bool {
80        self.hard_suppression_threshold()
81            .is_some_and(|threshold| confidence < threshold)
82    }
83
84    /// Legacy baseline threshold paired with [`CodeContext::should_hard_suppress`].
85    pub const fn hard_suppression_threshold(&self) -> Option<f64> {
86        match self {
87            Self::Documentation | Self::TestCode | Self::Comment => {
88                Some(SOFT_CONTEXT_HARD_SUPPRESSION_THRESHOLD)
89            }
90            Self::Encrypted => Some(ENCRYPTED_CONTEXT_HARD_SUPPRESSION_THRESHOLD),
91            _ => None,
92        }
93    }
94}