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}