Skip to main content

agentsight_capture/analyzers/
common.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 eunomia-bpf org.
3
4/// Common utilities for data analysis and processing across analyzers
5use serde_json::Value;
6
7/// Detect if string data is binary or text based on control characters
8///
9/// This function determines data type by checking if the string contains
10/// control characters beyond the allowed ones (\n, \r, \t).
11///
12/// # Arguments
13/// * `data_str` - The string data to analyze
14///
15/// # Returns
16/// * `"text"` if the data contains only printable characters and allowed control chars
17/// * `"binary"` if the data contains other control characters (likely binary data)
18///
19/// # Examples
20/// ```
21/// use agentsight_capture::analyzers::common::detect_data_type;
22///
23/// assert_eq!(detect_data_type("Hello World"), "text");
24/// assert_eq!(detect_data_type("HTTP/1.1 200 OK\r\n"), "text");
25/// assert_eq!(detect_data_type("\x00\x01\x02binary"), "binary");
26/// ```
27pub fn detect_data_type(data_str: &str) -> &'static str {
28    if data_str
29        .chars()
30        .all(|c| !c.is_control() || c == '\n' || c == '\r' || c == '\t')
31    {
32        "text"
33    } else {
34        "binary"
35    }
36}
37
38/// Convert data to a human-readable string representation
39///
40/// This function handles both text and binary data by detecting the type
41/// and formatting appropriately. Binary data is converted to hex representation.
42///
43/// # Arguments
44/// * `data` - The JSON value containing the data to convert
45///
46/// # Returns
47/// * For text data: the original string
48/// * For binary data: hex-encoded string with "HEX:" prefix
49/// * For null data: "null" string
50/// * For other types: JSON string representation
51///
52/// # Examples
53/// ```
54/// use serde_json::json;
55/// use agentsight_capture::analyzers::common::data_to_string;
56///
57/// let text_data = json!("Hello World");
58/// assert_eq!(data_to_string(&text_data), "Hello World");
59///
60/// let binary_data = json!("\x00\x01\x02");
61/// assert!(data_to_string(&binary_data).starts_with("HEX:"));
62/// ```
63pub fn data_to_string(data: &Value) -> String {
64    match data {
65        Value::String(s) => {
66            // Check if string contains valid UTF-8 text or binary data
67            if detect_data_type(s) == "text" {
68                s.clone()
69            } else {
70                // Convert to hex if it contains control characters (likely binary)
71                format!("HEX:{}", hex::encode(s.as_bytes()))
72            }
73        }
74        Value::Null => "null".to_string(),
75        _ => data.to_string(),
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use serde_json::json;
83
84    #[test]
85    fn test_detect_data_type() {
86        // Test text data detection
87        assert_eq!(detect_data_type("Hello World"), "text");
88        assert_eq!(detect_data_type("HTTP/1.1 200 OK\r\n"), "text");
89        assert_eq!(detect_data_type("JSON: {\"key\": \"value\"}\n"), "text");
90        assert_eq!(detect_data_type("Line1\nLine2\tTabbed"), "text");
91
92        // Test binary data detection (contains control characters)
93        assert_eq!(detect_data_type("\x00\x01\x02binary"), "binary");
94        assert_eq!(detect_data_type("text\x00with\x01null"), "binary");
95        assert_eq!(detect_data_type("\x1b[31mANSI\x1b[0m"), "binary");
96
97        // Test edge cases
98        assert_eq!(detect_data_type(""), "text"); // Empty string is text
99        assert_eq!(detect_data_type("\r\n\t"), "text"); // Only allowed control chars
100    }
101
102    #[test]
103    fn test_data_to_string() {
104        // Test text data
105        let text_value = json!("Hello World");
106        assert_eq!(data_to_string(&text_value), "Hello World");
107
108        // Test binary data
109        let binary_value = json!("\x00\x01\x02binary");
110        let result = data_to_string(&binary_value);
111        assert!(result.starts_with("HEX:"));
112        assert!(result.contains("000102"));
113
114        // Test null value
115        let null_value = json!(null);
116        assert_eq!(data_to_string(&null_value), "null");
117
118        // Test other types
119        let number_value = json!(42);
120        assert_eq!(data_to_string(&number_value), "42");
121    }
122
123    #[test]
124    fn test_edge_cases() {
125        // Empty string
126        assert_eq!(detect_data_type(""), "text");
127        assert_eq!(data_to_string(&json!("")), "");
128
129        // Only control characters
130        assert_eq!(detect_data_type("\r\n\t"), "text");
131        assert_eq!(detect_data_type("\x00"), "binary");
132
133        // Mixed content
134        assert_eq!(detect_data_type("Hello\x00World"), "binary");
135        assert_eq!(detect_data_type("Hello\nWorld"), "text");
136    }
137}