1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//! Privacy-first scanning core for secrets and sensitive data.
//!
//! `cribra` provides deterministic detection, reporting, querying and
//! share-safe transformation of UTF-8 text. Applications own I/O and storage;
//! the crate operates on caller-provided text and does not retain matched
//! secret values inside public [`Finding`] values.
//!
//! # Quick start
//!
//! ```
//! use cribra::Scanner;
//!
//! let scanner = Scanner::default();
//! let results = scanner.scan([
//! ("config.env", "TOKEN=example"),
//! ("settings.toml", "mode = \"production\""),
//! ]);
//!
//! assert_eq!(results.len(), 2);
//! println!("{}", results.summary());
//! ```
//!
//! # Result model
//!
//! A scan returns [`ScanResults<K>`], preserving the caller's source key `K`.
//! Each source owns an immutable [`ScanReport`], whose [`Finding`] values expose
//! rule metadata, severity, confidence, optional [`Remediation`] and a
//! [`Location`].
//!
//! Source coordinates use:
//!
//! - zero-based, half-open UTF-8 byte offsets;
//! - one-based lines;
//! - one-based Unicode scalar columns.
//!
//! Findings intentionally do not contain the matched source value.
//!
//! //! # Ambiguous candidates and explainability
//!
//! [`ScanReport`] keeps classified [`Finding`] values separate from
//! [`SensitiveCandidate`] values that are structurally review-worthy but do not
//! have enough evidence for classification.
//!
//! Explainability projects those existing authorities into [`Explanation`]:
//!
//! - `Explanation::Classified(DetectionMode)` describes how a rule-backed
//! finding was validated;
//! - `Explanation::Ambiguous(CandidateEvidence)` describes the evidence behind
//! a review-only candidate.
//!
//! Findings do not duplicate rule metadata. Their explanation is resolved
//! against the [`Scanner`] that owns the compiled metadata and fails closed when
//! it cannot be resolved unambiguously. Candidate explanation is projected
//! directly from its existing evidence.
//!
//! Explanation is presentation-agnostic and contains no source snippets or
//! matched sensitive values. Applications remain responsible for human-facing
//! copy.
//!
//! # Querying
//!
//! [`ScanResults::query`] builds a lazy [`ScanQuery`] over borrowed findings.
//! Filters can be composed before optionally materializing an explicitly sorted
//! [`SortedScanQuery`].
//!
//! ```
//! use cribra::{ScanSort, Scanner, Severity};
//!
//! let scanner = Scanner::default();
//! let results = scanner.scan([("config.env", "TOKEN=example")]);
//!
//! let findings = results
//! .query()
//! .minimum_severity(Severity::High)
//! .sort(ScanSort::Location);
//!
//! for (source, finding) in findings.iter() {
//! println!("{source}: {}", finding.rule_id());
//! }
//! ```
//!
//! # Transformations
//!
//! [`transform`] provides explicit share-safe transformations:
//!
//! - [`transform::redact`] for conservative replacement;
//! - [`transform::template`] for semantic placeholders;
//! - [`transform::pseudonymize`] for deterministic keyed pseudonyms;
//! - [`transform::synthesize`] for deterministic keyed synthetic values;
//! - [`transform::ShareBundle`] for transformed keyed batches plus manifest
//! metadata.
//!
//! ```
//! use cribra::{Rule, Scanner, Severity, transform::redact};
//!
//! let scanner = Scanner::builder()
//! .rule(Rule::literal("credential", "SECRET", Severity::High))
//! .build()?;
//!
//! let source = "TOKEN=SECRET";
//! let results = scanner.scan([("memory", source)]);
//! let report = results.single_report().expect("one report");
//!
//! assert_eq!(redact(source, report)?, "TOKEN=[REDACTED]");
//!
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! # Optional features
//!
//! `serde` enables serialization support for public data contracts.
//!
//! `parallel` enables `Scanner::parallel_scan`, which distributes independent
//! inputs through Rayon while preserving input order and the same per-source
//! semantics as serial scanning.
//!
//! # Application boundary
//!
//! File loading, network access, repository integration, authentication,
//! persistence and UI are intentionally outside this crate. This keeps the
//! scanner reusable in local-first native, WASM/PWA, desktop and service
//! applications.
//! Privacy-first Rust engine for detecting secrets and sensitive data.
//!
//! Cribrais a deterministic, local-first scanning core. It accepts UTF-8
//! text and returns structured findings without filesystem, network, terminal,
//! browser or cloud responsibilities.
//!
//! # Example
//!
//! ```
//! use cribra::{Rule, Scanner, Severity};
//!
//! let scanner = Scanner::builder()
//! .rule(Rule::prefix(
//! "example-token",
//! "example_live_",
//! Severity::Critical,
//! ))
//! .build()?;
//!
//! let results = scanner.scan([
//! ("memory", "TOKEN=example_live_123456"),
//! ]);
//!
//! let report = results.single_report().expect("one source was scanned");
//! assert_eq!(report.len(), 1);
//!
//! # Ok::<(), cribra::ScannerBuildError>(())
//! ```
//!
//! With the optional `parallel` feature, native callers can use
//! `Scanner::parallel_scan` while preserving input order.
pub use Confidence;
pub use Explanation;
pub use Finding;
pub use Location;
pub use Redaction;
pub use Remediation;
pub use ;
pub use ;
pub use ScanEntry;
pub use ;
pub use ScanReport;
pub use ScanResults;
pub use ScanSort;
pub use ScanSummary;
pub use Scanner;
pub use ;
pub use ;
pub use Severity;
pub use redact;