ssh_cli/masking.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2// G-SECDEV-05: pure module — no `unsafe` permitted (crate root allows only OS FFI / test env).
3#![forbid(unsafe_code)]
4//! Masking of sensitive values (passwords, tokens) — agent-safe.
5//!
6//! Rule (GAP-SSH-SEC-002): **always** returns `"***"`, without exposing prefix/suffix.
7//!
8//! Performance: returns `&'static str` (zero heap). Callers that need owned
9//! values can `.to_string()` / `.into()` at the edge — never allocate in the
10//! mask path itself (Rules Rust: treat every allocation as measurable cost).
11
12/// Fixed placeholder for any sensitive value.
13pub const FIXED_MASK: &str = "***";
14
15/// Masks a sensitive value without leaking useful characters.
16///
17/// Always returns the static placeholder [`FIXED_MASK`]. No heap allocation.
18///
19/// # Examples
20///
21/// ```
22/// use ssh_cli::masking::mask;
23///
24/// assert_eq!(mask("curto"), "***");
25/// assert_eq!(mask("password-secreta-muito-longa-aqui-123456"), "***");
26/// assert_eq!(mask("a"), ssh_cli::masking::FIXED_MASK);
27/// ```
28#[must_use]
29#[inline]
30pub fn mask(_valor: &str) -> &'static str {
31 FIXED_MASK
32}
33
34#[cfg(test)]
35mod tests {
36 use super::*;
37
38 #[test]
39 fn empty_value_returns_triple_asterisk() {
40 assert_eq!(mask(""), "***");
41 }
42
43 #[test]
44 fn short_value_returns_triple_asterisk() {
45 assert_eq!(mask("abc"), "***");
46 }
47
48 #[test]
49 fn long_value_never_exposes_prefix_or_suffix() {
50 let password = "senha-secreta-muito-longa-aqui-123456";
51 assert_eq!(mask(password), "***");
52 assert!(!mask(password).contains("senha"));
53 assert!(!mask(password).contains("1234"));
54 }
55
56 #[test]
57 fn unicode_value_does_not_crash() {
58 let emojis = "🔒🔑🛡🔐✨🎉💎⚡🌟🔥🎨🚀🌈🍀🎯🎪🎭🎬🎮🎲";
59 assert_eq!(mask(emojis), "***");
60 }
61
62 #[test]
63 fn mask_returns_static_placeholder() {
64 // API contract: &'static str equal to FIXED_MASK (no owned String).
65 let m: &'static str = mask("secret");
66 assert_eq!(m, FIXED_MASK);
67 assert_eq!(m, "***");
68 }
69}