nabla_cli/enterprise/secure/
supply_chain.rs

1#![allow(dead_code)]
2use crate::binary::BinaryAnalysis;
3use crate::enterprise::types::{CodeLocation, ConfidenceLevel, SeverityLevel};
4use chrono::Utc;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use uuid::Uuid;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SupplyChainAnalysisResult {
11    pub analysis_id: Uuid,
12    pub file_path: String,
13    pub malicious_patterns: Vec<MaliciousPattern>,
14    pub build_anomalies: Vec<BuildAnomaly>,
15    pub dependency_issues: Vec<DependencyIssue>,
16    pub analysis_duration_ms: u64,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct MaliciousPattern {
21    pub pattern_id: String,
22    pub pattern_type: MaliciousPatternType,
23    pub location: CodeLocation,
24    pub confidence: ConfidenceLevel,
25    pub description: String,
26    pub yara_rule: Option<String>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub enum MaliciousPatternType {
31    KnownMalware,
32    Backdoor,
33    Obfuscation,
34    AntiAnalysis,
35    SuspiciousString,
36    HiddenFunctionality,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct BuildAnomaly {
41    pub anomaly_type: BuildAnomalyType,
42    pub description: String,
43    pub severity: SeverityLevel,
44    pub metadata: HashMap<String, serde_json::Value>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub enum BuildAnomalyType {
49    UnexpectedCompiler,
50    SuspiciousBuildFlags,
51    ModifiedTimestamps,
52    UnknownToolchain,
53    CompromisedBuildEnvironment,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct DependencyIssue {
58    pub dependency_name: String,
59    pub issue_type: DependencyIssueType,
60    pub severity: SeverityLevel,
61    pub description: String,
62    pub source_location: Option<String>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub enum DependencyIssueType {
67    Outdated,
68    Vulnerable,
69    Suspicious,
70    Malicious,
71    Unlicensed,
72}
73
74pub fn analyze_supply_chain_security(analysis: &BinaryAnalysis) -> SupplyChainAnalysisResult {
75    let start_time = Utc::now();
76
77    let mut result = SupplyChainAnalysisResult {
78        analysis_id: Uuid::new_v4(),
79        file_path: analysis.file_name.clone(),
80        malicious_patterns: Vec::new(),
81        build_anomalies: Vec::new(),
82        dependency_issues: Vec::new(),
83        analysis_duration_ms: 0,
84    };
85
86    // Analyze for malicious patterns and indicators
87    result.malicious_patterns = analyze_malicious_patterns(analysis);
88
89    // Analyze build metadata for anomalies
90    result.build_anomalies = analyze_build_anomalies(analysis);
91
92    // Analyze dependencies for security issues
93    result.dependency_issues = analyze_dependency_issues(analysis);
94
95    let end_time = Utc::now();
96    result.analysis_duration_ms = (end_time - start_time).num_milliseconds() as u64;
97
98    result
99}
100
101fn analyze_malicious_patterns(analysis: &BinaryAnalysis) -> Vec<MaliciousPattern> {
102    let mut patterns = Vec::new();
103
104    // Known malware function signatures
105    let malware_functions: HashMap<&str, (MaliciousPatternType, ConfidenceLevel, &str)> =
106        HashMap::from([
107            (
108                "CreateRemoteThread",
109                (
110                    MaliciousPatternType::KnownMalware,
111                    ConfidenceLevel::High,
112                    "Process injection technique",
113                ),
114            ),
115            (
116                "WriteProcessMemory",
117                (
118                    MaliciousPatternType::KnownMalware,
119                    ConfidenceLevel::High,
120                    "Memory manipulation for injection",
121                ),
122            ),
123            (
124                "VirtualAllocEx",
125                (
126                    MaliciousPatternType::KnownMalware,
127                    ConfidenceLevel::Medium,
128                    "Remote memory allocation",
129                ),
130            ),
131            (
132                "SetWindowsHookEx",
133                (
134                    MaliciousPatternType::KnownMalware,
135                    ConfidenceLevel::Medium,
136                    "Windows hook injection",
137                ),
138            ),
139            (
140                "CreateToolhelp32Snapshot",
141                (
142                    MaliciousPatternType::AntiAnalysis,
143                    ConfidenceLevel::Medium,
144                    "Process enumeration",
145                ),
146            ),
147            (
148                "Module32First",
149                (
150                    MaliciousPatternType::AntiAnalysis,
151                    ConfidenceLevel::Medium,
152                    "Module enumeration",
153                ),
154            ),
155            (
156                "NtQuerySystemInformation",
157                (
158                    MaliciousPatternType::AntiAnalysis,
159                    ConfidenceLevel::High,
160                    "System information gathering",
161                ),
162            ),
163            (
164                "ZwQuerySystemInformation",
165                (
166                    MaliciousPatternType::AntiAnalysis,
167                    ConfidenceLevel::High,
168                    "Low-level system queries",
169                ),
170            ),
171            (
172                "RtlAdjustPrivilege",
173                (
174                    MaliciousPatternType::KnownMalware,
175                    ConfidenceLevel::High,
176                    "Privilege escalation",
177                ),
178            ),
179            (
180                "NtTerminateProcess",
181                (
182                    MaliciousPatternType::AntiAnalysis,
183                    ConfidenceLevel::Medium,
184                    "Process termination",
185                ),
186            ),
187        ]);
188
189    // Check imports and symbols for malicious functions
190    for item in analysis
191        .imports
192        .iter()
193        .chain(analysis.detected_symbols.iter())
194    {
195        if let Some((pattern_type, confidence, description)) = malware_functions.get(item.as_str())
196        {
197            patterns.push(MaliciousPattern {
198                pattern_id: format!("FUNC_{}", item),
199                pattern_type: pattern_type.clone(),
200                location: CodeLocation {
201                    file_path: analysis.file_name.clone(),
202                    line_number: None,
203                    column_number: None,
204                    function_name: Some(item.clone()),
205                    binary_offset: None,
206                },
207                confidence: confidence.clone(),
208                description: format!("Suspicious function: {} - {}", item, description),
209                yara_rule: Some(format!(
210                    "rule {} {{ strings: $func = \"{}\" condition: $func }}",
211                    item, item
212                )),
213            });
214        }
215    }
216
217    // Analyze embedded strings for malicious indicators
218    for string in &analysis.embedded_strings {
219        if let Some(pattern) = analyze_string_for_malicious_content(string, &analysis.file_name) {
220            patterns.push(pattern);
221        }
222    }
223
224    // Check for obfuscation patterns
225    patterns.extend(detect_obfuscation_patterns(analysis));
226
227    // Check for backdoor indicators
228    patterns.extend(detect_backdoor_patterns(analysis));
229
230    patterns
231}
232
233fn analyze_string_for_malicious_content(string: &str, file_path: &str) -> Option<MaliciousPattern> {
234    let lower = string.to_lowercase();
235
236    // Suspicious file paths and registry keys
237    let suspicious_paths = [
238        ("\\temp\\", "Temporary directory usage"),
239        ("\\appdata\\roaming\\", "User roaming directory"),
240        ("\\programdata\\", "System-wide data directory"),
241        ("hkey_current_user", "Registry manipulation"),
242        ("hkey_local_machine", "System registry access"),
243        ("\\system32\\", "System directory access"),
244        ("\\syswow64\\", "System directory access"),
245    ];
246
247    for (path, description) in &suspicious_paths {
248        if lower.contains(path) {
249            return Some(MaliciousPattern {
250                pattern_id: format!("PATH_{}", path.replace("\\", "_")),
251                pattern_type: MaliciousPatternType::SuspiciousString,
252                location: CodeLocation {
253                    file_path: file_path.to_string(),
254                    line_number: None,
255                    column_number: None,
256                    function_name: None,
257                    binary_offset: None,
258                },
259                confidence: ConfidenceLevel::Medium,
260                description: format!("Suspicious path reference: {}", description),
261                yara_rule: Some(format!(
262                    "rule SuspiciousPath {{ strings: $path = \"{}\" nocase condition: $path }}",
263                    path
264                )),
265            });
266        }
267    }
268
269    // Suspicious commands and executables
270    let suspicious_commands = [
271        ("cmd.exe", "Command prompt execution"),
272        ("powershell.exe", "PowerShell execution"),
273        ("rundll32.exe", "DLL execution"),
274        ("regsvr32.exe", "DLL registration"),
275        ("schtasks.exe", "Task scheduler"),
276        ("net.exe", "Network commands"),
277        ("netsh.exe", "Network shell"),
278    ];
279
280    for (cmd, description) in &suspicious_commands {
281        if lower.contains(cmd) {
282            return Some(MaliciousPattern {
283                pattern_id: format!("CMD_{}", cmd.replace(".", "_")),
284                pattern_type: MaliciousPatternType::SuspiciousString,
285                location: CodeLocation {
286                    file_path: file_path.to_string(),
287                    line_number: None,
288                    column_number: None,
289                    function_name: None,
290                    binary_offset: None,
291                },
292                confidence: ConfidenceLevel::Medium,
293                description: format!("Suspicious command reference: {}", description),
294                yara_rule: Some(format!(
295                    "rule SuspiciousCommand {{ strings: $cmd = \"{}\" nocase condition: $cmd }}",
296                    cmd
297                )),
298            });
299        }
300    }
301
302    // Network-related suspicious strings
303    let network_indicators = [
304        ("http://", "HTTP communication"),
305        ("https://", "HTTPS communication"),
306        ("ftp://", "FTP communication"),
307        ("tcp://", "TCP communication"),
308        ("udp://", "UDP communication"),
309    ];
310
311    for (indicator, description) in &network_indicators {
312        if string.contains(indicator) && string.len() > 10 {
313            // Check if it looks like a suspicious URL
314            if is_suspicious_url(string) {
315                return Some(MaliciousPattern {
316                    pattern_id: "SUSPICIOUS_URL".to_string(),
317                    pattern_type: MaliciousPatternType::SuspiciousString,
318                    location: CodeLocation {
319                        file_path: file_path.to_string(),
320                        line_number: None,
321                        column_number: None,
322                        function_name: None,
323                        binary_offset: None,
324                    },
325                    confidence: ConfidenceLevel::High,
326                    description: format!("Suspicious URL detected: {}", description),
327                    yara_rule: Some("rule SuspiciousURL { strings: $url = /https?:\\/\\/[^\\s]+/ condition: $url }".to_string()),
328                });
329            }
330        }
331    }
332
333    // Check for encoded/obfuscated content
334    if is_likely_obfuscated_string(string) {
335        return Some(MaliciousPattern {
336            pattern_id: "OBFUSCATED_STRING".to_string(),
337            pattern_type: MaliciousPatternType::Obfuscation,
338            location: CodeLocation {
339                file_path: file_path.to_string(),
340                line_number: None,
341                column_number: None,
342                function_name: None,
343                binary_offset: None,
344            },
345            confidence: ConfidenceLevel::Medium,
346            description: "Potentially obfuscated string detected".to_string(),
347            yara_rule: None,
348        });
349    }
350
351    None
352}
353
354fn detect_obfuscation_patterns(analysis: &BinaryAnalysis) -> Vec<MaliciousPattern> {
355    let mut patterns = Vec::new();
356
357    // Check for packing/obfuscation indicators
358    let packer_strings = [
359        "upx",
360        "aspack",
361        "pecompact",
362        "petite",
363        "nspack",
364        "fsg",
365        "mpress",
366        "themida",
367        "vmprotect",
368    ];
369
370    for string in &analysis.embedded_strings {
371        let lower = string.to_lowercase();
372        for packer in &packer_strings {
373            if lower.contains(packer) {
374                patterns.push(MaliciousPattern {
375                    pattern_id: format!("PACKER_{}", packer.to_uppercase()),
376                    pattern_type: MaliciousPatternType::Obfuscation,
377                    location: CodeLocation {
378                        file_path: analysis.file_name.clone(),
379                        line_number: None,
380                        column_number: None,
381                        function_name: None,
382                        binary_offset: None,
383                    },
384                    confidence: ConfidenceLevel::High,
385                    description: format!("Packer/Obfuscator detected: {}", packer),
386                    yara_rule: Some(format!(
387                        "rule Packer_{} {{ strings: $packer = \"{}\" nocase condition: $packer }}",
388                        packer, packer
389                    )),
390                });
391                break;
392            }
393        }
394    }
395
396    // Check for high entropy sections (potential packing)
397    if analysis.embedded_strings.len() < 5 && analysis.size_bytes > 1024 {
398        patterns.push(MaliciousPattern {
399            pattern_id: "LOW_STRING_COUNT".to_string(),
400            pattern_type: MaliciousPatternType::Obfuscation,
401            location: CodeLocation {
402                file_path: analysis.file_name.clone(),
403                line_number: None,
404                column_number: None,
405                function_name: None,
406                binary_offset: None,
407            },
408            confidence: ConfidenceLevel::Medium,
409            description: "Unusually low string count for binary size - possible packing"
410                .to_string(),
411            yara_rule: None,
412        });
413    }
414
415    patterns
416}
417
418fn detect_backdoor_patterns(analysis: &BinaryAnalysis) -> Vec<MaliciousPattern> {
419    let mut patterns = Vec::new();
420
421    // Check for backdoor-related network ports
422    let backdoor_ports = [
423        "31337", "12345", "54321", "4444", "5555", "6666", "7777", "8888", "9999",
424    ];
425
426    for string in &analysis.embedded_strings {
427        for port in &backdoor_ports {
428            if string.contains(port) {
429                patterns.push(MaliciousPattern {
430                    pattern_id: format!("BACKDOOR_PORT_{}", port),
431                    pattern_type: MaliciousPatternType::Backdoor,
432                    location: CodeLocation {
433                        file_path: analysis.file_name.clone(),
434                        line_number: None,
435                        column_number: None,
436                        function_name: None,
437                        binary_offset: None,
438                    },
439                    confidence: ConfidenceLevel::High,
440                    description: format!("Backdoor port {} detected", port),
441                    yara_rule: Some(format!(
442                        "rule BackdoorPort_{} {{ strings: $port = \"{}\" condition: $port }}",
443                        port, port
444                    )),
445                });
446            }
447        }
448    }
449
450    // Check for remote access tool (RAT) indicators
451    let rat_indicators = [
452        "remote desktop",
453        "vnc",
454        "teamviewer",
455        "anydesk",
456        "logmein",
457        "pcanyware",
458        "remote access",
459        "backdoor",
460        "trojan",
461        "rat",
462    ];
463
464    for string in &analysis.embedded_strings {
465        let lower = string.to_lowercase();
466        for indicator in &rat_indicators {
467            if lower.contains(indicator) {
468                patterns.push(MaliciousPattern {
469                    pattern_id: format!(
470                        "RAT_INDICATOR_{}",
471                        indicator.replace(" ", "_").to_uppercase()
472                    ),
473                    pattern_type: MaliciousPatternType::Backdoor,
474                    location: CodeLocation {
475                        file_path: analysis.file_name.clone(),
476                        line_number: None,
477                        column_number: None,
478                        function_name: None,
479                        binary_offset: None,
480                    },
481                    confidence: ConfidenceLevel::Medium,
482                    description: format!("Remote access tool indicator: {}", indicator),
483                    yara_rule: Some(format!(
484                        "rule RAT_Indicator {{ strings: $rat = \"{}\" nocase condition: $rat }}",
485                        indicator
486                    )),
487                });
488                break;
489            }
490        }
491    }
492
493    patterns
494}
495
496fn analyze_build_anomalies(analysis: &BinaryAnalysis) -> Vec<BuildAnomaly> {
497    let mut anomalies = Vec::new();
498
499    // Analyze build metadata if available
500    if let Some(metadata) = analysis.metadata.as_object() {
501        // Check for suspicious compiler information
502        if let Some(compiler_info) = metadata.get("compiler") {
503            let compiler_str = compiler_info.to_string().to_lowercase();
504
505            // Check for unexpected compilers
506            let suspicious_compilers = ["tcc", "lcc", "dmc", "unknown_compiler"];
507            for compiler in &suspicious_compilers {
508                if compiler_str.contains(compiler) {
509                    let mut metadata_map = HashMap::new();
510                    metadata_map.insert("compiler".to_string(), compiler_info.clone());
511
512                    anomalies.push(BuildAnomaly {
513                        anomaly_type: BuildAnomalyType::UnexpectedCompiler,
514                        description: format!("Unexpected compiler detected: {}", compiler),
515                        severity: SeverityLevel::Medium,
516                        metadata: metadata_map,
517                    });
518                }
519            }
520        }
521
522        // Check for suspicious build flags
523        if let Some(build_flags) = metadata.get("build_flags") {
524            let flags_str = build_flags.to_string().to_lowercase();
525
526            let suspicious_flags = [
527                ("-fno-stack-protector", "Stack protection disabled"),
528                ("-z execstack", "Executable stack enabled"),
529                ("-fno-pie", "Position independent executable disabled"),
530                ("--disable-relro", "RELRO protection disabled"),
531            ];
532
533            for (flag, description) in &suspicious_flags {
534                if flags_str.contains(flag) {
535                    let mut metadata_map = HashMap::new();
536                    metadata_map.insert(
537                        "suspicious_flag".to_string(),
538                        serde_json::Value::String(flag.to_string()),
539                    );
540
541                    anomalies.push(BuildAnomaly {
542                        anomaly_type: BuildAnomalyType::SuspiciousBuildFlags,
543                        description: format!("Suspicious build flag: {} - {}", flag, description),
544                        severity: SeverityLevel::High,
545                        metadata: metadata_map,
546                    });
547                }
548            }
549        }
550    }
551
552    // Check timestamp anomalies
553    let current_time = Utc::now();
554    let creation_time = analysis.created_at;
555
556    // Check if binary claims to be from the future
557    if creation_time > current_time {
558        let mut metadata_map = HashMap::new();
559        metadata_map.insert(
560            "creation_time".to_string(),
561            serde_json::Value::String(creation_time.to_rfc3339()),
562        );
563        metadata_map.insert(
564            "current_time".to_string(),
565            serde_json::Value::String(current_time.to_rfc3339()),
566        );
567
568        anomalies.push(BuildAnomaly {
569            anomaly_type: BuildAnomalyType::ModifiedTimestamps,
570            description: "Binary timestamp is in the future".to_string(),
571            severity: SeverityLevel::Medium,
572            metadata: metadata_map,
573        });
574    }
575
576    // Check for signs of compromised build environment
577    let compromised_indicators = [
578        "/tmp/",
579        "/var/tmp/",
580        "C:\\Temp\\",
581        "C:\\Windows\\Temp\\",
582        "BuildAgent",
583        "jenkins",
584        "bamboo",
585        "teamcity",
586    ];
587
588    for string in &analysis.embedded_strings {
589        for indicator in &compromised_indicators {
590            if string.contains(indicator) {
591                let mut metadata_map = HashMap::new();
592                metadata_map.insert(
593                    "indicator".to_string(),
594                    serde_json::Value::String(string.clone()),
595                );
596
597                anomalies.push(BuildAnomaly {
598                    anomaly_type: BuildAnomalyType::CompromisedBuildEnvironment,
599                    description: format!(
600                        "Potential compromised build environment indicator: {}",
601                        indicator
602                    ),
603                    severity: SeverityLevel::Low,
604                    metadata: metadata_map,
605                });
606                break;
607            }
608        }
609    }
610
611    anomalies
612}
613
614fn analyze_dependency_issues(analysis: &BinaryAnalysis) -> Vec<DependencyIssue> {
615    let mut issues = Vec::new();
616
617    // Analyze linked libraries for known vulnerable versions
618    for library in &analysis.linked_libraries {
619        if let Some(issue) = check_library_vulnerabilities(library) {
620            issues.push(issue);
621        }
622    }
623
624    // Check for suspicious or malicious dependencies
625    let suspicious_lib_patterns = [
626        (
627            "trojan",
628            DependencyIssueType::Malicious,
629            "Trojan-related library name",
630        ),
631        (
632            "backdoor",
633            DependencyIssueType::Malicious,
634            "Backdoor-related library name",
635        ),
636        (
637            "malware",
638            DependencyIssueType::Malicious,
639            "Malware-related library name",
640        ),
641        (
642            "crack",
643            DependencyIssueType::Suspicious,
644            "Crack-related library",
645        ),
646        (
647            "keygen",
648            DependencyIssueType::Suspicious,
649            "Key generator library",
650        ),
651        (
652            "hack",
653            DependencyIssueType::Suspicious,
654            "Hack-related library",
655        ),
656    ];
657
658    for library in &analysis.linked_libraries {
659        let lower = library.to_lowercase();
660        for (pattern, issue_type, description) in &suspicious_lib_patterns {
661            if lower.contains(pattern) {
662                issues.push(DependencyIssue {
663                    dependency_name: library.clone(),
664                    issue_type: issue_type.clone(),
665                    severity: match issue_type {
666                        DependencyIssueType::Malicious => SeverityLevel::Critical,
667                        DependencyIssueType::Suspicious => SeverityLevel::High,
668                        _ => SeverityLevel::Medium,
669                    },
670                    description: description.to_string(),
671                    source_location: Some(analysis.file_name.clone()),
672                });
673                break;
674            }
675        }
676    }
677
678    // Check for outdated system libraries (basic heuristics)
679    let system_libraries = [
680        ("libc.so.6", "System C library"),
681        ("libssl.so", "OpenSSL library"),
682        ("libcrypto.so", "OpenSSL crypto library"),
683        ("libcurl.so", "cURL library"),
684        ("libxml2.so", "XML library"),
685    ];
686
687    for (lib_pattern, description) in &system_libraries {
688        for library in &analysis.linked_libraries {
689            if library.contains(lib_pattern) {
690                // Very basic version checking - look for old version patterns
691                if library.contains(".0.") || library.contains("1.0") {
692                    issues.push(DependencyIssue {
693                        dependency_name: library.clone(),
694                        issue_type: DependencyIssueType::Outdated,
695                        severity: SeverityLevel::Medium,
696                        description: format!("Potentially outdated {}: {}", description, library),
697                        source_location: Some(analysis.file_name.clone()),
698                    });
699                }
700                break;
701            }
702        }
703    }
704
705    issues
706}
707
708fn check_library_vulnerabilities(library: &str) -> Option<DependencyIssue> {
709    // Known vulnerable library versions (simplified examples)
710    let vulnerable_libraries: HashMap<&str, (DependencyIssueType, SeverityLevel, &str)> =
711        HashMap::from([
712            (
713                "libssl.so.1.0.0",
714                (
715                    DependencyIssueType::Vulnerable,
716                    SeverityLevel::High,
717                    "OpenSSL 1.0.0 has known vulnerabilities",
718                ),
719            ),
720            (
721                "libcurl.so.3",
722                (
723                    DependencyIssueType::Vulnerable,
724                    SeverityLevel::Medium,
725                    "Old cURL version may have vulnerabilities",
726                ),
727            ),
728            (
729                "libxml2.so.2.6",
730                (
731                    DependencyIssueType::Vulnerable,
732                    SeverityLevel::Medium,
733                    "Old libxml2 version",
734                ),
735            ),
736        ]);
737
738    for (vuln_lib, (issue_type, severity, description)) in &vulnerable_libraries {
739        if library.contains(vuln_lib) {
740            return Some(DependencyIssue {
741                dependency_name: library.to_string(),
742                issue_type: issue_type.clone(),
743                severity: severity.clone(),
744                description: description.to_string(),
745                source_location: None,
746            });
747        }
748    }
749
750    None
751}
752
753// Helper functions
754
755fn is_suspicious_url(url: &str) -> bool {
756    let lower = url.to_lowercase();
757
758    // Check for suspicious TLDs
759    let suspicious_tlds = [".tk", ".ml", ".ga", ".cf", ".bit", ".onion"];
760    for tld in &suspicious_tlds {
761        if lower.contains(tld) {
762            return true;
763        }
764    }
765
766    // Check for suspicious domain patterns
767    let suspicious_patterns = [
768        "bit.ly",
769        "tinyurl",
770        "goo.gl",
771        "t.co", // URL shorteners
772        "no-ip.org",
773        "dyndns.org", // Dynamic DNS
774        "ngrok.io",
775        "localtunnel.me", // Tunneling services
776    ];
777
778    for pattern in &suspicious_patterns {
779        if lower.contains(pattern) {
780            return true;
781        }
782    }
783
784    // Check for IP addresses instead of domains
785    if url.contains("://") {
786        if let Some(domain_start) = url.find("://") {
787            let domain_part = &url[domain_start + 3..];
788            if let Some(path_start) = domain_part.find('/') {
789                let domain = &domain_part[..path_start];
790                if is_ip_address(domain) {
791                    return true;
792                }
793            }
794        }
795    }
796
797    false
798}
799
800fn is_likely_obfuscated_string(string: &str) -> bool {
801    if string.len() < 10 {
802        return false;
803    }
804
805    // Check for high percentage of non-alphanumeric characters
806    let non_alphanum_count = string
807        .chars()
808        .filter(|c| !c.is_alphanumeric() && *c != ' ')
809        .count();
810    let non_alphanum_ratio = non_alphanum_count as f32 / string.len() as f32;
811
812    if non_alphanum_ratio > 0.3 {
813        return true;
814    }
815
816    // Check for repeated character patterns (common in obfuscation)
817    let mut char_counts = HashMap::new();
818    for c in string.chars() {
819        *char_counts.entry(c).or_insert(0) += 1;
820    }
821
822    let max_char_count = char_counts.values().max().unwrap_or(&0);
823    if *max_char_count as f32 / string.len() as f32 > 0.4 {
824        return true;
825    }
826
827    false
828}
829
830fn is_ip_address(s: &str) -> bool {
831    let parts: Vec<&str> = s.split('.').collect();
832    if parts.len() != 4 {
833        return false;
834    }
835
836    for part in parts {
837        if part.parse::<u8>().is_err() {
838            return false;
839        }
840    }
841    true
842}
843
844#[cfg(test)]
845mod tests {
846    use super::*;
847    use chrono::Utc;
848
849    fn create_test_analysis() -> BinaryAnalysis {
850        BinaryAnalysis {
851            id: Uuid::new_v4(),
852            file_name: "test.bin".to_string(),
853            format: "elf".to_string(),
854            architecture: "x86_64".to_string(),
855            languages: vec!["C".to_string()],
856            detected_symbols: vec!["CreateRemoteThread".to_string()],
857            embedded_strings: vec![
858                "31337".to_string(),
859                "upx".to_string(),
860                "http://malicious.example.com".to_string(),
861            ],
862            suspected_secrets: vec![],
863            imports: vec!["WriteProcessMemory".to_string()],
864            exports: vec![],
865            hash_sha256: "test".to_string(),
866            hash_blake3: None,
867            size_bytes: 1024,
868            linked_libraries: vec!["libssl.so.1.0.0".to_string()],
869            static_linked: false,
870            version_info: None,
871            license_info: None,
872            metadata: serde_json::json!({
873                "compiler": "unknown_compiler"
874            }),
875            created_at: Utc::now(),
876            sbom: None,
877            binary_data: Some(vec![0x7f, 0x45, 0x4c, 0x46]),
878            entry_point: Some("0x401000".to_string()),
879            code_sections: vec![],
880        }
881    }
882
883    #[test]
884    fn test_analyze_supply_chain_security() {
885        let analysis = create_test_analysis();
886        let result = analyze_supply_chain_security(&analysis);
887
888        assert_eq!(result.file_path, "test.bin");
889        assert!(!result.malicious_patterns.is_empty());
890        assert!(!result.build_anomalies.is_empty());
891        assert!(!result.dependency_issues.is_empty());
892    }
893
894    #[test]
895    fn test_is_suspicious_url() {
896        assert!(is_suspicious_url("http://malicious.tk/payload"));
897        assert!(is_suspicious_url("https://192.168.1.1/backdoor"));
898        assert!(!is_suspicious_url("https://google.com"));
899    }
900
901    #[test]
902    fn test_is_likely_obfuscated_string() {
903        assert!(is_likely_obfuscated_string("####@@@@!!!!%%%%"));
904        assert!(is_likely_obfuscated_string("aaaaaaaaaaaaaaaaaa"));
905        assert!(!is_likely_obfuscated_string("normal text string"));
906    }
907}