Skip to main content

forbidden_strings/
scan_finding.rs

1//! What:
2//!  Redacted structured findings shared by embedded and standalone scans.
3//! Why:
4//!  A policy consumer must not recover candidate identity or finding kind by parsing terminal text.
5//!
6//! In TS you'd write (pseudocode):
7//! ```ts
8//! // A discriminated union carries rule identity and source location, never matched bytes.
9//! ```
10
11/// What:
12///  One content/name result or explicit inability to complete a scan.
13/// Why:
14///  Enum variants keep source locations separate from operational failures without sentinel line numbers.
15///
16/// In TS you'd write (pseudocode):
17/// ```ts
18/// type ScanFinding = { kind: 'content'; line: number; rule: string }
19///   | { kind: 'name'; component: number; rule: string }
20///   | { kind: 'engine-error' } | { kind: 'pathname-line-break' };
21/// ```
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub enum ScanFinding {
24    /// A rule matched content on one physical source line.
25    Content {
26        /// One-based line index;
27        ///  usize indexes an in-memory file rather than a fixed-width wire integer.
28        line: usize,
29        /// Owned non-secret configured name or opaque numeric rule token,
30        ///  never the matched source.
31        rule: String,
32    },
33    /// A rule matched an entire logical pathname component.
34    Name {
35        /// One-based component number after excluding navigation/volume prefixes.
36        component: usize,
37        /// Owned rule identity,
38        ///  independent of the fully masked matching component.
39        rule: String,
40    },
41    /// A matcher failed,
42    ///  so absence of matches cannot be treated as a pass.
43    EngineError,
44    /// A pathname contains line endings that the line-based matcher cannot inspect faithfully.
45    PathnameLineBreak,
46}
47
48/// Keep terminal rendering at the standalone consumer boundary.
49impl ScanFinding {
50    /// What:
51    ///  Render the existing standalone protocol using an already sanitized display path.
52    /// Why:
53    ///  Structured consumers use the enum directly;
54    ///  the executable preserves its current text contract.
55    ///
56    /// In TS you'd write (pseudocode):
57    /// ```ts
58    /// function render(finding, safeDisplayPath): string;
59    /// ```
60    pub(crate) fn render(&self, safe_display: &str) -> String {
61        // Match only the enum's actual variants; adding an operational failure requires an explicit rendering choice.
62        match self {
63            Self::Content { line, rule } => return format!("{safe_display}:{line} rule={rule}"),
64            Self::Name { component, rule } => return format!("{safe_display}:name:{component} rule={rule}"),
65            Self::EngineError => return format!("{safe_display}: engine error"),
66            Self::PathnameLineBreak => return format!("{safe_display}: unsupported pathname line break"),
67        }
68    }
69}