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);
74 }
75
76 #[test]
77 fn test_truncation_with_entities() {
78 let input = "<".repeat(300);
80 let result = sanitize_label(&input);
81 assert_eq!(result.len(), 256 * 4);
83 }
84
85 #[test]
86 fn test_empty_string() {
87 assert_eq!(sanitize_label(""), "");
88 }
89
90 #[test]
91 fn test_unicode_preserved() {
92 assert_eq!(sanitize_label("你好世界"), "你好世界");
93 }
94
95 #[test]
96 fn test_mixed_content() {
97 assert_eq!(
98 sanitize_label("Node <A> & \"B\""),
99 "Node <A> & "B""
100 );
101 }
102
103 #[test]
104 fn test_backtick_and_braces() {
105 assert_eq!(sanitize_label("`{code}`"), "`{code}`");
107 }
108}