Skip to main content

ssh_cli/
mascaramento.rs

1//! Mascaramento Unicode-safe de valores sensíveis (senhas, tokens).
2//!
3//! Regras:
4//! - Valores com até 16 caracteres (inclusive): retorna `"***"`.
5//! - Valores com mais de 16 caracteres: primeiros 12 + `...` + últimos 4.
6//!
7//! Usa `chars()` (e não indexação por bytes) para preservar grafemas Unicode.
8
9/// Limite mínimo para mascarar (inclusive). Valores com comprimento menor ou
10/// igual retornam `"***"`.
11pub const LIMITE_MINIMO_MASCARAR: usize = 16;
12
13/// Número de caracteres iniciais preservados.
14pub const CHARS_INICIO: usize = 12;
15
16/// Número de caracteres finais preservados.
17pub const CHARS_FIM: usize = 4;
18
19/// Mascara um valor sensível preservando início e fim.
20///
21/// # Exemplos
22///
23/// ```
24/// use ssh_cli::mascaramento::mascarar;
25///
26/// assert_eq!(mascarar("curto"), "***");
27/// assert_eq!(mascarar("1234567890abcdef"), "***"); // 16 chars
28///
29/// let longo = "0123456789abcdefghij";
30/// assert_eq!(mascarar(longo), "0123456789ab...ghij");
31/// ```
32#[must_use]
33pub fn mascarar(valor: &str) -> String {
34    let total: usize = valor.chars().count();
35
36    if total <= LIMITE_MINIMO_MASCARAR {
37        return "***".to_string();
38    }
39
40    let inicio: String = valor.chars().take(CHARS_INICIO).collect();
41    let fim: String = valor.chars().skip(total - CHARS_FIM).collect();
42
43    format!("{inicio}...{fim}")
44}
45
46#[cfg(test)]
47mod testes {
48    use super::*;
49
50    #[test]
51    fn valor_vazio_retorna_triplo_asterisco() {
52        assert_eq!(mascarar(""), "***");
53    }
54
55    #[test]
56    fn valor_curto_retorna_triplo_asterisco() {
57        assert_eq!(mascarar("abc"), "***");
58    }
59
60    #[test]
61    fn valor_com_16_caracteres_retorna_triplo_asterisco() {
62        assert_eq!(mascarar("1234567890abcdef"), "***");
63    }
64
65    #[test]
66    fn valor_com_17_caracteres_mostra_inicio_e_fim() {
67        let r = mascarar("1234567890abcdefg");
68        assert_eq!(r, "1234567890ab...defg");
69    }
70
71    #[test]
72    fn valor_longo_preserva_12_iniciais_e_4_finais() {
73        let senha = "senha-secreta-muito-longa-aqui-123456";
74        let r = mascarar(senha);
75        assert!(r.starts_with("senha-secret"));
76        assert!(r.ends_with("3456"));
77        assert!(r.contains("..."));
78    }
79
80    #[test]
81    fn valor_com_acentos_preserva_grafemas() {
82        let acentuado = "ação-configuração-senha-segura-123";
83        let r = mascarar(acentuado);
84        assert!(r.starts_with("ação-configu"));
85        assert!(r.contains("..."));
86    }
87
88    #[test]
89    fn valor_com_unicode_nao_crasha() {
90        let emojis = "🔒🔑🛡🔐✨🎉💎⚡🌟🔥🎨🚀🌈🍀🎯🎪🎭🎬🎮🎲";
91        let _ = mascarar(emojis);
92    }
93}