1use crate::parser::ast::MagicRule;
10use crate::{EvaluationConfig, LibmagicError};
11use serde::{Deserialize, Serialize};
12
13#[cfg(test)]
14use crate::parser::ast::{Endianness, OffsetSpec, Operator, TypeKind, Value};
15
16pub mod offset;
17pub mod operators;
18pub mod strength;
19pub mod types;
20
21#[derive(Debug, Clone)]
40pub struct EvaluationContext {
41 current_offset: usize,
43 recursion_depth: u32,
45 config: EvaluationConfig,
47}
48
49impl EvaluationContext {
50 #[must_use]
66 pub const fn new(config: EvaluationConfig) -> Self {
67 Self {
68 current_offset: 0,
69 recursion_depth: 0,
70 config,
71 }
72 }
73
74 #[must_use]
80 pub const fn current_offset(&self) -> usize {
81 self.current_offset
82 }
83
84 pub fn set_current_offset(&mut self, offset: usize) {
90 self.current_offset = offset;
91 }
92
93 #[must_use]
99 pub const fn recursion_depth(&self) -> u32 {
100 self.recursion_depth
101 }
102
103 pub fn increment_recursion_depth(&mut self) -> Result<(), LibmagicError> {
115 if self.recursion_depth >= self.config.max_recursion_depth {
116 return Err(LibmagicError::EvaluationError(
117 crate::error::EvaluationError::recursion_limit_exceeded(self.recursion_depth),
118 ));
119 }
120 self.recursion_depth += 1;
121 Ok(())
122 }
123
124 pub fn decrement_recursion_depth(&mut self) -> Result<(), LibmagicError> {
131 if self.recursion_depth == 0 {
132 return Err(LibmagicError::EvaluationError(
133 crate::error::EvaluationError::internal_error(
134 "Attempted to decrement recursion depth below 0",
135 ),
136 ));
137 }
138 self.recursion_depth -= 1;
139 Ok(())
140 }
141
142 #[must_use]
148 pub const fn config(&self) -> &EvaluationConfig {
149 &self.config
150 }
151
152 #[must_use]
158 pub const fn should_stop_at_first_match(&self) -> bool {
159 self.config.stop_at_first_match
160 }
161
162 #[must_use]
168 pub const fn max_string_length(&self) -> usize {
169 self.config.max_string_length
170 }
171
172 #[must_use]
178 pub const fn enable_mime_types(&self) -> bool {
179 self.config.enable_mime_types
180 }
181
182 #[must_use]
188 pub const fn timeout_ms(&self) -> Option<u64> {
189 self.config.timeout_ms
190 }
191
192 pub fn reset(&mut self) {
197 self.current_offset = 0;
198 self.recursion_depth = 0;
199 }
200}
201
202#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
207pub struct MatchResult {
208 pub message: String,
210 pub offset: usize,
212 pub level: u32,
214 pub value: crate::parser::ast::Value,
216 pub confidence: f64,
222}
223
224impl MatchResult {
225 #[must_use]
244 pub fn calculate_confidence(level: u32) -> f64 {
245 (0.3 + (f64::from(level) * 0.2)).min(1.0)
246 }
247}
248
249pub fn evaluate_single_rule(
299 rule: &MagicRule,
300 buffer: &[u8],
301) -> Result<Option<(usize, crate::parser::ast::Value)>, LibmagicError> {
302 let absolute_offset = offset::resolve_offset(&rule.offset, buffer)?;
304
305 let read_value = types::read_typed_value(buffer, absolute_offset, &rule.typ)
307 .map_err(|e| LibmagicError::EvaluationError(e.into()))?;
308
309 Ok(
311 operators::apply_operator(&rule.op, &read_value, &rule.value)
312 .then_some((absolute_offset, read_value)),
313 )
314}
315
316pub fn evaluate_rules(
391 rules: &[MagicRule],
392 buffer: &[u8],
393 context: &mut EvaluationContext,
394) -> Result<Vec<MatchResult>, LibmagicError> {
395 let mut matches = Vec::with_capacity(8);
396 let start_time = std::time::Instant::now();
397 let mut rule_count = 0u32;
398
399 for rule in rules {
400 rule_count = rule_count.wrapping_add(1);
402 if rule_count.trailing_zeros() >= 4 {
403 if let Some(timeout_ms) = context.timeout_ms() {
404 if start_time.elapsed().as_millis() > u128::from(timeout_ms) {
405 return Err(LibmagicError::Timeout { timeout_ms });
406 }
407 }
408 }
409
410 let match_data = match evaluate_single_rule(rule, buffer) {
412 Ok(data) => data,
413 Err(_e) => {
414 continue;
416 }
417 };
418
419 if let Some((absolute_offset, read_value)) = match_data {
420 let match_result = MatchResult {
421 message: rule.message.clone(),
422 offset: absolute_offset,
423 level: rule.level,
424 value: read_value,
425 confidence: MatchResult::calculate_confidence(rule.level),
426 };
427 matches.push(match_result);
428
429 if !rule.children.is_empty() {
431 context.increment_recursion_depth()?;
433
434 match evaluate_rules(&rule.children, buffer, context) {
436 Ok(child_matches) => {
437 matches.extend(child_matches);
438 }
439 Err(LibmagicError::Timeout { .. }) => {
440 context.decrement_recursion_depth()?;
442 return Err(LibmagicError::Timeout {
443 timeout_ms: context.timeout_ms().unwrap_or(0),
444 });
445 }
446 Err(LibmagicError::EvaluationError(
447 crate::error::EvaluationError::RecursionLimitExceeded { .. },
448 )) => {
449 context.decrement_recursion_depth()?;
451 return Err(LibmagicError::EvaluationError(
452 crate::error::EvaluationError::RecursionLimitExceeded {
453 depth: context.recursion_depth(),
454 },
455 ));
456 }
457 Err(_e) => {
458 }
460 }
461
462 context.decrement_recursion_depth()?;
464 }
465
466 if context.should_stop_at_first_match() {
468 break;
469 }
470 }
471 }
472
473 Ok(matches)
474}
475
476pub fn evaluate_rules_with_config(
524 rules: &[MagicRule],
525 buffer: &[u8],
526 config: &EvaluationConfig,
527) -> Result<Vec<MatchResult>, LibmagicError> {
528 let mut context = EvaluationContext::new(config.clone());
529 evaluate_rules(rules, buffer, &mut context)
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535 use crate::parser::ast::{Endianness, OffsetSpec, Operator, TypeKind, Value};
536
537 #[test]
538 fn test_evaluate_single_rule_byte_equal_match() {
539 let rule = MagicRule {
540 offset: OffsetSpec::Absolute(0),
541 typ: TypeKind::Byte,
542 op: Operator::Equal,
543 value: Value::Uint(0x7f),
544 message: "ELF magic".to_string(),
545 children: vec![],
546 level: 0,
547 strength_modifier: None,
548 };
549
550 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
552 assert!(result.is_some());
553 }
554
555 #[test]
556 fn test_evaluate_single_rule_byte_equal_no_match() {
557 let rule = MagicRule {
558 offset: OffsetSpec::Absolute(0),
559 typ: TypeKind::Byte,
560 op: Operator::Equal,
561 value: Value::Uint(0x7f),
562 message: "ELF magic".to_string(),
563 children: vec![],
564 level: 0,
565 strength_modifier: None,
566 };
567
568 let buffer = &[0x50, 0x4b, 0x03, 0x04]; let result = evaluate_single_rule(&rule, buffer).unwrap();
570 assert!(result.is_none());
571 }
572
573 #[test]
574 fn test_evaluate_single_rule_byte_not_equal_match() {
575 let rule = MagicRule {
576 offset: OffsetSpec::Absolute(0),
577 typ: TypeKind::Byte,
578 op: Operator::NotEqual,
579 value: Value::Uint(0x00),
580 message: "Non-zero byte".to_string(),
581 children: vec![],
582 level: 0,
583 strength_modifier: None,
584 };
585
586 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
587 let result = evaluate_single_rule(&rule, buffer).unwrap();
588 assert!(result.is_some()); }
590
591 #[test]
592 fn test_evaluate_single_rule_byte_not_equal_no_match() {
593 let rule = MagicRule {
594 offset: OffsetSpec::Absolute(0),
595 typ: TypeKind::Byte,
596 op: Operator::NotEqual,
597 value: Value::Uint(0x7f),
598 message: "Not ELF magic".to_string(),
599 children: vec![],
600 level: 0,
601 strength_modifier: None,
602 };
603
604 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
605 let result = evaluate_single_rule(&rule, buffer).unwrap();
606 assert!(result.is_none()); }
608
609 #[test]
610 fn test_evaluate_single_rule_byte_bitwise_and_match() {
611 let rule = MagicRule {
612 offset: OffsetSpec::Absolute(0),
613 typ: TypeKind::Byte,
614 op: Operator::BitwiseAnd,
615 value: Value::Uint(0x80), message: "High bit set".to_string(),
617 children: vec![],
618 level: 0,
619 strength_modifier: None,
620 };
621
622 let buffer = &[0xff, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
624 assert!(result.is_some()); }
626
627 #[test]
628 fn test_evaluate_single_rule_byte_bitwise_and_no_match() {
629 let rule = MagicRule {
630 offset: OffsetSpec::Absolute(0),
631 typ: TypeKind::Byte,
632 op: Operator::BitwiseAnd,
633 value: Value::Uint(0x80), message: "High bit set".to_string(),
635 children: vec![],
636 level: 0,
637 strength_modifier: None,
638 };
639
640 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
642 assert!(result.is_none()); }
644
645 #[test]
646 fn test_evaluate_single_rule_short_little_endian() {
647 let rule = MagicRule {
648 offset: OffsetSpec::Absolute(0),
649 typ: TypeKind::Short {
650 endian: Endianness::Little,
651 signed: false,
652 },
653 op: Operator::Equal,
654 value: Value::Uint(0x1234),
655 message: "Little-endian short".to_string(),
656 children: vec![],
657 level: 0,
658 strength_modifier: None,
659 };
660
661 let buffer = &[0x34, 0x12, 0x56, 0x78]; let result = evaluate_single_rule(&rule, buffer).unwrap();
663 assert!(result.is_some());
664 }
665
666 #[test]
667 fn test_evaluate_single_rule_short_big_endian() {
668 let rule = MagicRule {
669 offset: OffsetSpec::Absolute(0),
670 typ: TypeKind::Short {
671 endian: Endianness::Big,
672 signed: false,
673 },
674 op: Operator::Equal,
675 value: Value::Uint(0x1234),
676 message: "Big-endian short".to_string(),
677 children: vec![],
678 level: 0,
679 strength_modifier: None,
680 };
681
682 let buffer = &[0x12, 0x34, 0x56, 0x78]; let result = evaluate_single_rule(&rule, buffer).unwrap();
684 assert!(result.is_some());
685 }
686
687 #[test]
688 fn test_evaluate_single_rule_short_signed_positive() {
689 let rule = MagicRule {
690 offset: OffsetSpec::Absolute(0),
691 typ: TypeKind::Short {
692 endian: Endianness::Little,
693 signed: true,
694 },
695 op: Operator::Equal,
696 value: Value::Int(32767), message: "Positive signed short".to_string(),
698 children: vec![],
699 level: 0,
700 strength_modifier: None,
701 };
702
703 let buffer = &[0xff, 0x7f, 0x00, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
705 assert!(result.is_some());
706 }
707
708 #[test]
709 fn test_evaluate_single_rule_short_signed_negative() {
710 let rule = MagicRule {
711 offset: OffsetSpec::Absolute(0),
712 typ: TypeKind::Short {
713 endian: Endianness::Little,
714 signed: true,
715 },
716 op: Operator::Equal,
717 value: Value::Int(-1), message: "Negative signed short".to_string(),
719 children: vec![],
720 level: 0,
721 strength_modifier: None,
722 };
723
724 let buffer = &[0xff, 0xff, 0x00, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
726 assert!(result.is_some());
727 }
728
729 #[test]
730 fn test_evaluate_single_rule_long_little_endian() {
731 let rule = MagicRule {
732 offset: OffsetSpec::Absolute(0),
733 typ: TypeKind::Long {
734 endian: Endianness::Little,
735 signed: false,
736 },
737 op: Operator::Equal,
738 value: Value::Uint(0x1234_5678),
739 message: "Little-endian long".to_string(),
740 children: vec![],
741 level: 0,
742 strength_modifier: None,
743 };
744
745 let buffer = &[0x78, 0x56, 0x34, 0x12, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
747 assert!(result.is_some());
748 }
749
750 #[test]
751 fn test_evaluate_single_rule_long_big_endian() {
752 let rule = MagicRule {
753 offset: OffsetSpec::Absolute(0),
754 typ: TypeKind::Long {
755 endian: Endianness::Big,
756 signed: false,
757 },
758 op: Operator::Equal,
759 value: Value::Uint(0x1234_5678),
760 message: "Big-endian long".to_string(),
761 children: vec![],
762 level: 0,
763 strength_modifier: None,
764 };
765
766 let buffer = &[0x12, 0x34, 0x56, 0x78, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
768 assert!(result.is_some());
769 }
770
771 #[test]
772 fn test_evaluate_single_rule_long_signed_positive() {
773 let rule = MagicRule {
774 offset: OffsetSpec::Absolute(0),
775 typ: TypeKind::Long {
776 endian: Endianness::Little,
777 signed: true,
778 },
779 op: Operator::Equal,
780 value: Value::Int(2_147_483_647), message: "Positive signed long".to_string(),
782 children: vec![],
783 level: 0,
784 strength_modifier: None,
785 };
786
787 let buffer = &[0xff, 0xff, 0xff, 0x7f, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
789 assert!(result.is_some());
790 }
791
792 #[test]
793 fn test_evaluate_single_rule_long_signed_negative() {
794 let rule = MagicRule {
795 offset: OffsetSpec::Absolute(0),
796 typ: TypeKind::Long {
797 endian: Endianness::Little,
798 signed: true,
799 },
800 op: Operator::Equal,
801 value: Value::Int(-1), message: "Negative signed long".to_string(),
803 children: vec![],
804 level: 0,
805 strength_modifier: None,
806 };
807
808 let buffer = &[0xff, 0xff, 0xff, 0xff, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
810 assert!(result.is_some());
811 }
812
813 #[test]
814 fn test_evaluate_single_rule_different_offsets() {
815 let rule = MagicRule {
816 offset: OffsetSpec::Absolute(2), typ: TypeKind::Byte,
818 op: Operator::Equal,
819 value: Value::Uint(0x4c),
820 message: "ELF class byte".to_string(),
821 children: vec![],
822 level: 0,
823 strength_modifier: None,
824 };
825
826 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
828 assert!(result.is_some()); }
830
831 #[test]
832 fn test_evaluate_single_rule_negative_offset() {
833 let rule = MagicRule {
834 offset: OffsetSpec::Absolute(-1), typ: TypeKind::Byte,
836 op: Operator::Equal,
837 value: Value::Uint(0x46),
838 message: "Last byte".to_string(),
839 children: vec![],
840 level: 0,
841 strength_modifier: None,
842 };
843
844 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
846 assert!(result.is_some()); }
848
849 #[test]
850 fn test_evaluate_single_rule_from_end_offset() {
851 let rule = MagicRule {
852 offset: OffsetSpec::FromEnd(-2), typ: TypeKind::Byte,
854 op: Operator::Equal,
855 value: Value::Uint(0x4c),
856 message: "Second to last byte".to_string(),
857 children: vec![],
858 level: 0,
859 strength_modifier: None,
860 };
861
862 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
864 assert!(result.is_some()); }
866
867 #[test]
868 fn test_evaluate_single_rule_offset_out_of_bounds() {
869 let rule = MagicRule {
870 offset: OffsetSpec::Absolute(10), typ: TypeKind::Byte,
872 op: Operator::Equal,
873 value: Value::Uint(0x00),
874 message: "Out of bounds".to_string(),
875 children: vec![],
876 level: 0,
877 strength_modifier: None,
878 };
879
880 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer);
882 assert!(result.is_err());
883
884 match result.unwrap_err() {
885 LibmagicError::EvaluationError(msg) => {
886 let error_string = format!("{msg}");
887 assert!(error_string.contains("Buffer overrun"));
888 }
889 _ => panic!("Expected EvaluationError"),
890 }
891 }
892
893 #[test]
894 fn test_evaluate_single_rule_short_insufficient_bytes() {
895 let rule = MagicRule {
896 offset: OffsetSpec::Absolute(3), typ: TypeKind::Short {
898 endian: Endianness::Little,
899 signed: false,
900 },
901 op: Operator::Equal,
902 value: Value::Uint(0x1234),
903 message: "Insufficient bytes".to_string(),
904 children: vec![],
905 level: 0,
906 strength_modifier: None,
907 };
908
909 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer);
911 assert!(result.is_err());
912
913 match result.unwrap_err() {
914 LibmagicError::EvaluationError(msg) => {
915 let error_string = format!("{msg}");
916 assert!(error_string.contains("Buffer overrun"));
917 }
918 _ => panic!("Expected EvaluationError"),
919 }
920 }
921
922 #[test]
923 fn test_evaluate_single_rule_long_insufficient_bytes() {
924 let rule = MagicRule {
925 offset: OffsetSpec::Absolute(2), typ: TypeKind::Long {
927 endian: Endianness::Little,
928 signed: false,
929 },
930 op: Operator::Equal,
931 value: Value::Uint(0x1234_5678),
932 message: "Insufficient bytes".to_string(),
933 children: vec![],
934 level: 0,
935 strength_modifier: None,
936 };
937
938 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer);
940 assert!(result.is_err());
941
942 match result.unwrap_err() {
943 LibmagicError::EvaluationError(msg) => {
944 let error_string = format!("{msg}");
945 assert!(error_string.contains("Buffer overrun"));
946 }
947 _ => panic!("Expected EvaluationError"),
948 }
949 }
950
951 #[test]
952 fn test_evaluate_single_rule_empty_buffer() {
953 let rule = MagicRule {
954 offset: OffsetSpec::Absolute(0),
955 typ: TypeKind::Byte,
956 op: Operator::Equal,
957 value: Value::Uint(0x00),
958 message: "Empty buffer".to_string(),
959 children: vec![],
960 level: 0,
961 strength_modifier: None,
962 };
963
964 let buffer = &[]; let result = evaluate_single_rule(&rule, buffer);
966 assert!(result.is_err());
967
968 match result.unwrap_err() {
969 LibmagicError::EvaluationError(msg) => {
970 let error_string = format!("{msg}");
971 assert!(error_string.contains("Buffer overrun"));
972 }
973 _ => panic!("Expected EvaluationError"),
974 }
975 }
976
977 #[test]
978 fn test_evaluate_single_rule_string_type_supported() {
979 let rule = MagicRule {
980 offset: OffsetSpec::Absolute(0),
981 typ: TypeKind::String { max_length: None },
982 op: Operator::Equal,
983 value: Value::String("test".to_string()),
984 message: "String type".to_string(),
985 children: vec![],
986 level: 0,
987 strength_modifier: None,
988 };
989
990 let buffer = b"test\x00 data";
992 let result = evaluate_single_rule(&rule, buffer);
993 assert!(result.is_ok());
994 let matches = result.unwrap();
995 assert!(matches.is_some()); let rule_no_match = MagicRule {
999 offset: OffsetSpec::Absolute(0),
1000 typ: TypeKind::String { max_length: None },
1001 op: Operator::Equal,
1002 value: Value::String("hello".to_string()),
1003 message: "String type".to_string(),
1004 children: vec![],
1005 level: 0,
1006 strength_modifier: None,
1007 };
1008
1009 let result = evaluate_single_rule(&rule_no_match, buffer);
1010 assert!(result.is_ok());
1011 let matches = result.unwrap();
1012 assert!(matches.is_none()); }
1014}
1015
1016#[test]
1017fn test_evaluate_single_rule_cross_type_comparison() {
1018 let rule = MagicRule {
1020 offset: OffsetSpec::Absolute(0),
1021 typ: TypeKind::Byte,
1022 op: Operator::Equal,
1023 value: Value::Int(42), message: "Cross-type comparison".to_string(),
1025 children: vec![],
1026 level: 0,
1027 strength_modifier: None,
1028 };
1029
1030 let buffer = &[42]; let result = evaluate_single_rule(&rule, buffer).unwrap();
1032 assert!(result.is_some()); }
1034
1035#[test]
1036fn test_evaluate_single_rule_bitwise_and_with_shorts() {
1037 let rule = MagicRule {
1038 offset: OffsetSpec::Absolute(0),
1039 typ: TypeKind::Short {
1040 endian: Endianness::Little,
1041 signed: false,
1042 },
1043 op: Operator::BitwiseAnd,
1044 value: Value::Uint(0xff00), message: "High byte check".to_string(),
1046 children: vec![],
1047 level: 0,
1048 strength_modifier: None,
1049 };
1050
1051 let buffer = &[0x34, 0x12]; let result = evaluate_single_rule(&rule, buffer).unwrap();
1053 assert!(result.is_some()); }
1055
1056#[test]
1057fn test_evaluate_single_rule_bitwise_and_with_longs() {
1058 let rule = MagicRule {
1059 offset: OffsetSpec::Absolute(0),
1060 typ: TypeKind::Long {
1061 endian: Endianness::Big,
1062 signed: false,
1063 },
1064 op: Operator::BitwiseAnd,
1065 value: Value::Uint(0xffff_0000), message: "High word check".to_string(),
1067 children: vec![],
1068 level: 0,
1069 strength_modifier: None,
1070 };
1071
1072 let buffer = &[0x12, 0x34, 0x56, 0x78]; let result = evaluate_single_rule(&rule, buffer).unwrap();
1074 assert!(result.is_some()); }
1076
1077#[test]
1078fn test_evaluate_single_rule_comprehensive_elf_check() {
1079 let rule = MagicRule {
1081 offset: OffsetSpec::Absolute(0),
1082 typ: TypeKind::Long {
1083 endian: Endianness::Little,
1084 signed: false,
1085 },
1086 op: Operator::Equal,
1087 value: Value::Uint(0x464c_457f), message: "ELF executable".to_string(),
1089 children: vec![],
1090 level: 0,
1091 strength_modifier: None,
1092 };
1093
1094 let elf_buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let result = evaluate_single_rule(&rule, elf_buffer).unwrap();
1096 assert!(result.is_some());
1097
1098 let non_elf_buffer = &[0x50, 0x4b, 0x03, 0x04, 0x14, 0x00]; let result = evaluate_single_rule(&rule, non_elf_buffer).unwrap();
1100 assert!(result.is_none());
1101}
1102
1103#[test]
1104fn test_evaluate_single_rule_native_endianness() {
1105 let rule = MagicRule {
1106 offset: OffsetSpec::Absolute(0),
1107 typ: TypeKind::Short {
1108 endian: Endianness::Native,
1109 signed: false,
1110 },
1111 op: Operator::NotEqual,
1112 value: Value::Uint(0),
1113 message: "Non-zero native short".to_string(),
1114 children: vec![],
1115 level: 0,
1116 strength_modifier: None,
1117 };
1118
1119 let buffer = &[0x01, 0x02]; let result = evaluate_single_rule(&rule, buffer).unwrap();
1121 assert!(result.is_some()); }
1123
1124#[test]
1125fn test_evaluate_single_rule_all_operators() {
1126 let buffer = &[0x42, 0x00, 0xff, 0x80];
1127
1128 let equal_rule = MagicRule {
1130 offset: OffsetSpec::Absolute(0),
1131 typ: TypeKind::Byte,
1132 op: Operator::Equal,
1133 value: Value::Uint(0x42),
1134 message: "Equal test".to_string(),
1135 children: vec![],
1136 level: 0,
1137 strength_modifier: None,
1138 };
1139 assert!(evaluate_single_rule(&equal_rule, buffer).unwrap().is_some());
1140
1141 let not_equal_rule = MagicRule {
1143 offset: OffsetSpec::Absolute(1),
1144 typ: TypeKind::Byte,
1145 op: Operator::NotEqual,
1146 value: Value::Uint(0x42),
1147 message: "NotEqual test".to_string(),
1148 children: vec![],
1149 level: 0,
1150 strength_modifier: None,
1151 };
1152 assert!(
1153 evaluate_single_rule(¬_equal_rule, buffer)
1154 .unwrap()
1155 .is_some()
1156 ); let bitwise_and_rule = MagicRule {
1160 offset: OffsetSpec::Absolute(3),
1161 typ: TypeKind::Byte,
1162 op: Operator::BitwiseAnd,
1163 value: Value::Uint(0x80),
1164 message: "BitwiseAnd test".to_string(),
1165 children: vec![],
1166 level: 0,
1167 strength_modifier: None,
1168 };
1169 assert!(
1170 evaluate_single_rule(&bitwise_and_rule, buffer)
1171 .unwrap()
1172 .is_some()
1173 ); }
1175
1176#[test]
1177fn test_evaluate_single_rule_edge_case_values() {
1178 let max_uint_rule = MagicRule {
1180 offset: OffsetSpec::Absolute(0),
1181 typ: TypeKind::Long {
1182 endian: Endianness::Little,
1183 signed: false,
1184 },
1185 op: Operator::Equal,
1186 value: Value::Uint(0xffff_ffff),
1187 message: "Max uint32".to_string(),
1188 children: vec![],
1189 level: 0,
1190 strength_modifier: None,
1191 };
1192
1193 let max_buffer = &[0xff, 0xff, 0xff, 0xff];
1194 let result = evaluate_single_rule(&max_uint_rule, max_buffer).unwrap();
1195 assert!(result.is_some());
1196
1197 let min_int_rule = MagicRule {
1199 offset: OffsetSpec::Absolute(0),
1200 typ: TypeKind::Long {
1201 endian: Endianness::Little,
1202 signed: true,
1203 },
1204 op: Operator::Equal,
1205 value: Value::Int(-2_147_483_648), message: "Min int32".to_string(),
1207 children: vec![],
1208 level: 0,
1209 strength_modifier: None,
1210 };
1211
1212 let min_buffer = &[0x00, 0x00, 0x00, 0x80]; let result = evaluate_single_rule(&min_int_rule, min_buffer).unwrap();
1214 assert!(result.is_some());
1215}
1216
1217#[test]
1218fn test_evaluate_single_rule_various_buffer_sizes() {
1219 let single_byte_rule = MagicRule {
1221 offset: OffsetSpec::Absolute(0),
1222 typ: TypeKind::Byte,
1223 op: Operator::Equal,
1224 value: Value::Uint(0xaa),
1225 message: "Single byte".to_string(),
1226 children: vec![],
1227 level: 0,
1228 strength_modifier: None,
1229 };
1230
1231 let single_buffer = &[0xaa];
1232 let result = evaluate_single_rule(&single_byte_rule, single_buffer).unwrap();
1233 assert!(result.is_some());
1234
1235 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
1237 let large_buffer: Vec<u8> = (0..1024).map(|i| (i % 256) as u8).collect();
1238 let large_rule = MagicRule {
1239 offset: OffsetSpec::Absolute(1000),
1240 typ: TypeKind::Byte,
1241 op: Operator::Equal,
1242 value: Value::Uint((1000 % 256) as u64),
1243 message: "Large buffer".to_string(),
1244 children: vec![],
1245 level: 0,
1246 strength_modifier: None,
1247 };
1248
1249 let result = evaluate_single_rule(&large_rule, &large_buffer).unwrap();
1250 assert!(result.is_some());
1251}
1252
1253#[test]
1255fn test_evaluation_context_new() {
1256 let config = EvaluationConfig::default();
1257 let context = EvaluationContext::new(config.clone());
1258
1259 assert_eq!(context.current_offset(), 0);
1260 assert_eq!(context.recursion_depth(), 0);
1261 assert_eq!(
1262 context.config().max_recursion_depth,
1263 config.max_recursion_depth
1264 );
1265 assert_eq!(context.config().max_string_length, config.max_string_length);
1266 assert_eq!(
1267 context.config().stop_at_first_match,
1268 config.stop_at_first_match
1269 );
1270}
1271
1272#[test]
1273fn test_evaluation_context_offset_management() {
1274 let config = EvaluationConfig::default();
1275 let mut context = EvaluationContext::new(config);
1276
1277 assert_eq!(context.current_offset(), 0);
1279
1280 context.set_current_offset(42);
1282 assert_eq!(context.current_offset(), 42);
1283
1284 context.set_current_offset(1024);
1286 assert_eq!(context.current_offset(), 1024);
1287
1288 context.set_current_offset(0);
1290 assert_eq!(context.current_offset(), 0);
1291}
1292
1293#[test]
1294fn test_evaluation_context_recursion_depth_management() {
1295 let config = EvaluationConfig::default();
1296 let mut context = EvaluationContext::new(config);
1297
1298 assert_eq!(context.recursion_depth(), 0);
1300
1301 context.increment_recursion_depth().unwrap();
1303 assert_eq!(context.recursion_depth(), 1);
1304
1305 context.increment_recursion_depth().unwrap();
1306 assert_eq!(context.recursion_depth(), 2);
1307
1308 context.decrement_recursion_depth().unwrap();
1310 assert_eq!(context.recursion_depth(), 1);
1311
1312 context.decrement_recursion_depth().unwrap();
1313 assert_eq!(context.recursion_depth(), 0);
1314}
1315
1316#[test]
1317fn test_evaluation_context_recursion_depth_limit() {
1318 let config = EvaluationConfig {
1319 max_recursion_depth: 2,
1320 ..Default::default()
1321 };
1322 let mut context = EvaluationContext::new(config);
1323
1324 assert!(context.increment_recursion_depth().is_ok());
1326 assert_eq!(context.recursion_depth(), 1);
1327
1328 assert!(context.increment_recursion_depth().is_ok());
1329 assert_eq!(context.recursion_depth(), 2);
1330
1331 let result = context.increment_recursion_depth();
1333 assert!(result.is_err());
1334 assert_eq!(context.recursion_depth(), 2); match result.unwrap_err() {
1337 LibmagicError::EvaluationError(msg) => {
1338 let error_string = format!("{msg}");
1339 assert!(error_string.contains("Recursion limit exceeded"));
1340 }
1341 _ => panic!("Expected EvaluationError"),
1342 }
1343}
1344
1345#[test]
1346fn test_evaluation_context_recursion_depth_underflow() {
1347 let config = EvaluationConfig::default();
1348 let mut context = EvaluationContext::new(config);
1349
1350 let result = context.decrement_recursion_depth();
1352 assert!(result.is_err());
1353
1354 let err = result.unwrap_err();
1355 let err_msg = err.to_string();
1356 assert!(
1357 err_msg.contains("decrement recursion depth below 0"),
1358 "Expected error about decrementing below 0, got: {err_msg}"
1359 );
1360}
1361
1362#[test]
1363fn test_evaluation_context_config_access() {
1364 let config = EvaluationConfig {
1365 max_recursion_depth: 10,
1366 max_string_length: 4096,
1367 stop_at_first_match: false,
1368 enable_mime_types: true,
1369 timeout_ms: Some(2000),
1370 };
1371
1372 let context = EvaluationContext::new(config);
1373
1374 assert_eq!(context.config().max_recursion_depth, 10);
1376 assert_eq!(context.config().max_string_length, 4096);
1377 assert!(!context.config().stop_at_first_match);
1378
1379 assert!(!context.should_stop_at_first_match());
1381 assert_eq!(context.max_string_length(), 4096);
1382}
1383
1384#[test]
1385fn test_evaluation_context_reset() {
1386 let config = EvaluationConfig::default();
1387 let mut context = EvaluationContext::new(config.clone());
1388
1389 context.set_current_offset(100);
1391 context.increment_recursion_depth().unwrap();
1392 context.increment_recursion_depth().unwrap();
1393
1394 assert_eq!(context.current_offset(), 100);
1395 assert_eq!(context.recursion_depth(), 2);
1396
1397 context.reset();
1399
1400 assert_eq!(context.current_offset(), 0);
1401 assert_eq!(context.recursion_depth(), 0);
1402 assert_eq!(
1403 context.config().max_recursion_depth,
1404 config.max_recursion_depth
1405 );
1406}
1407
1408#[test]
1409fn test_evaluation_context_clone() {
1410 let config = EvaluationConfig {
1411 max_recursion_depth: 5,
1412 max_string_length: 2048,
1413 ..Default::default()
1414 };
1415
1416 let mut context = EvaluationContext::new(config);
1417 context.set_current_offset(50);
1418 context.increment_recursion_depth().unwrap();
1419
1420 let cloned_context = context.clone();
1422
1423 assert_eq!(context.current_offset(), cloned_context.current_offset());
1425 assert_eq!(context.recursion_depth(), cloned_context.recursion_depth());
1426 assert_eq!(
1427 context.config().max_recursion_depth,
1428 cloned_context.config().max_recursion_depth
1429 );
1430 assert_eq!(
1431 context.config().max_string_length,
1432 cloned_context.config().max_string_length
1433 );
1434
1435 context.set_current_offset(75);
1437 assert_eq!(context.current_offset(), 75);
1438 assert_eq!(cloned_context.current_offset(), 50);
1439}
1440
1441#[test]
1442fn test_evaluation_context_with_custom_config() {
1443 let config = EvaluationConfig {
1444 max_recursion_depth: 15,
1445 max_string_length: 16384,
1446 stop_at_first_match: false,
1447 enable_mime_types: true,
1448 timeout_ms: Some(5000),
1449 };
1450
1451 let context = EvaluationContext::new(config);
1452
1453 assert_eq!(context.config().max_recursion_depth, 15);
1454 assert_eq!(context.max_string_length(), 16384);
1455 assert!(!context.should_stop_at_first_match());
1456
1457 let mut mutable_context = context;
1459 for i in 1..=15 {
1460 assert!(mutable_context.increment_recursion_depth().is_ok());
1461 assert_eq!(mutable_context.recursion_depth(), i);
1462 }
1463
1464 let result = mutable_context.increment_recursion_depth();
1466 assert!(result.is_err());
1467}
1468
1469#[test]
1470fn test_evaluation_context_mime_types_access() {
1471 let config_with_mime = EvaluationConfig {
1472 enable_mime_types: true,
1473 ..Default::default()
1474 };
1475 let context_with_mime = EvaluationContext::new(config_with_mime);
1476 assert!(context_with_mime.enable_mime_types());
1477
1478 let config_without_mime = EvaluationConfig {
1479 enable_mime_types: false,
1480 ..Default::default()
1481 };
1482 let context_without_mime = EvaluationContext::new(config_without_mime);
1483 assert!(!context_without_mime.enable_mime_types());
1484}
1485
1486#[test]
1487fn test_evaluation_context_timeout_access() {
1488 let config_with_timeout = EvaluationConfig {
1489 timeout_ms: Some(5000),
1490 ..Default::default()
1491 };
1492 let context_with_timeout = EvaluationContext::new(config_with_timeout);
1493 assert_eq!(context_with_timeout.timeout_ms(), Some(5000));
1494
1495 let config_without_timeout = EvaluationConfig {
1496 timeout_ms: None,
1497 ..Default::default()
1498 };
1499 let context_without_timeout = EvaluationContext::new(config_without_timeout);
1500 assert_eq!(context_without_timeout.timeout_ms(), None);
1501}
1502
1503#[test]
1504fn test_evaluation_context_comprehensive_config() {
1505 let config = EvaluationConfig {
1506 max_recursion_depth: 30,
1507 max_string_length: 16384,
1508 stop_at_first_match: false,
1509 enable_mime_types: true,
1510 timeout_ms: Some(10000),
1511 };
1512 let context = EvaluationContext::new(config);
1513
1514 assert_eq!(context.config().max_recursion_depth, 30);
1515 assert_eq!(context.config().max_string_length, 16384);
1516 assert!(!context.should_stop_at_first_match());
1517 assert!(context.enable_mime_types());
1518 assert_eq!(context.timeout_ms(), Some(10000));
1519 assert_eq!(context.max_string_length(), 16384);
1520}
1521
1522#[test]
1523fn test_evaluation_context_performance_config() {
1524 let config = EvaluationConfig {
1525 max_recursion_depth: 5,
1526 max_string_length: 512,
1527 stop_at_first_match: true,
1528 enable_mime_types: false,
1529 timeout_ms: Some(1000),
1530 };
1531 let context = EvaluationContext::new(config);
1532
1533 assert_eq!(context.config().max_recursion_depth, 5);
1534 assert_eq!(context.max_string_length(), 512);
1535 assert!(context.should_stop_at_first_match());
1536 assert!(!context.enable_mime_types());
1537 assert_eq!(context.timeout_ms(), Some(1000));
1538}
1539
1540#[test]
1541fn test_match_result_creation() {
1542 let match_result = MatchResult {
1543 message: "ELF executable".to_string(),
1544 offset: 0,
1545 level: 0,
1546 value: Value::Uint(0x7f),
1547 confidence: MatchResult::calculate_confidence(0),
1548 };
1549
1550 assert_eq!(match_result.message, "ELF executable");
1551 assert_eq!(match_result.offset, 0);
1552 assert_eq!(match_result.level, 0);
1553 assert_eq!(match_result.value, Value::Uint(0x7f));
1554 assert!((match_result.confidence - 0.3).abs() < 0.001);
1555}
1556
1557#[test]
1558fn test_match_result_clone() {
1559 let original = MatchResult {
1560 message: "Test message".to_string(),
1561 offset: 42,
1562 level: 1,
1563 value: Value::String("test".to_string()),
1564 confidence: MatchResult::calculate_confidence(1),
1565 };
1566
1567 let cloned = original.clone();
1568 assert_eq!(original, cloned);
1569}
1570
1571#[test]
1572fn test_match_result_debug() {
1573 let match_result = MatchResult {
1574 message: "Debug test".to_string(),
1575 offset: 10,
1576 level: 2,
1577 value: Value::Bytes(vec![0x01, 0x02]),
1578 confidence: MatchResult::calculate_confidence(2),
1579 };
1580
1581 let debug_str = format!("{match_result:?}");
1582 assert!(debug_str.contains("MatchResult"));
1583 assert!(debug_str.contains("Debug test"));
1584 assert!(debug_str.contains("10"));
1585 assert!(debug_str.contains('2'));
1586}
1587
1588#[test]
1589fn test_confidence_calculation_depth_0() {
1590 let confidence = MatchResult::calculate_confidence(0);
1591 assert!((confidence - 0.3).abs() < 0.001);
1592}
1593
1594#[test]
1595fn test_confidence_calculation_depth_1() {
1596 let confidence = MatchResult::calculate_confidence(1);
1597 assert!((confidence - 0.5).abs() < 0.001);
1598}
1599
1600#[test]
1601fn test_confidence_calculation_depth_2() {
1602 let confidence = MatchResult::calculate_confidence(2);
1603 assert!((confidence - 0.7).abs() < 0.001);
1604}
1605
1606#[test]
1607fn test_confidence_calculation_depth_3() {
1608 let confidence = MatchResult::calculate_confidence(3);
1609 assert!((confidence - 0.9).abs() < 0.001);
1610}
1611
1612#[test]
1613fn test_confidence_calculation_capped_at_1() {
1614 let confidence_4 = MatchResult::calculate_confidence(4);
1616 assert!((confidence_4 - 1.0).abs() < 0.001);
1617
1618 let confidence_10 = MatchResult::calculate_confidence(10);
1619 assert!((confidence_10 - 1.0).abs() < 0.001);
1620
1621 let confidence_100 = MatchResult::calculate_confidence(100);
1622 assert!((confidence_100 - 1.0).abs() < 0.001);
1623}
1624
1625#[test]
1626fn test_evaluate_rules_empty_list() {
1627 let rules = vec![];
1628 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1629 let config = EvaluationConfig::default();
1630 let mut context = EvaluationContext::new(config);
1631
1632 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1633 assert!(matches.is_empty());
1634}
1635
1636#[test]
1637fn test_evaluate_rules_single_matching_rule() {
1638 let rule = MagicRule {
1639 offset: OffsetSpec::Absolute(0),
1640 typ: TypeKind::Byte,
1641 op: Operator::Equal,
1642 value: Value::Uint(0x7f),
1643 message: "ELF magic".to_string(),
1644 children: vec![],
1645 level: 0,
1646 strength_modifier: None,
1647 };
1648
1649 let rules = vec![rule];
1650 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1651 let config = EvaluationConfig::default();
1652 let mut context = EvaluationContext::new(config);
1653
1654 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1655 assert_eq!(matches.len(), 1);
1656 assert_eq!(matches[0].message, "ELF magic");
1657 assert_eq!(matches[0].offset, 0);
1658 assert_eq!(matches[0].level, 0);
1659 assert_eq!(matches[0].value, Value::Uint(0x7f));
1660}
1661
1662#[test]
1663fn test_evaluate_rules_single_non_matching_rule() {
1664 let rule = MagicRule {
1665 offset: OffsetSpec::Absolute(0),
1666 typ: TypeKind::Byte,
1667 op: Operator::Equal,
1668 value: Value::Uint(0x50), message: "ZIP magic".to_string(),
1670 children: vec![],
1671 level: 0,
1672 strength_modifier: None,
1673 };
1674
1675 let rules = vec![rule];
1676 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let config = EvaluationConfig::default();
1678 let mut context = EvaluationContext::new(config);
1679
1680 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1681 assert!(matches.is_empty());
1682}
1683
1684#[test]
1685fn test_evaluate_rules_multiple_rules_stop_at_first() {
1686 let rule1 = MagicRule {
1687 offset: OffsetSpec::Absolute(0),
1688 typ: TypeKind::Byte,
1689 op: Operator::Equal,
1690 value: Value::Uint(0x7f),
1691 message: "First match".to_string(),
1692 children: vec![],
1693 level: 0,
1694 strength_modifier: None,
1695 };
1696
1697 let rule2 = MagicRule {
1698 offset: OffsetSpec::Absolute(1),
1699 typ: TypeKind::Byte,
1700 op: Operator::Equal,
1701 value: Value::Uint(0x45),
1702 message: "Second match".to_string(),
1703 children: vec![],
1704 level: 0,
1705 strength_modifier: None,
1706 };
1707
1708 let rule_list = vec![rule1, rule2];
1709 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1710 let config = EvaluationConfig {
1711 stop_at_first_match: true,
1712 ..Default::default()
1713 };
1714 let mut context = EvaluationContext::new(config);
1715
1716 let matches = evaluate_rules(&rule_list, buffer, &mut context).unwrap();
1717 assert_eq!(matches.len(), 1);
1718 assert_eq!(matches[0].message, "First match");
1719}
1720
1721#[test]
1722fn test_evaluate_rules_multiple_rules_find_all() {
1723 let rule1 = MagicRule {
1724 offset: OffsetSpec::Absolute(0),
1725 typ: TypeKind::Byte,
1726 op: Operator::Equal,
1727 value: Value::Uint(0x7f),
1728 message: "First match".to_string(),
1729 children: vec![],
1730 level: 0,
1731 strength_modifier: None,
1732 };
1733
1734 let rule2 = MagicRule {
1735 offset: OffsetSpec::Absolute(1),
1736 typ: TypeKind::Byte,
1737 op: Operator::Equal,
1738 value: Value::Uint(0x45),
1739 message: "Second match".to_string(),
1740 children: vec![],
1741 level: 0,
1742 strength_modifier: None,
1743 };
1744
1745 let rule_set = vec![rule1, rule2];
1746 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1747 let config = EvaluationConfig {
1748 stop_at_first_match: false,
1749 ..Default::default()
1750 };
1751 let mut context = EvaluationContext::new(config);
1752
1753 let matches = evaluate_rules(&rule_set, buffer, &mut context).unwrap();
1754 assert_eq!(matches.len(), 2);
1755 assert_eq!(matches[0].message, "First match");
1756 assert_eq!(matches[1].message, "Second match");
1757}
1758
1759#[test]
1760fn test_evaluate_rules_hierarchical_parent_child() {
1761 let child_rule = MagicRule {
1762 offset: OffsetSpec::Absolute(4),
1763 typ: TypeKind::Byte,
1764 op: Operator::Equal,
1765 value: Value::Uint(0x02), message: "64-bit".to_string(),
1767 children: vec![],
1768 level: 1,
1769 strength_modifier: None,
1770 };
1771
1772 let parent_rule = MagicRule {
1773 offset: OffsetSpec::Absolute(0),
1774 typ: TypeKind::Byte,
1775 op: Operator::Equal,
1776 value: Value::Uint(0x7f),
1777 message: "ELF".to_string(),
1778 children: vec![child_rule],
1779 level: 0,
1780 strength_modifier: None,
1781 };
1782
1783 let rules = vec![parent_rule];
1784 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let config = EvaluationConfig::default();
1786 let mut context = EvaluationContext::new(config);
1787
1788 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1789 assert_eq!(matches.len(), 2);
1790 assert_eq!(matches[0].message, "ELF");
1791 assert_eq!(matches[0].level, 0);
1792 assert_eq!(matches[1].message, "64-bit");
1793 assert_eq!(matches[1].level, 1);
1794}
1795
1796#[test]
1797fn test_evaluate_rules_hierarchical_parent_no_match() {
1798 let child_rule = MagicRule {
1799 offset: OffsetSpec::Absolute(4),
1800 typ: TypeKind::Byte,
1801 op: Operator::Equal,
1802 value: Value::Uint(0x02),
1803 message: "64-bit".to_string(),
1804 children: vec![],
1805 level: 1,
1806 strength_modifier: None,
1807 };
1808
1809 let parent_rule = MagicRule {
1810 offset: OffsetSpec::Absolute(0),
1811 typ: TypeKind::Byte,
1812 op: Operator::Equal,
1813 value: Value::Uint(0x50), message: "ZIP".to_string(),
1815 children: vec![child_rule],
1816 level: 0,
1817 strength_modifier: None,
1818 };
1819
1820 let rules = vec![parent_rule];
1821 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let config = EvaluationConfig::default();
1823 let mut context = EvaluationContext::new(config);
1824
1825 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1826 assert!(matches.is_empty()); }
1828
1829#[test]
1830fn test_evaluate_rules_hierarchical_parent_match_child_no_match() {
1831 let child_rule = MagicRule {
1832 offset: OffsetSpec::Absolute(4),
1833 typ: TypeKind::Byte,
1834 op: Operator::Equal,
1835 value: Value::Uint(0x01), message: "32-bit".to_string(),
1837 children: vec![],
1838 level: 1,
1839 strength_modifier: None,
1840 };
1841
1842 let parent_rule = MagicRule {
1843 offset: OffsetSpec::Absolute(0),
1844 typ: TypeKind::Byte,
1845 op: Operator::Equal,
1846 value: Value::Uint(0x7f),
1847 message: "ELF".to_string(),
1848 children: vec![child_rule],
1849 level: 0,
1850 strength_modifier: None,
1851 };
1852
1853 let rules = vec![parent_rule];
1854 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let config = EvaluationConfig::default();
1856 let mut context = EvaluationContext::new(config);
1857
1858 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1859 assert_eq!(matches.len(), 1); assert_eq!(matches[0].message, "ELF");
1861 assert_eq!(matches[0].level, 0);
1862}
1863
1864#[test]
1865fn test_evaluate_rules_deep_hierarchy() {
1866 let grandchild_rule = MagicRule {
1867 offset: OffsetSpec::Absolute(5),
1868 typ: TypeKind::Byte,
1869 op: Operator::Equal,
1870 value: Value::Uint(0x01), message: "little-endian".to_string(),
1872 children: vec![],
1873 level: 2,
1874 strength_modifier: None,
1875 };
1876
1877 let child_rule = MagicRule {
1878 offset: OffsetSpec::Absolute(4),
1879 typ: TypeKind::Byte,
1880 op: Operator::Equal,
1881 value: Value::Uint(0x02), message: "64-bit".to_string(),
1883 children: vec![grandchild_rule],
1884 level: 1,
1885 strength_modifier: None,
1886 };
1887
1888 let parent_rule = MagicRule {
1889 offset: OffsetSpec::Absolute(0),
1890 typ: TypeKind::Byte,
1891 op: Operator::Equal,
1892 value: Value::Uint(0x7f),
1893 message: "ELF".to_string(),
1894 children: vec![child_rule],
1895 level: 0,
1896 strength_modifier: None,
1897 };
1898
1899 let rules = vec![parent_rule];
1900 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let config = EvaluationConfig::default();
1902 let mut context = EvaluationContext::new(config);
1903
1904 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1905 assert_eq!(matches.len(), 3);
1906 assert_eq!(matches[0].message, "ELF");
1907 assert_eq!(matches[0].level, 0);
1908 assert_eq!(matches[1].message, "64-bit");
1909 assert_eq!(matches[1].level, 1);
1910 assert_eq!(matches[2].message, "little-endian");
1911 assert_eq!(matches[2].level, 2);
1912}
1913
1914#[test]
1915fn test_evaluate_rules_multiple_children() {
1916 let child1 = MagicRule {
1917 offset: OffsetSpec::Absolute(4),
1918 typ: TypeKind::Byte,
1919 op: Operator::Equal,
1920 value: Value::Uint(0x02),
1921 message: "64-bit".to_string(),
1922 children: vec![],
1923 level: 1,
1924 strength_modifier: None,
1925 };
1926
1927 let child2 = MagicRule {
1928 offset: OffsetSpec::Absolute(5),
1929 typ: TypeKind::Byte,
1930 op: Operator::Equal,
1931 value: Value::Uint(0x01),
1932 message: "little-endian".to_string(),
1933 children: vec![],
1934 level: 1,
1935 strength_modifier: None,
1936 };
1937
1938 let parent_rule = MagicRule {
1939 offset: OffsetSpec::Absolute(0),
1940 typ: TypeKind::Byte,
1941 op: Operator::Equal,
1942 value: Value::Uint(0x7f),
1943 message: "ELF".to_string(),
1944 children: vec![child1, child2],
1945 level: 0,
1946 strength_modifier: None,
1947 };
1948
1949 let rules = vec![parent_rule];
1950 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01];
1951 let config = EvaluationConfig {
1952 stop_at_first_match: false, ..Default::default()
1954 };
1955 let mut context = EvaluationContext::new(config);
1956
1957 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1958 assert_eq!(matches.len(), 3);
1959 assert_eq!(matches[0].message, "ELF");
1960 assert_eq!(matches[1].message, "64-bit");
1961 assert_eq!(matches[2].message, "little-endian");
1962}
1963
1964#[test]
1965fn test_evaluate_rules_recursion_depth_limit() {
1966 let mut current_rule = MagicRule {
1968 offset: OffsetSpec::Absolute(10),
1969 typ: TypeKind::Byte,
1970 op: Operator::Equal,
1971 value: Value::Uint(0x00),
1972 message: "Deep level".to_string(),
1973 children: vec![],
1974 level: 10,
1975 strength_modifier: None,
1976 };
1977
1978 for i in (0u32..10u32).rev() {
1980 current_rule = MagicRule {
1981 offset: OffsetSpec::Absolute(i64::from(i)),
1982 typ: TypeKind::Byte,
1983 op: Operator::Equal,
1984 value: Value::Uint(u64::from(i)),
1985 message: format!("Level {i}"),
1986 children: vec![current_rule],
1987 level: i,
1988 strength_modifier: None,
1989 };
1990 }
1991
1992 let rules = vec![current_rule];
1993 let buffer = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; let config = EvaluationConfig {
1995 max_recursion_depth: 5, ..Default::default()
1997 };
1998 let mut context = EvaluationContext::new(config);
1999
2000 let result = evaluate_rules(&rules, buffer, &mut context);
2001 assert!(result.is_err());
2002
2003 match result.unwrap_err() {
2004 LibmagicError::EvaluationError(msg) => {
2005 let error_string = format!("{msg}");
2006 assert!(error_string.contains("Recursion limit exceeded"));
2007 }
2008 _ => panic!("Expected EvaluationError for recursion limit"),
2009 }
2010}
2011
2012#[test]
2013fn test_evaluate_rules_with_config_convenience() {
2014 let rule = MagicRule {
2015 offset: OffsetSpec::Absolute(0),
2016 typ: TypeKind::Byte,
2017 op: Operator::Equal,
2018 value: Value::Uint(0x7f),
2019 message: "ELF magic".to_string(),
2020 children: vec![],
2021 level: 0,
2022 strength_modifier: None,
2023 };
2024
2025 let rules = vec![rule];
2026 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2027 let config = EvaluationConfig::default();
2028
2029 let matches = evaluate_rules_with_config(&rules, buffer, &config).unwrap();
2030 assert_eq!(matches.len(), 1);
2031 assert_eq!(matches[0].message, "ELF magic");
2032}
2033
2034#[test]
2035fn test_evaluate_rules_timeout() {
2036 let rule = MagicRule {
2037 offset: OffsetSpec::Absolute(0),
2038 typ: TypeKind::Byte,
2039 op: Operator::Equal,
2040 value: Value::Uint(0x7f),
2041 message: "ELF magic".to_string(),
2042 children: vec![],
2043 level: 0,
2044 strength_modifier: None,
2045 };
2046
2047 let rules = vec![rule];
2048 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2049 let config = EvaluationConfig {
2050 timeout_ms: Some(0), ..Default::default()
2052 };
2053 let mut context = EvaluationContext::new(config);
2054
2055 let result = evaluate_rules(&rules, buffer, &mut context);
2057 if let Err(LibmagicError::Timeout { timeout_ms }) = result {
2060 assert_eq!(timeout_ms, 0);
2061 }
2062}
2063
2064#[test]
2065fn test_evaluate_rules_empty_buffer() {
2066 let rule = MagicRule {
2067 offset: OffsetSpec::Absolute(0),
2068 typ: TypeKind::Byte,
2069 op: Operator::Equal,
2070 value: Value::Uint(0x7f),
2071 message: "Should not match".to_string(),
2072 children: vec![],
2073 level: 0,
2074 strength_modifier: None,
2075 };
2076
2077 let rules = vec![rule];
2078 let buffer = &[]; let config = EvaluationConfig::default();
2080 let mut context = EvaluationContext::new(config);
2081
2082 let result = evaluate_rules(&rules, buffer, &mut context);
2084 assert!(result.is_ok());
2085
2086 let matches = result.unwrap();
2087 assert_eq!(matches.len(), 0); }
2089
2090#[test]
2091fn test_evaluate_rules_mixed_matching_non_matching() {
2092 let rule1 = MagicRule {
2093 offset: OffsetSpec::Absolute(0),
2094 typ: TypeKind::Byte,
2095 op: Operator::Equal,
2096 value: Value::Uint(0x7f),
2097 message: "Matches".to_string(),
2098 children: vec![],
2099 level: 0,
2100 strength_modifier: None,
2101 };
2102
2103 let rule2 = MagicRule {
2104 offset: OffsetSpec::Absolute(1),
2105 typ: TypeKind::Byte,
2106 op: Operator::Equal,
2107 value: Value::Uint(0x99), message: "Doesn't match".to_string(),
2109 children: vec![],
2110 level: 0,
2111 strength_modifier: None,
2112 };
2113
2114 let rule3 = MagicRule {
2115 offset: OffsetSpec::Absolute(2),
2116 typ: TypeKind::Byte,
2117 op: Operator::Equal,
2118 value: Value::Uint(0x4c),
2119 message: "Also matches".to_string(),
2120 children: vec![],
2121 level: 0,
2122 strength_modifier: None,
2123 };
2124
2125 let rule_collection = vec![rule1, rule2, rule3];
2126 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2127 let config = EvaluationConfig {
2128 stop_at_first_match: false,
2129 ..Default::default()
2130 };
2131 let mut context = EvaluationContext::new(config);
2132
2133 let matches = evaluate_rules(&rule_collection, buffer, &mut context).unwrap();
2134 assert_eq!(matches.len(), 2);
2135 assert_eq!(matches[0].message, "Matches");
2136 assert_eq!(matches[1].message, "Also matches");
2137}
2138
2139#[test]
2140fn test_evaluate_rules_context_state_preservation() {
2141 let rule = MagicRule {
2142 offset: OffsetSpec::Absolute(0),
2143 typ: TypeKind::Byte,
2144 op: Operator::Equal,
2145 value: Value::Uint(0x7f),
2146 message: "ELF magic".to_string(),
2147 children: vec![],
2148 level: 0,
2149 strength_modifier: None,
2150 };
2151
2152 let rules = vec![rule];
2153 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2154 let config = EvaluationConfig::default();
2155 let mut context = EvaluationContext::new(config);
2156
2157 context.set_current_offset(100);
2159 let initial_offset = context.current_offset();
2160 let initial_depth = context.recursion_depth();
2161
2162 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2163 assert_eq!(matches.len(), 1);
2164
2165 assert_eq!(context.current_offset(), initial_offset);
2167 assert_eq!(context.recursion_depth(), initial_depth);
2168}
2169
2170#[test]
2171fn test_evaluation_context_state_management_sequence() {
2172 let config = EvaluationConfig::default();
2173 let mut context = EvaluationContext::new(config);
2174
2175 assert_eq!(context.current_offset(), 0);
2177 assert_eq!(context.recursion_depth(), 0);
2178
2179 context.set_current_offset(10);
2181 assert_eq!(context.current_offset(), 10);
2182
2183 context.increment_recursion_depth().unwrap();
2185 assert_eq!(context.recursion_depth(), 1);
2186
2187 context.set_current_offset(25);
2189 assert_eq!(context.current_offset(), 25);
2190
2191 context.increment_recursion_depth().unwrap();
2193 assert_eq!(context.recursion_depth(), 2);
2194
2195 context.decrement_recursion_depth().unwrap();
2197 assert_eq!(context.recursion_depth(), 1);
2198
2199 context.set_current_offset(50);
2201 assert_eq!(context.current_offset(), 50);
2202
2203 context.decrement_recursion_depth().unwrap();
2205 assert_eq!(context.recursion_depth(), 0);
2206
2207 assert_eq!(context.current_offset(), 50);
2209 assert_eq!(context.recursion_depth(), 0);
2210}
2211#[test]
2212fn test_error_recovery_skip_problematic_rules() {
2213 let rules = vec![
2215 MagicRule {
2217 offset: OffsetSpec::Absolute(0),
2218 typ: TypeKind::Byte,
2219 op: Operator::Equal,
2220 value: Value::Uint(0x7f),
2221 message: "Valid rule".to_string(),
2222 children: vec![],
2223 level: 0,
2224 strength_modifier: None,
2225 },
2226 MagicRule {
2228 offset: OffsetSpec::Absolute(100), typ: TypeKind::Byte,
2230 op: Operator::Equal,
2231 value: Value::Uint(0x00),
2232 message: "Invalid rule".to_string(),
2233 children: vec![],
2234 level: 0,
2235 strength_modifier: None,
2236 },
2237 MagicRule {
2239 offset: OffsetSpec::Absolute(1),
2240 typ: TypeKind::Byte,
2241 op: Operator::Equal,
2242 value: Value::Uint(0x45),
2243 message: "Another valid rule".to_string(),
2244 children: vec![],
2245 level: 0,
2246 strength_modifier: None,
2247 },
2248 ];
2249
2250 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let config = EvaluationConfig {
2252 max_recursion_depth: 20,
2253 max_string_length: 8192,
2254 stop_at_first_match: false, enable_mime_types: false,
2256 timeout_ms: None,
2257 };
2258 let mut context = EvaluationContext::new(config);
2259
2260 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2262
2263 assert_eq!(matches.len(), 2);
2265 assert_eq!(matches[0].message, "Valid rule");
2266 assert_eq!(matches[1].message, "Another valid rule");
2267}
2268
2269#[test]
2270fn test_error_recovery_child_rule_failures() {
2271 let rules = vec![MagicRule {
2273 offset: OffsetSpec::Absolute(0),
2274 typ: TypeKind::Byte,
2275 op: Operator::Equal,
2276 value: Value::Uint(0x7f),
2277 message: "Parent rule".to_string(),
2278 children: vec![
2279 MagicRule {
2281 offset: OffsetSpec::Absolute(1),
2282 typ: TypeKind::Byte,
2283 op: Operator::Equal,
2284 value: Value::Uint(0x45),
2285 message: "Valid child".to_string(),
2286 children: vec![],
2287 level: 1,
2288 strength_modifier: None,
2289 },
2290 MagicRule {
2292 offset: OffsetSpec::Absolute(100), typ: TypeKind::Byte,
2294 op: Operator::Equal,
2295 value: Value::Uint(0x00),
2296 message: "Invalid child".to_string(),
2297 children: vec![],
2298 level: 1,
2299 strength_modifier: None,
2300 },
2301 ],
2302 level: 0,
2303 strength_modifier: None,
2304 }];
2305
2306 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let config = EvaluationConfig::default();
2308 let mut context = EvaluationContext::new(config);
2309
2310 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2312
2313 assert_eq!(matches.len(), 2);
2315 assert_eq!(matches[0].message, "Parent rule");
2316 assert_eq!(matches[1].message, "Valid child");
2317}
2318
2319#[test]
2320fn test_error_recovery_mixed_rule_types() {
2321 let rules = vec![
2323 MagicRule {
2325 offset: OffsetSpec::Absolute(0),
2326 typ: TypeKind::Byte,
2327 op: Operator::Equal,
2328 value: Value::Uint(0x7f),
2329 message: "Valid byte".to_string(),
2330 children: vec![],
2331 level: 0,
2332 strength_modifier: None,
2333 },
2334 MagicRule {
2336 offset: OffsetSpec::Absolute(3), typ: TypeKind::Short {
2338 endian: Endianness::Little,
2339 signed: false,
2340 },
2341 op: Operator::Equal,
2342 value: Value::Uint(0x1234),
2343 message: "Invalid short".to_string(),
2344 children: vec![],
2345 level: 0,
2346 strength_modifier: None,
2347 },
2348 MagicRule {
2350 offset: OffsetSpec::Absolute(1),
2351 typ: TypeKind::String {
2352 max_length: Some(3),
2353 },
2354 op: Operator::Equal,
2355 value: Value::String("ELF".to_string()),
2356 message: "Valid string".to_string(),
2357 children: vec![],
2358 level: 0,
2359 strength_modifier: None,
2360 },
2361 ];
2362
2363 let buffer = &[0x7f, b'E', b'L', b'F']; let config = EvaluationConfig {
2365 max_recursion_depth: 20,
2366 max_string_length: 8192,
2367 stop_at_first_match: false, enable_mime_types: false,
2369 timeout_ms: None,
2370 };
2371 let mut context = EvaluationContext::new(config);
2372
2373 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2375
2376 assert_eq!(matches.len(), 2);
2378 assert_eq!(matches[0].message, "Valid byte");
2379 assert_eq!(matches[1].message, "Valid string");
2380}
2381
2382#[test]
2383fn test_error_recovery_all_rules_fail() {
2384 let rules = vec![
2386 MagicRule {
2388 offset: OffsetSpec::Absolute(100),
2389 typ: TypeKind::Byte,
2390 op: Operator::Equal,
2391 value: Value::Uint(0x00),
2392 message: "Out of bounds".to_string(),
2393 children: vec![],
2394 level: 0,
2395 strength_modifier: None,
2396 },
2397 MagicRule {
2399 offset: OffsetSpec::Absolute(2),
2400 typ: TypeKind::Long {
2401 endian: Endianness::Little,
2402 signed: false,
2403 },
2404 op: Operator::Equal,
2405 value: Value::Uint(0x1234_5678),
2406 message: "Insufficient bytes".to_string(),
2407 children: vec![],
2408 level: 0,
2409 strength_modifier: None,
2410 },
2411 ];
2412
2413 let buffer = &[0x7f, 0x45]; let config = EvaluationConfig::default();
2415 let mut context = EvaluationContext::new(config);
2416
2417 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2419 assert_eq!(matches.len(), 0);
2420}
2421
2422#[test]
2423fn test_error_recovery_timeout_propagation() {
2424 let rules = vec![MagicRule {
2426 offset: OffsetSpec::Absolute(0),
2427 typ: TypeKind::Byte,
2428 op: Operator::Equal,
2429 value: Value::Uint(0x7f),
2430 message: "Test rule".to_string(),
2431 children: vec![],
2432 level: 0,
2433 strength_modifier: None,
2434 }];
2435
2436 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2437 let config = EvaluationConfig {
2438 max_recursion_depth: 10,
2439 max_string_length: 1024,
2440 stop_at_first_match: false,
2441 enable_mime_types: false,
2442 timeout_ms: Some(0), };
2444 let mut context = EvaluationContext::new(config);
2445
2446 let result = evaluate_rules(&rules, buffer, &mut context);
2449
2450 match result {
2452 Ok(_) | Err(LibmagicError::Timeout { .. }) => {
2453 }
2455 Err(e) => {
2456 panic!("Unexpected error type: {e:?}");
2457 }
2458 }
2459}
2460
2461#[test]
2462fn test_error_recovery_recursion_limit_propagation() {
2463 let rules = vec![MagicRule {
2465 offset: OffsetSpec::Absolute(0),
2466 typ: TypeKind::Byte,
2467 op: Operator::Equal,
2468 value: Value::Uint(0x7f),
2469 message: "Parent".to_string(),
2470 children: vec![MagicRule {
2471 offset: OffsetSpec::Absolute(1),
2472 typ: TypeKind::Byte,
2473 op: Operator::Equal,
2474 value: Value::Uint(0x45),
2475 message: "Child".to_string(),
2476 children: vec![],
2477 level: 1,
2478 strength_modifier: None,
2479 }],
2480 level: 0,
2481 strength_modifier: None,
2482 }];
2483
2484 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2485 let config = EvaluationConfig {
2486 max_recursion_depth: 0, max_string_length: 1024,
2488 stop_at_first_match: false,
2489 enable_mime_types: false,
2490 timeout_ms: None,
2491 };
2492 let mut context = EvaluationContext::new(config);
2493
2494 let result = evaluate_rules(&rules, buffer, &mut context);
2496 assert!(result.is_err());
2497
2498 match result.unwrap_err() {
2499 LibmagicError::EvaluationError(crate::error::EvaluationError::RecursionLimitExceeded {
2500 ..
2501 }) => {
2502 }
2504 _ => panic!("Expected recursion limit error"),
2505 }
2506}
2507
2508#[test]
2509fn test_error_recovery_preserves_context_state() {
2510 let rules = vec![
2512 MagicRule {
2514 offset: OffsetSpec::Absolute(0),
2515 typ: TypeKind::Byte,
2516 op: Operator::Equal,
2517 value: Value::Uint(0x7f),
2518 message: "Valid rule".to_string(),
2519 children: vec![],
2520 level: 0,
2521 strength_modifier: None,
2522 },
2523 MagicRule {
2525 offset: OffsetSpec::Absolute(100),
2526 typ: TypeKind::Byte,
2527 op: Operator::Equal,
2528 value: Value::Uint(0x00),
2529 message: "Invalid rule".to_string(),
2530 children: vec![],
2531 level: 0,
2532 strength_modifier: None,
2533 },
2534 ];
2535
2536 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2537 let config = EvaluationConfig::default();
2538 let mut context = EvaluationContext::new(config);
2539
2540 context.set_current_offset(42);
2542 let initial_offset = context.current_offset();
2543 let initial_depth = context.recursion_depth();
2544
2545 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2547 assert_eq!(matches.len(), 1);
2548
2549 assert_eq!(context.current_offset(), initial_offset);
2551 assert_eq!(context.recursion_depth(), initial_depth);
2552}
2553#[test]
2554fn test_debug_error_recovery() {
2555 let rule = MagicRule {
2557 offset: OffsetSpec::Absolute(100), typ: TypeKind::Byte,
2559 op: Operator::Equal,
2560 value: Value::Uint(0x00),
2561 message: "Out of bounds rule".to_string(),
2562 children: vec![],
2563 level: 0,
2564 strength_modifier: None,
2565 };
2566
2567 let buffer = &[0x7f, 0x45]; let single_result = evaluate_single_rule(&rule, buffer);
2571 println!("Single rule result: {single_result:?}");
2572 assert!(single_result.is_err());
2573
2574 let rules = vec![rule];
2576 let config = EvaluationConfig::default();
2577 let mut context = EvaluationContext::new(config);
2578
2579 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2580 println!("Rules evaluation matches: {}", matches.len());
2581 assert_eq!(matches.len(), 0);
2582}
2583#[test]
2584fn test_debug_mixed_rules() {
2585 let rules = vec![
2586 MagicRule {
2588 offset: OffsetSpec::Absolute(0),
2589 typ: TypeKind::Byte,
2590 op: Operator::Equal,
2591 value: Value::Uint(0x7f),
2592 message: "Valid rule".to_string(),
2593 children: vec![],
2594 level: 0,
2595 strength_modifier: None,
2596 },
2597 MagicRule {
2599 offset: OffsetSpec::Absolute(100), typ: TypeKind::Byte,
2601 op: Operator::Equal,
2602 value: Value::Uint(0x00),
2603 message: "Invalid rule".to_string(),
2604 children: vec![],
2605 level: 0,
2606 strength_modifier: None,
2607 },
2608 MagicRule {
2610 offset: OffsetSpec::Absolute(1),
2611 typ: TypeKind::Byte,
2612 op: Operator::Equal,
2613 value: Value::Uint(0x45),
2614 message: "Another valid rule".to_string(),
2615 children: vec![],
2616 level: 0,
2617 strength_modifier: None,
2618 },
2619 ];
2620
2621 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; for (i, rule) in rules.iter().enumerate() {
2625 let result = evaluate_single_rule(rule, buffer);
2626 println!("Rule {}: '{}' -> {:?}", i, rule.message, result);
2627 }
2628
2629 let config = EvaluationConfig::default();
2631 let mut context = EvaluationContext::new(config);
2632
2633 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2634 println!("Total matches: {}", matches.len());
2635 for (i, m) in matches.iter().enumerate() {
2636 println!("Match {}: '{}'", i, m.message);
2637 }
2638}