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 result.malicious_patterns = analyze_malicious_patterns(analysis);
88
89 result.build_anomalies = analyze_build_anomalies(analysis);
91
92 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 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 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 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 patterns.extend(detect_obfuscation_patterns(analysis));
226
227 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 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 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 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 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 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 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 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 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 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 if let Some(metadata) = analysis.metadata.as_object() {
501 if let Some(compiler_info) = metadata.get("compiler") {
503 let compiler_str = compiler_info.to_string().to_lowercase();
504
505 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 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 let current_time = Utc::now();
554 let creation_time = analysis.created_at;
555
556 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 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 for library in &analysis.linked_libraries {
619 if let Some(issue) = check_library_vulnerabilities(library) {
620 issues.push(issue);
621 }
622 }
623
624 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 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 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 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
753fn is_suspicious_url(url: &str) -> bool {
756 let lower = url.to_lowercase();
757
758 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 let suspicious_patterns = [
768 "bit.ly",
769 "tinyurl",
770 "goo.gl",
771 "t.co", "no-ip.org",
773 "dyndns.org", "ngrok.io",
775 "localtunnel.me", ];
777
778 for pattern in &suspicious_patterns {
779 if lower.contains(pattern) {
780 return true;
781 }
782 }
783
784 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 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 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}