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;
11
12pub use documentation::documentation_line_flags;
13pub use false_positive::{
14    is_false_positive_context, is_false_positive_context_with_path, is_false_positive_match_context,
15};
16pub use inference::{infer_context, infer_context_with_documentation, is_known_example_credential};
17
18const ASSIGNMENT_CONFIDENCE_MULTIPLIER: f64 = 1.0;
19const STRING_LITERAL_CONFIDENCE_MULTIPLIER: f64 = 0.9;
20const UNKNOWN_CONFIDENCE_MULTIPLIER: f64 = 0.8;
21const DOCUMENTATION_CONFIDENCE_MULTIPLIER: f64 = 0.3;
22const COMMENT_CONFIDENCE_MULTIPLIER: f64 = 0.4;
23const TEST_CODE_CONFIDENCE_MULTIPLIER: f64 = 0.3;
24const ENCRYPTED_CONFIDENCE_MULTIPLIER: f64 = 0.05;
25
26/// The structural context of a code location.
27#[derive(Debug, Clone, Copy, PartialEq)]
28pub enum CodeContext {
29    /// Direct assignment: `key = value`, `key: value`, `KEY=value`.
30    Assignment,
31    /// Inside a comment (`//`, `#`, `/*`, `--`, and similar).
32    Comment,
33    /// Inside a test function or test file.
34    TestCode,
35    /// Inside an encrypted/sealed block.
36    Encrypted,
37    /// Inside documentation (docstring, markdown code fence).
38    Documentation,
39    /// Inside a string literal in ordinary code.
40    StringLiteral,
41    /// Unknown or unstructured context.
42    Unknown,
43}
44
45impl CodeContext {
46    /// Confidence multiplier for this context.
47    pub fn confidence_multiplier(&self) -> f64 {
48        match self {
49            Self::Assignment => ASSIGNMENT_CONFIDENCE_MULTIPLIER,
50            Self::StringLiteral => STRING_LITERAL_CONFIDENCE_MULTIPLIER,
51            Self::Unknown => UNKNOWN_CONFIDENCE_MULTIPLIER,
52            Self::Documentation => DOCUMENTATION_CONFIDENCE_MULTIPLIER,
53            Self::Comment => COMMENT_CONFIDENCE_MULTIPLIER,
54            Self::TestCode => TEST_CODE_CONFIDENCE_MULTIPLIER,
55            Self::Encrypted => ENCRYPTED_CONFIDENCE_MULTIPLIER,
56        }
57    }
58
59    /// Returns `true` if this context should trigger hard suppression for low-confidence findings.
60    pub fn should_hard_suppress(&self, confidence: f64) -> bool {
61        match self {
62            Self::Documentation | Self::TestCode | Self::Comment => confidence < 0.5,
63            Self::Encrypted => confidence < 0.8,
64            _ => false,
65        }
66    }
67}
68
69#[cfg(test)]
70#[path = "context_tests.rs"]
71mod context_tests;