graphify_security/
label_validator.rs1const MAX_LABEL_LEN: usize = 256;
5
6pub fn sanitize_label(label: &str) -> String {
12 let cleaned: String = label
13 .chars()
14 .filter(|c| !c.is_control())
15 .take(MAX_LABEL_LEN)
16 .collect();
17
18 cleaned
19 .replace('&', "&")
20 .replace('<', "<")
21 .replace('>', ">")
22 .replace('"', """)
23 .replace('\'', "'")
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29
30 #[test]
31 fn test_plain_text_unchanged() {
32 assert_eq!(sanitize_label("Hello World"), "Hello World");
33 }
34
35 #[test]
36 fn test_html_entities_escaped() {
37 assert_eq!(
38 sanitize_label("<script>alert(\"xss\")</script>"),
39 "<script>alert("xss")</script>"
40 );
41 }
42
43 #[test]
44 fn test_ampersand_escaped() {
45 assert_eq!(sanitize_label("A & B"), "A & B");
46 }
47
48 #[test]
49 fn test_single_quote_escaped() {
50 assert_eq!(sanitize_label("it's"), "it's");
51 }
52
53 #[test]
54 fn test_control_chars_stripped() {
55 assert_eq!(sanitize_label("hello\x00world\x07"), "helloworld");
56 }
57
58 #[test]
59 fn test_newlines_stripped() {
60 assert_eq!(sanitize_label("line1\nline2\r\n"), "line1line2");
61 }
62
63 #[test]
64 fn test_tabs_stripped() {
65 assert_eq!(sanitize_label("col1\tcol2"), "col1col2");
66 }
67
68 #[test]
69 fn test_truncation() {
70 let long = "a".repeat(300);
71 let result = sanitize_label(&long);
72 assert_eq!(result.len(), 256);
73 }
74
75 #[test]
76 fn test_truncation_with_entities() {
77 let input = "<".repeat(300);
78 let result = sanitize_label(&input);
79 assert_eq!(result.len(), 256 * 4);
80 }
81
82 #[test]
83 fn test_empty_string() {
84 assert_eq!(sanitize_label(""), "");
85 }
86
87 #[test]
88 fn test_unicode_preserved() {
89 assert_eq!(sanitize_label("你好世界"), "你好世界");
90 }
91
92 #[test]
93 fn test_mixed_content() {
94 assert_eq!(
95 sanitize_label("Node <A> & \"B\""),
96 "Node <A> & "B""
97 );
98 }
99
100 #[test]
101 fn test_backtick_and_braces() {
102 assert_eq!(sanitize_label("`{code}`"), "`{code}`");
103 }
104}