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 RuleMatch {
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 RuleMatch {
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 let expected_value = types::coerce_value_to_type(&rule.value, &rule.typ);
311
312 Ok(
314 operators::apply_operator(&rule.op, &read_value, &expected_value)
315 .then_some((absolute_offset, read_value)),
316 )
317}
318
319pub fn evaluate_rules(
394 rules: &[MagicRule],
395 buffer: &[u8],
396 context: &mut EvaluationContext,
397) -> Result<Vec<RuleMatch>, LibmagicError> {
398 let mut matches = Vec::with_capacity(8);
399 let start_time = std::time::Instant::now();
400 let mut rule_count = 0u32;
401
402 for rule in rules {
403 rule_count = rule_count.wrapping_add(1);
405 if rule_count.trailing_zeros() >= 4 {
406 if let Some(timeout_ms) = context.timeout_ms() {
407 if start_time.elapsed().as_millis() > u128::from(timeout_ms) {
408 return Err(LibmagicError::Timeout { timeout_ms });
409 }
410 }
411 }
412
413 let match_data = match evaluate_single_rule(rule, buffer) {
415 Ok(data) => data,
416 Err(
417 LibmagicError::EvaluationError(
418 crate::error::EvaluationError::BufferOverrun { .. }
419 | crate::error::EvaluationError::InvalidOffset { .. }
420 | crate::error::EvaluationError::TypeReadError(_),
421 )
422 | LibmagicError::IoError(_),
423 ) => {
424 continue;
426 }
427 Err(e) => {
428 return Err(e);
430 }
431 };
432
433 if let Some((absolute_offset, read_value)) = match_data {
434 let match_result = RuleMatch {
435 message: rule.message.clone(),
436 offset: absolute_offset,
437 level: rule.level,
438 value: read_value,
439 confidence: RuleMatch::calculate_confidence(rule.level),
440 };
441 matches.push(match_result);
442
443 if !rule.children.is_empty() {
445 context.increment_recursion_depth()?;
447
448 match evaluate_rules(&rule.children, buffer, context) {
450 Ok(child_matches) => {
451 matches.extend(child_matches);
452 }
453 Err(LibmagicError::Timeout { .. }) => {
454 context.decrement_recursion_depth()?;
456 return Err(LibmagicError::Timeout {
457 timeout_ms: context.timeout_ms().unwrap_or(0),
458 });
459 }
460 Err(LibmagicError::EvaluationError(
461 crate::error::EvaluationError::RecursionLimitExceeded { .. },
462 )) => {
463 context.decrement_recursion_depth()?;
465 return Err(LibmagicError::EvaluationError(
466 crate::error::EvaluationError::RecursionLimitExceeded {
467 depth: context.recursion_depth(),
468 },
469 ));
470 }
471 Err(
472 LibmagicError::EvaluationError(
473 crate::error::EvaluationError::BufferOverrun { .. }
474 | crate::error::EvaluationError::InvalidOffset { .. }
475 | crate::error::EvaluationError::TypeReadError(_),
476 )
477 | LibmagicError::IoError(_),
478 ) => {
479 }
481 Err(e) => {
482 context.decrement_recursion_depth()?;
484 return Err(e);
485 }
486 }
487
488 context.decrement_recursion_depth()?;
490 }
491
492 if context.should_stop_at_first_match() {
494 break;
495 }
496 }
497 }
498
499 Ok(matches)
500}
501
502pub fn evaluate_rules_with_config(
550 rules: &[MagicRule],
551 buffer: &[u8],
552 config: &EvaluationConfig,
553) -> Result<Vec<RuleMatch>, LibmagicError> {
554 let mut context = EvaluationContext::new(config.clone());
555 evaluate_rules(rules, buffer, &mut context)
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561 use crate::parser::ast::{Endianness, OffsetSpec, Operator, TypeKind, Value};
562
563 #[test]
564 fn test_evaluate_single_rule_byte_equal_match() {
565 let rule = MagicRule {
566 offset: OffsetSpec::Absolute(0),
567 typ: TypeKind::Byte { signed: true },
568 op: Operator::Equal,
569 value: Value::Uint(0x7f),
570 message: "ELF magic".to_string(),
571 children: vec![],
572 level: 0,
573 strength_modifier: None,
574 };
575
576 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
578 assert!(result.is_some());
579 }
580
581 #[test]
582 fn test_evaluate_single_rule_byte_equal_no_match() {
583 let rule = MagicRule {
584 offset: OffsetSpec::Absolute(0),
585 typ: TypeKind::Byte { signed: true },
586 op: Operator::Equal,
587 value: Value::Uint(0x7f),
588 message: "ELF magic".to_string(),
589 children: vec![],
590 level: 0,
591 strength_modifier: None,
592 };
593
594 let buffer = &[0x50, 0x4b, 0x03, 0x04]; let result = evaluate_single_rule(&rule, buffer).unwrap();
596 assert!(result.is_none());
597 }
598
599 #[test]
600 fn test_evaluate_single_rule_byte_not_equal_match() {
601 let rule = MagicRule {
602 offset: OffsetSpec::Absolute(0),
603 typ: TypeKind::Byte { signed: true },
604 op: Operator::NotEqual,
605 value: Value::Uint(0x00),
606 message: "Non-zero byte".to_string(),
607 children: vec![],
608 level: 0,
609 strength_modifier: None,
610 };
611
612 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
613 let result = evaluate_single_rule(&rule, buffer).unwrap();
614 assert!(result.is_some()); }
616
617 #[test]
618 fn test_evaluate_single_rule_byte_not_equal_no_match() {
619 let rule = MagicRule {
620 offset: OffsetSpec::Absolute(0),
621 typ: TypeKind::Byte { signed: true },
622 op: Operator::NotEqual,
623 value: Value::Uint(0x7f),
624 message: "Not ELF magic".to_string(),
625 children: vec![],
626 level: 0,
627 strength_modifier: None,
628 };
629
630 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
631 let result = evaluate_single_rule(&rule, buffer).unwrap();
632 assert!(result.is_none()); }
634
635 #[test]
636 fn test_evaluate_single_rule_byte_bitwise_and_match() {
637 let rule = MagicRule {
638 offset: OffsetSpec::Absolute(0),
639 typ: TypeKind::Byte { signed: true },
640 op: Operator::BitwiseAnd,
641 value: Value::Uint(0x80), message: "High bit set".to_string(),
643 children: vec![],
644 level: 0,
645 strength_modifier: None,
646 };
647
648 let buffer = &[0xff, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
650 assert!(result.is_some()); }
652
653 #[test]
654 fn test_evaluate_single_rule_byte_bitwise_and_no_match() {
655 let rule = MagicRule {
656 offset: OffsetSpec::Absolute(0),
657 typ: TypeKind::Byte { signed: true },
658 op: Operator::BitwiseAnd,
659 value: Value::Uint(0x80), message: "High bit set".to_string(),
661 children: vec![],
662 level: 0,
663 strength_modifier: None,
664 };
665
666 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
668 assert!(result.is_none()); }
670
671 #[test]
672 fn test_evaluate_single_rule_short_little_endian() {
673 let rule = MagicRule {
674 offset: OffsetSpec::Absolute(0),
675 typ: TypeKind::Short {
676 endian: Endianness::Little,
677 signed: false,
678 },
679 op: Operator::Equal,
680 value: Value::Uint(0x1234),
681 message: "Little-endian short".to_string(),
682 children: vec![],
683 level: 0,
684 strength_modifier: None,
685 };
686
687 let buffer = &[0x34, 0x12, 0x56, 0x78]; let result = evaluate_single_rule(&rule, buffer).unwrap();
689 assert!(result.is_some());
690 }
691
692 #[test]
693 fn test_evaluate_single_rule_short_big_endian() {
694 let rule = MagicRule {
695 offset: OffsetSpec::Absolute(0),
696 typ: TypeKind::Short {
697 endian: Endianness::Big,
698 signed: false,
699 },
700 op: Operator::Equal,
701 value: Value::Uint(0x1234),
702 message: "Big-endian short".to_string(),
703 children: vec![],
704 level: 0,
705 strength_modifier: None,
706 };
707
708 let buffer = &[0x12, 0x34, 0x56, 0x78]; let result = evaluate_single_rule(&rule, buffer).unwrap();
710 assert!(result.is_some());
711 }
712
713 #[test]
714 fn test_evaluate_single_rule_short_signed_positive() {
715 let rule = MagicRule {
716 offset: OffsetSpec::Absolute(0),
717 typ: TypeKind::Short {
718 endian: Endianness::Little,
719 signed: true,
720 },
721 op: Operator::Equal,
722 value: Value::Int(32767), message: "Positive signed short".to_string(),
724 children: vec![],
725 level: 0,
726 strength_modifier: None,
727 };
728
729 let buffer = &[0xff, 0x7f, 0x00, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
731 assert!(result.is_some());
732 }
733
734 #[test]
735 fn test_evaluate_single_rule_short_signed_negative() {
736 let rule = MagicRule {
737 offset: OffsetSpec::Absolute(0),
738 typ: TypeKind::Short {
739 endian: Endianness::Little,
740 signed: true,
741 },
742 op: Operator::Equal,
743 value: Value::Int(-1), message: "Negative signed short".to_string(),
745 children: vec![],
746 level: 0,
747 strength_modifier: None,
748 };
749
750 let buffer = &[0xff, 0xff, 0x00, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
752 assert!(result.is_some());
753 }
754
755 #[test]
756 fn test_evaluate_single_rule_long_little_endian() {
757 let rule = MagicRule {
758 offset: OffsetSpec::Absolute(0),
759 typ: TypeKind::Long {
760 endian: Endianness::Little,
761 signed: false,
762 },
763 op: Operator::Equal,
764 value: Value::Uint(0x1234_5678),
765 message: "Little-endian long".to_string(),
766 children: vec![],
767 level: 0,
768 strength_modifier: None,
769 };
770
771 let buffer = &[0x78, 0x56, 0x34, 0x12, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
773 assert!(result.is_some());
774 }
775
776 #[test]
777 fn test_evaluate_single_rule_long_big_endian() {
778 let rule = MagicRule {
779 offset: OffsetSpec::Absolute(0),
780 typ: TypeKind::Long {
781 endian: Endianness::Big,
782 signed: false,
783 },
784 op: Operator::Equal,
785 value: Value::Uint(0x1234_5678),
786 message: "Big-endian long".to_string(),
787 children: vec![],
788 level: 0,
789 strength_modifier: None,
790 };
791
792 let buffer = &[0x12, 0x34, 0x56, 0x78, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
794 assert!(result.is_some());
795 }
796
797 #[test]
798 fn test_evaluate_single_rule_long_signed_positive() {
799 let rule = MagicRule {
800 offset: OffsetSpec::Absolute(0),
801 typ: TypeKind::Long {
802 endian: Endianness::Little,
803 signed: true,
804 },
805 op: Operator::Equal,
806 value: Value::Int(2_147_483_647), message: "Positive signed long".to_string(),
808 children: vec![],
809 level: 0,
810 strength_modifier: None,
811 };
812
813 let buffer = &[0xff, 0xff, 0xff, 0x7f, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
815 assert!(result.is_some());
816 }
817
818 #[test]
819 fn test_evaluate_single_rule_long_signed_negative() {
820 let rule = MagicRule {
821 offset: OffsetSpec::Absolute(0),
822 typ: TypeKind::Long {
823 endian: Endianness::Little,
824 signed: true,
825 },
826 op: Operator::Equal,
827 value: Value::Int(-1), message: "Negative signed long".to_string(),
829 children: vec![],
830 level: 0,
831 strength_modifier: None,
832 };
833
834 let buffer = &[0xff, 0xff, 0xff, 0xff, 0x00]; let result = evaluate_single_rule(&rule, buffer).unwrap();
836 assert!(result.is_some());
837 }
838
839 #[test]
840 fn test_evaluate_single_rule_different_offsets() {
841 let rule = MagicRule {
842 offset: OffsetSpec::Absolute(2), typ: TypeKind::Byte { signed: true },
844 op: Operator::Equal,
845 value: Value::Uint(0x4c),
846 message: "ELF class byte".to_string(),
847 children: vec![],
848 level: 0,
849 strength_modifier: None,
850 };
851
852 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
854 assert!(result.is_some()); }
856
857 #[test]
858 fn test_evaluate_single_rule_negative_offset() {
859 let rule = MagicRule {
860 offset: OffsetSpec::Absolute(-1), typ: TypeKind::Byte { signed: true },
862 op: Operator::Equal,
863 value: Value::Uint(0x46),
864 message: "Last byte".to_string(),
865 children: vec![],
866 level: 0,
867 strength_modifier: None,
868 };
869
870 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
872 assert!(result.is_some()); }
874
875 #[test]
876 fn test_evaluate_single_rule_from_end_offset() {
877 let rule = MagicRule {
878 offset: OffsetSpec::FromEnd(-2), typ: TypeKind::Byte { signed: true },
880 op: Operator::Equal,
881 value: Value::Uint(0x4c),
882 message: "Second to last byte".to_string(),
883 children: vec![],
884 level: 0,
885 strength_modifier: None,
886 };
887
888 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer).unwrap();
890 assert!(result.is_some()); }
892
893 #[test]
894 fn test_evaluate_single_rule_offset_out_of_bounds() {
895 let rule = MagicRule {
896 offset: OffsetSpec::Absolute(10), typ: TypeKind::Byte { signed: true },
898 op: Operator::Equal,
899 value: Value::Uint(0x00),
900 message: "Out of bounds".to_string(),
901 children: vec![],
902 level: 0,
903 strength_modifier: None,
904 };
905
906 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer);
908 assert!(result.is_err());
909
910 match result.unwrap_err() {
911 LibmagicError::EvaluationError(msg) => {
912 let error_string = format!("{msg}");
913 assert!(error_string.contains("Buffer overrun"));
914 }
915 _ => panic!("Expected EvaluationError"),
916 }
917 }
918
919 #[test]
920 fn test_evaluate_single_rule_short_insufficient_bytes() {
921 let rule = MagicRule {
922 offset: OffsetSpec::Absolute(3), typ: TypeKind::Short {
924 endian: Endianness::Little,
925 signed: false,
926 },
927 op: Operator::Equal,
928 value: Value::Uint(0x1234),
929 message: "Insufficient bytes".to_string(),
930 children: vec![],
931 level: 0,
932 strength_modifier: None,
933 };
934
935 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let result = evaluate_single_rule(&rule, buffer);
937 assert!(result.is_err());
938
939 match result.unwrap_err() {
940 LibmagicError::EvaluationError(msg) => {
941 let error_string = format!("{msg}");
942 assert!(error_string.contains("Buffer overrun"));
943 }
944 _ => panic!("Expected EvaluationError"),
945 }
946 }
947
948 #[test]
949 fn test_evaluate_single_rule_long_insufficient_bytes() {
950 let rule = MagicRule {
951 offset: OffsetSpec::Absolute(2), typ: TypeKind::Long {
953 endian: Endianness::Little,
954 signed: false,
955 },
956 op: Operator::Equal,
957 value: Value::Uint(0x1234_5678),
958 message: "Insufficient bytes".to_string(),
959 children: vec![],
960 level: 0,
961 strength_modifier: None,
962 };
963
964 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; 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_empty_buffer() {
979 let rule = MagicRule {
980 offset: OffsetSpec::Absolute(0),
981 typ: TypeKind::Byte { signed: true },
982 op: Operator::Equal,
983 value: Value::Uint(0x00),
984 message: "Empty buffer".to_string(),
985 children: vec![],
986 level: 0,
987 strength_modifier: None,
988 };
989
990 let buffer = &[]; let result = evaluate_single_rule(&rule, buffer);
992 assert!(result.is_err());
993
994 match result.unwrap_err() {
995 LibmagicError::EvaluationError(msg) => {
996 let error_string = format!("{msg}");
997 assert!(error_string.contains("Buffer overrun"));
998 }
999 _ => panic!("Expected EvaluationError"),
1000 }
1001 }
1002
1003 #[test]
1004 fn test_evaluate_single_rule_string_type_supported() {
1005 let rule = MagicRule {
1006 offset: OffsetSpec::Absolute(0),
1007 typ: TypeKind::String { max_length: None },
1008 op: Operator::Equal,
1009 value: Value::String("test".to_string()),
1010 message: "String type".to_string(),
1011 children: vec![],
1012 level: 0,
1013 strength_modifier: None,
1014 };
1015
1016 let buffer = b"test\x00 data";
1018 let result = evaluate_single_rule(&rule, buffer);
1019 assert!(result.is_ok());
1020 let matches = result.unwrap();
1021 assert!(matches.is_some()); let rule_no_match = MagicRule {
1025 offset: OffsetSpec::Absolute(0),
1026 typ: TypeKind::String { max_length: None },
1027 op: Operator::Equal,
1028 value: Value::String("hello".to_string()),
1029 message: "String type".to_string(),
1030 children: vec![],
1031 level: 0,
1032 strength_modifier: None,
1033 };
1034
1035 let result = evaluate_single_rule(&rule_no_match, buffer);
1036 assert!(result.is_ok());
1037 let matches = result.unwrap();
1038 assert!(matches.is_none()); }
1040}
1041
1042#[test]
1043fn test_evaluate_single_rule_cross_type_comparison() {
1044 let rule = MagicRule {
1046 offset: OffsetSpec::Absolute(0),
1047 typ: TypeKind::Byte { signed: true },
1048 op: Operator::Equal,
1049 value: Value::Int(42), message: "Cross-type comparison".to_string(),
1051 children: vec![],
1052 level: 0,
1053 strength_modifier: None,
1054 };
1055
1056 let buffer = &[42]; let result = evaluate_single_rule(&rule, buffer).unwrap();
1058 assert!(result.is_some()); }
1060
1061#[test]
1062fn test_evaluate_single_rule_bitwise_and_with_shorts() {
1063 let rule = MagicRule {
1064 offset: OffsetSpec::Absolute(0),
1065 typ: TypeKind::Short {
1066 endian: Endianness::Little,
1067 signed: false,
1068 },
1069 op: Operator::BitwiseAnd,
1070 value: Value::Uint(0xff00), message: "High byte check".to_string(),
1072 children: vec![],
1073 level: 0,
1074 strength_modifier: None,
1075 };
1076
1077 let buffer = &[0x34, 0x12]; let result = evaluate_single_rule(&rule, buffer).unwrap();
1079 assert!(result.is_some()); }
1081
1082#[test]
1083fn test_evaluate_single_rule_bitwise_and_with_longs() {
1084 let rule = MagicRule {
1085 offset: OffsetSpec::Absolute(0),
1086 typ: TypeKind::Long {
1087 endian: Endianness::Big,
1088 signed: false,
1089 },
1090 op: Operator::BitwiseAnd,
1091 value: Value::Uint(0xffff_0000), message: "High word check".to_string(),
1093 children: vec![],
1094 level: 0,
1095 strength_modifier: None,
1096 };
1097
1098 let buffer = &[0x12, 0x34, 0x56, 0x78]; let result = evaluate_single_rule(&rule, buffer).unwrap();
1100 assert!(result.is_some()); }
1102
1103#[test]
1104fn test_evaluate_single_rule_comprehensive_elf_check() {
1105 let rule = MagicRule {
1107 offset: OffsetSpec::Absolute(0),
1108 typ: TypeKind::Long {
1109 endian: Endianness::Little,
1110 signed: false,
1111 },
1112 op: Operator::Equal,
1113 value: Value::Uint(0x464c_457f), message: "ELF executable".to_string(),
1115 children: vec![],
1116 level: 0,
1117 strength_modifier: None,
1118 };
1119
1120 let elf_buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let result = evaluate_single_rule(&rule, elf_buffer).unwrap();
1122 assert!(result.is_some());
1123
1124 let non_elf_buffer = &[0x50, 0x4b, 0x03, 0x04, 0x14, 0x00]; let result = evaluate_single_rule(&rule, non_elf_buffer).unwrap();
1126 assert!(result.is_none());
1127}
1128
1129#[test]
1130fn test_evaluate_single_rule_native_endianness() {
1131 let rule = MagicRule {
1132 offset: OffsetSpec::Absolute(0),
1133 typ: TypeKind::Short {
1134 endian: Endianness::Native,
1135 signed: false,
1136 },
1137 op: Operator::NotEqual,
1138 value: Value::Uint(0),
1139 message: "Non-zero native short".to_string(),
1140 children: vec![],
1141 level: 0,
1142 strength_modifier: None,
1143 };
1144
1145 let buffer = &[0x01, 0x02]; let result = evaluate_single_rule(&rule, buffer).unwrap();
1147 assert!(result.is_some()); }
1149
1150#[test]
1151fn test_evaluate_single_rule_all_operators() {
1152 let buffer = &[0x42, 0x00, 0xff, 0x80];
1153
1154 let equal_rule = MagicRule {
1156 offset: OffsetSpec::Absolute(0),
1157 typ: TypeKind::Byte { signed: true },
1158 op: Operator::Equal,
1159 value: Value::Uint(0x42),
1160 message: "Equal test".to_string(),
1161 children: vec![],
1162 level: 0,
1163 strength_modifier: None,
1164 };
1165 assert!(evaluate_single_rule(&equal_rule, buffer).unwrap().is_some());
1166
1167 let not_equal_rule = MagicRule {
1169 offset: OffsetSpec::Absolute(1),
1170 typ: TypeKind::Byte { signed: true },
1171 op: Operator::NotEqual,
1172 value: Value::Uint(0x42),
1173 message: "NotEqual test".to_string(),
1174 children: vec![],
1175 level: 0,
1176 strength_modifier: None,
1177 };
1178 assert!(
1179 evaluate_single_rule(¬_equal_rule, buffer)
1180 .unwrap()
1181 .is_some()
1182 ); let bitwise_and_rule = MagicRule {
1186 offset: OffsetSpec::Absolute(3),
1187 typ: TypeKind::Byte { signed: true },
1188 op: Operator::BitwiseAnd,
1189 value: Value::Uint(0x80),
1190 message: "BitwiseAnd test".to_string(),
1191 children: vec![],
1192 level: 0,
1193 strength_modifier: None,
1194 };
1195 assert!(
1196 evaluate_single_rule(&bitwise_and_rule, buffer)
1197 .unwrap()
1198 .is_some()
1199 ); }
1201
1202#[test]
1203fn test_evaluate_single_rule_comparison_operators() {
1204 let buffer = &[0x42, 0x00, 0xff, 0x80];
1205
1206 let less_than_rule = MagicRule {
1208 offset: OffsetSpec::Absolute(1),
1209 typ: TypeKind::Byte { signed: false },
1210 op: Operator::LessThan,
1211 value: Value::Uint(0x42),
1212 message: "LessThan test".to_string(),
1213 children: vec![],
1214 level: 0,
1215 strength_modifier: None,
1216 };
1217 assert!(
1218 evaluate_single_rule(&less_than_rule, buffer)
1219 .unwrap()
1220 .is_some()
1221 );
1222
1223 let greater_than_rule = MagicRule {
1225 offset: OffsetSpec::Absolute(2),
1226 typ: TypeKind::Byte { signed: false },
1227 op: Operator::GreaterThan,
1228 value: Value::Uint(0x42),
1229 message: "GreaterThan test".to_string(),
1230 children: vec![],
1231 level: 0,
1232 strength_modifier: None,
1233 };
1234 assert!(
1235 evaluate_single_rule(&greater_than_rule, buffer)
1236 .unwrap()
1237 .is_some()
1238 );
1239
1240 let less_equal_rule = MagicRule {
1242 offset: OffsetSpec::Absolute(0),
1243 typ: TypeKind::Byte { signed: false },
1244 op: Operator::LessEqual,
1245 value: Value::Uint(0x42),
1246 message: "LessEqual test".to_string(),
1247 children: vec![],
1248 level: 0,
1249 strength_modifier: None,
1250 };
1251 assert!(
1252 evaluate_single_rule(&less_equal_rule, buffer)
1253 .unwrap()
1254 .is_some()
1255 );
1256
1257 let greater_equal_rule = MagicRule {
1259 offset: OffsetSpec::Absolute(0),
1260 typ: TypeKind::Byte { signed: false },
1261 op: Operator::GreaterEqual,
1262 value: Value::Uint(0x42),
1263 message: "GreaterEqual test".to_string(),
1264 children: vec![],
1265 level: 0,
1266 strength_modifier: None,
1267 };
1268 assert!(
1269 evaluate_single_rule(&greater_equal_rule, buffer)
1270 .unwrap()
1271 .is_some()
1272 );
1273}
1274
1275#[test]
1276fn test_evaluate_comparison_with_signed_byte() {
1277 let buffer = &[0x80];
1279
1280 let signed_rule = MagicRule {
1282 offset: OffsetSpec::Absolute(0),
1283 typ: TypeKind::Byte { signed: true },
1284 op: Operator::LessThan,
1285 value: Value::Uint(0),
1286 message: "signed less".to_string(),
1287 children: vec![],
1288 level: 0,
1289 strength_modifier: None,
1290 };
1291 assert!(
1292 evaluate_single_rule(&signed_rule, buffer)
1293 .unwrap()
1294 .is_some()
1295 );
1296
1297 let unsigned_rule = MagicRule {
1299 offset: OffsetSpec::Absolute(0),
1300 typ: TypeKind::Byte { signed: false },
1301 op: Operator::LessThan,
1302 value: Value::Uint(0),
1303 message: "unsigned less".to_string(),
1304 children: vec![],
1305 level: 0,
1306 strength_modifier: None,
1307 };
1308 assert!(
1309 evaluate_single_rule(&unsigned_rule, buffer)
1310 .unwrap()
1311 .is_none()
1312 );
1313}
1314
1315#[test]
1316fn test_evaluate_comparison_operators_negative_cases() {
1317 let buffer = &[0x42]; let cases: Vec<(Operator, u64, bool)> = vec![
1320 (Operator::LessThan, 66, false),
1322 (Operator::LessThan, 67, true),
1323 (Operator::GreaterThan, 66, false),
1325 (Operator::GreaterThan, 65, true),
1326 (Operator::LessEqual, 65, false),
1328 (Operator::LessEqual, 66, true),
1329 (Operator::GreaterEqual, 67, false),
1331 (Operator::GreaterEqual, 66, true),
1332 ];
1333
1334 for (op, value, expected) in cases {
1335 let rule = MagicRule {
1336 offset: OffsetSpec::Absolute(0),
1337 typ: TypeKind::Byte { signed: false },
1338 op: op.clone(),
1339 value: Value::Uint(value),
1340 message: "test".to_string(),
1341 children: vec![],
1342 level: 0,
1343 strength_modifier: None,
1344 };
1345 let result = evaluate_single_rule(&rule, buffer).unwrap();
1346 assert_eq!(
1347 result.is_some(),
1348 expected,
1349 "{op:?} with value {value}: expected {expected}"
1350 );
1351 }
1352}
1353
1354#[test]
1355fn test_evaluate_single_rule_edge_case_values() {
1356 let max_uint_rule = MagicRule {
1358 offset: OffsetSpec::Absolute(0),
1359 typ: TypeKind::Long {
1360 endian: Endianness::Little,
1361 signed: false,
1362 },
1363 op: Operator::Equal,
1364 value: Value::Uint(0xffff_ffff),
1365 message: "Max uint32".to_string(),
1366 children: vec![],
1367 level: 0,
1368 strength_modifier: None,
1369 };
1370
1371 let max_buffer = &[0xff, 0xff, 0xff, 0xff];
1372 let result = evaluate_single_rule(&max_uint_rule, max_buffer).unwrap();
1373 assert!(result.is_some());
1374
1375 let min_int_rule = MagicRule {
1377 offset: OffsetSpec::Absolute(0),
1378 typ: TypeKind::Long {
1379 endian: Endianness::Little,
1380 signed: true,
1381 },
1382 op: Operator::Equal,
1383 value: Value::Int(-2_147_483_648), message: "Min int32".to_string(),
1385 children: vec![],
1386 level: 0,
1387 strength_modifier: None,
1388 };
1389
1390 let min_buffer = &[0x00, 0x00, 0x00, 0x80]; let result = evaluate_single_rule(&min_int_rule, min_buffer).unwrap();
1392 assert!(result.is_some());
1393}
1394
1395#[test]
1396fn test_evaluate_single_rule_various_buffer_sizes() {
1397 let single_byte_rule = MagicRule {
1399 offset: OffsetSpec::Absolute(0),
1400 typ: TypeKind::Byte { signed: false },
1401 op: Operator::Equal,
1402 value: Value::Uint(0xaa),
1403 message: "Single byte".to_string(),
1404 children: vec![],
1405 level: 0,
1406 strength_modifier: None,
1407 };
1408
1409 let single_buffer = &[0xaa];
1410 let result = evaluate_single_rule(&single_byte_rule, single_buffer).unwrap();
1411 assert!(result.is_some());
1412
1413 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
1415 let large_buffer: Vec<u8> = (0..1024).map(|i| (i % 256) as u8).collect();
1416 let large_rule = MagicRule {
1417 offset: OffsetSpec::Absolute(1000),
1418 typ: TypeKind::Byte { signed: false },
1419 op: Operator::Equal,
1420 value: Value::Uint((1000 % 256) as u64),
1421 message: "Large buffer".to_string(),
1422 children: vec![],
1423 level: 0,
1424 strength_modifier: None,
1425 };
1426
1427 let result = evaluate_single_rule(&large_rule, &large_buffer).unwrap();
1428 assert!(result.is_some());
1429}
1430
1431#[test]
1433fn test_evaluation_context_new() {
1434 let config = EvaluationConfig::default();
1435 let context = EvaluationContext::new(config.clone());
1436
1437 assert_eq!(context.current_offset(), 0);
1438 assert_eq!(context.recursion_depth(), 0);
1439 assert_eq!(
1440 context.config().max_recursion_depth,
1441 config.max_recursion_depth
1442 );
1443 assert_eq!(context.config().max_string_length, config.max_string_length);
1444 assert_eq!(
1445 context.config().stop_at_first_match,
1446 config.stop_at_first_match
1447 );
1448}
1449
1450#[test]
1451fn test_evaluation_context_offset_management() {
1452 let config = EvaluationConfig::default();
1453 let mut context = EvaluationContext::new(config);
1454
1455 assert_eq!(context.current_offset(), 0);
1457
1458 context.set_current_offset(42);
1460 assert_eq!(context.current_offset(), 42);
1461
1462 context.set_current_offset(1024);
1464 assert_eq!(context.current_offset(), 1024);
1465
1466 context.set_current_offset(0);
1468 assert_eq!(context.current_offset(), 0);
1469}
1470
1471#[test]
1472fn test_evaluation_context_recursion_depth_management() {
1473 let config = EvaluationConfig::default();
1474 let mut context = EvaluationContext::new(config);
1475
1476 assert_eq!(context.recursion_depth(), 0);
1478
1479 context.increment_recursion_depth().unwrap();
1481 assert_eq!(context.recursion_depth(), 1);
1482
1483 context.increment_recursion_depth().unwrap();
1484 assert_eq!(context.recursion_depth(), 2);
1485
1486 context.decrement_recursion_depth().unwrap();
1488 assert_eq!(context.recursion_depth(), 1);
1489
1490 context.decrement_recursion_depth().unwrap();
1491 assert_eq!(context.recursion_depth(), 0);
1492}
1493
1494#[test]
1495fn test_evaluation_context_recursion_depth_limit() {
1496 let config = EvaluationConfig {
1497 max_recursion_depth: 2,
1498 ..Default::default()
1499 };
1500 let mut context = EvaluationContext::new(config);
1501
1502 assert!(context.increment_recursion_depth().is_ok());
1504 assert_eq!(context.recursion_depth(), 1);
1505
1506 assert!(context.increment_recursion_depth().is_ok());
1507 assert_eq!(context.recursion_depth(), 2);
1508
1509 let result = context.increment_recursion_depth();
1511 assert!(result.is_err());
1512 assert_eq!(context.recursion_depth(), 2); match result.unwrap_err() {
1515 LibmagicError::EvaluationError(msg) => {
1516 let error_string = format!("{msg}");
1517 assert!(error_string.contains("Recursion limit exceeded"));
1518 }
1519 _ => panic!("Expected EvaluationError"),
1520 }
1521}
1522
1523#[test]
1524fn test_evaluation_context_recursion_depth_underflow() {
1525 let config = EvaluationConfig::default();
1526 let mut context = EvaluationContext::new(config);
1527
1528 let result = context.decrement_recursion_depth();
1530 assert!(result.is_err());
1531
1532 let err = result.unwrap_err();
1533 let err_msg = err.to_string();
1534 assert!(
1535 err_msg.contains("decrement recursion depth below 0"),
1536 "Expected error about decrementing below 0, got: {err_msg}"
1537 );
1538}
1539
1540#[test]
1541fn test_evaluation_context_config_access() {
1542 let config = EvaluationConfig {
1543 max_recursion_depth: 10,
1544 max_string_length: 4096,
1545 stop_at_first_match: false,
1546 enable_mime_types: true,
1547 timeout_ms: Some(2000),
1548 };
1549
1550 let context = EvaluationContext::new(config);
1551
1552 assert_eq!(context.config().max_recursion_depth, 10);
1554 assert_eq!(context.config().max_string_length, 4096);
1555 assert!(!context.config().stop_at_first_match);
1556
1557 assert!(!context.should_stop_at_first_match());
1559 assert_eq!(context.max_string_length(), 4096);
1560}
1561
1562#[test]
1563fn test_evaluation_context_reset() {
1564 let config = EvaluationConfig::default();
1565 let mut context = EvaluationContext::new(config.clone());
1566
1567 context.set_current_offset(100);
1569 context.increment_recursion_depth().unwrap();
1570 context.increment_recursion_depth().unwrap();
1571
1572 assert_eq!(context.current_offset(), 100);
1573 assert_eq!(context.recursion_depth(), 2);
1574
1575 context.reset();
1577
1578 assert_eq!(context.current_offset(), 0);
1579 assert_eq!(context.recursion_depth(), 0);
1580 assert_eq!(
1581 context.config().max_recursion_depth,
1582 config.max_recursion_depth
1583 );
1584}
1585
1586#[test]
1587fn test_evaluation_context_clone() {
1588 let config = EvaluationConfig {
1589 max_recursion_depth: 5,
1590 max_string_length: 2048,
1591 ..Default::default()
1592 };
1593
1594 let mut context = EvaluationContext::new(config);
1595 context.set_current_offset(50);
1596 context.increment_recursion_depth().unwrap();
1597
1598 let cloned_context = context.clone();
1600
1601 assert_eq!(context.current_offset(), cloned_context.current_offset());
1603 assert_eq!(context.recursion_depth(), cloned_context.recursion_depth());
1604 assert_eq!(
1605 context.config().max_recursion_depth,
1606 cloned_context.config().max_recursion_depth
1607 );
1608 assert_eq!(
1609 context.config().max_string_length,
1610 cloned_context.config().max_string_length
1611 );
1612
1613 context.set_current_offset(75);
1615 assert_eq!(context.current_offset(), 75);
1616 assert_eq!(cloned_context.current_offset(), 50);
1617}
1618
1619#[test]
1620fn test_evaluation_context_with_custom_config() {
1621 let config = EvaluationConfig {
1622 max_recursion_depth: 15,
1623 max_string_length: 16384,
1624 stop_at_first_match: false,
1625 enable_mime_types: true,
1626 timeout_ms: Some(5000),
1627 };
1628
1629 let context = EvaluationContext::new(config);
1630
1631 assert_eq!(context.config().max_recursion_depth, 15);
1632 assert_eq!(context.max_string_length(), 16384);
1633 assert!(!context.should_stop_at_first_match());
1634
1635 let mut mutable_context = context;
1637 for i in 1..=15 {
1638 assert!(mutable_context.increment_recursion_depth().is_ok());
1639 assert_eq!(mutable_context.recursion_depth(), i);
1640 }
1641
1642 let result = mutable_context.increment_recursion_depth();
1644 assert!(result.is_err());
1645}
1646
1647#[test]
1648fn test_evaluation_context_mime_types_access() {
1649 let config_with_mime = EvaluationConfig {
1650 enable_mime_types: true,
1651 ..Default::default()
1652 };
1653 let context_with_mime = EvaluationContext::new(config_with_mime);
1654 assert!(context_with_mime.enable_mime_types());
1655
1656 let config_without_mime = EvaluationConfig {
1657 enable_mime_types: false,
1658 ..Default::default()
1659 };
1660 let context_without_mime = EvaluationContext::new(config_without_mime);
1661 assert!(!context_without_mime.enable_mime_types());
1662}
1663
1664#[test]
1665fn test_evaluation_context_timeout_access() {
1666 let config_with_timeout = EvaluationConfig {
1667 timeout_ms: Some(5000),
1668 ..Default::default()
1669 };
1670 let context_with_timeout = EvaluationContext::new(config_with_timeout);
1671 assert_eq!(context_with_timeout.timeout_ms(), Some(5000));
1672
1673 let config_without_timeout = EvaluationConfig {
1674 timeout_ms: None,
1675 ..Default::default()
1676 };
1677 let context_without_timeout = EvaluationContext::new(config_without_timeout);
1678 assert_eq!(context_without_timeout.timeout_ms(), None);
1679}
1680
1681#[test]
1682fn test_evaluation_context_comprehensive_config() {
1683 let config = EvaluationConfig {
1684 max_recursion_depth: 30,
1685 max_string_length: 16384,
1686 stop_at_first_match: false,
1687 enable_mime_types: true,
1688 timeout_ms: Some(10000),
1689 };
1690 let context = EvaluationContext::new(config);
1691
1692 assert_eq!(context.config().max_recursion_depth, 30);
1693 assert_eq!(context.config().max_string_length, 16384);
1694 assert!(!context.should_stop_at_first_match());
1695 assert!(context.enable_mime_types());
1696 assert_eq!(context.timeout_ms(), Some(10000));
1697 assert_eq!(context.max_string_length(), 16384);
1698}
1699
1700#[test]
1701fn test_evaluation_context_performance_config() {
1702 let config = EvaluationConfig {
1703 max_recursion_depth: 5,
1704 max_string_length: 512,
1705 stop_at_first_match: true,
1706 enable_mime_types: false,
1707 timeout_ms: Some(1000),
1708 };
1709 let context = EvaluationContext::new(config);
1710
1711 assert_eq!(context.config().max_recursion_depth, 5);
1712 assert_eq!(context.max_string_length(), 512);
1713 assert!(context.should_stop_at_first_match());
1714 assert!(!context.enable_mime_types());
1715 assert_eq!(context.timeout_ms(), Some(1000));
1716}
1717
1718#[test]
1719fn test_match_result_creation() {
1720 let match_result = RuleMatch {
1721 message: "ELF executable".to_string(),
1722 offset: 0,
1723 level: 0,
1724 value: Value::Uint(0x7f),
1725 confidence: RuleMatch::calculate_confidence(0),
1726 };
1727
1728 assert_eq!(match_result.message, "ELF executable");
1729 assert_eq!(match_result.offset, 0);
1730 assert_eq!(match_result.level, 0);
1731 assert_eq!(match_result.value, Value::Uint(0x7f));
1732 assert!((match_result.confidence - 0.3).abs() < 0.001);
1733}
1734
1735#[test]
1736fn test_match_result_clone() {
1737 let original = RuleMatch {
1738 message: "Test message".to_string(),
1739 offset: 42,
1740 level: 1,
1741 value: Value::String("test".to_string()),
1742 confidence: RuleMatch::calculate_confidence(1),
1743 };
1744
1745 let cloned = original.clone();
1746 assert_eq!(original, cloned);
1747}
1748
1749#[test]
1750fn test_match_result_debug() {
1751 let match_result = RuleMatch {
1752 message: "Debug test".to_string(),
1753 offset: 10,
1754 level: 2,
1755 value: Value::Bytes(vec![0x01, 0x02]),
1756 confidence: RuleMatch::calculate_confidence(2),
1757 };
1758
1759 let debug_str = format!("{match_result:?}");
1760 assert!(debug_str.contains("RuleMatch"));
1761 assert!(debug_str.contains("Debug test"));
1762 assert!(debug_str.contains("10"));
1763 assert!(debug_str.contains('2'));
1764}
1765
1766#[test]
1767fn test_confidence_calculation_depth_0() {
1768 let confidence = RuleMatch::calculate_confidence(0);
1769 assert!((confidence - 0.3).abs() < 0.001);
1770}
1771
1772#[test]
1773fn test_confidence_calculation_depth_1() {
1774 let confidence = RuleMatch::calculate_confidence(1);
1775 assert!((confidence - 0.5).abs() < 0.001);
1776}
1777
1778#[test]
1779fn test_confidence_calculation_depth_2() {
1780 let confidence = RuleMatch::calculate_confidence(2);
1781 assert!((confidence - 0.7).abs() < 0.001);
1782}
1783
1784#[test]
1785fn test_confidence_calculation_depth_3() {
1786 let confidence = RuleMatch::calculate_confidence(3);
1787 assert!((confidence - 0.9).abs() < 0.001);
1788}
1789
1790#[test]
1791fn test_confidence_calculation_capped_at_1() {
1792 let confidence_4 = RuleMatch::calculate_confidence(4);
1794 assert!((confidence_4 - 1.0).abs() < 0.001);
1795
1796 let confidence_10 = RuleMatch::calculate_confidence(10);
1797 assert!((confidence_10 - 1.0).abs() < 0.001);
1798
1799 let confidence_100 = RuleMatch::calculate_confidence(100);
1800 assert!((confidence_100 - 1.0).abs() < 0.001);
1801}
1802
1803#[test]
1804fn test_evaluate_rules_empty_list() {
1805 let rules = vec![];
1806 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1807 let config = EvaluationConfig::default();
1808 let mut context = EvaluationContext::new(config);
1809
1810 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1811 assert!(matches.is_empty());
1812}
1813
1814#[test]
1815fn test_evaluate_rules_single_matching_rule() {
1816 let rule = MagicRule {
1817 offset: OffsetSpec::Absolute(0),
1818 typ: TypeKind::Byte { signed: true },
1819 op: Operator::Equal,
1820 value: Value::Uint(0x7f),
1821 message: "ELF magic".to_string(),
1822 children: vec![],
1823 level: 0,
1824 strength_modifier: None,
1825 };
1826
1827 let rules = vec![rule];
1828 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1829 let config = EvaluationConfig::default();
1830 let mut context = EvaluationContext::new(config);
1831
1832 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1833 assert_eq!(matches.len(), 1);
1834 assert_eq!(matches[0].message, "ELF magic");
1835 assert_eq!(matches[0].offset, 0);
1836 assert_eq!(matches[0].level, 0);
1837 assert_eq!(matches[0].value, Value::Int(0x7f));
1839}
1840
1841#[test]
1842fn test_evaluate_rules_single_non_matching_rule() {
1843 let rule = MagicRule {
1844 offset: OffsetSpec::Absolute(0),
1845 typ: TypeKind::Byte { signed: true },
1846 op: Operator::Equal,
1847 value: Value::Uint(0x50), message: "ZIP magic".to_string(),
1849 children: vec![],
1850 level: 0,
1851 strength_modifier: None,
1852 };
1853
1854 let rules = vec![rule];
1855 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let config = EvaluationConfig::default();
1857 let mut context = EvaluationContext::new(config);
1858
1859 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1860 assert!(matches.is_empty());
1861}
1862
1863#[test]
1864fn test_evaluate_rules_multiple_rules_stop_at_first() {
1865 let rule1 = MagicRule {
1866 offset: OffsetSpec::Absolute(0),
1867 typ: TypeKind::Byte { signed: true },
1868 op: Operator::Equal,
1869 value: Value::Uint(0x7f),
1870 message: "First match".to_string(),
1871 children: vec![],
1872 level: 0,
1873 strength_modifier: None,
1874 };
1875
1876 let rule2 = MagicRule {
1877 offset: OffsetSpec::Absolute(1),
1878 typ: TypeKind::Byte { signed: true },
1879 op: Operator::Equal,
1880 value: Value::Uint(0x45),
1881 message: "Second match".to_string(),
1882 children: vec![],
1883 level: 0,
1884 strength_modifier: None,
1885 };
1886
1887 let rule_list = vec![rule1, rule2];
1888 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1889 let config = EvaluationConfig {
1890 stop_at_first_match: true,
1891 ..Default::default()
1892 };
1893 let mut context = EvaluationContext::new(config);
1894
1895 let matches = evaluate_rules(&rule_list, buffer, &mut context).unwrap();
1896 assert_eq!(matches.len(), 1);
1897 assert_eq!(matches[0].message, "First match");
1898}
1899
1900#[test]
1901fn test_evaluate_rules_multiple_rules_find_all() {
1902 let rule1 = MagicRule {
1903 offset: OffsetSpec::Absolute(0),
1904 typ: TypeKind::Byte { signed: true },
1905 op: Operator::Equal,
1906 value: Value::Uint(0x7f),
1907 message: "First match".to_string(),
1908 children: vec![],
1909 level: 0,
1910 strength_modifier: None,
1911 };
1912
1913 let rule2 = MagicRule {
1914 offset: OffsetSpec::Absolute(1),
1915 typ: TypeKind::Byte { signed: true },
1916 op: Operator::Equal,
1917 value: Value::Uint(0x45),
1918 message: "Second match".to_string(),
1919 children: vec![],
1920 level: 0,
1921 strength_modifier: None,
1922 };
1923
1924 let rule_set = vec![rule1, rule2];
1925 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1926 let config = EvaluationConfig {
1927 stop_at_first_match: false,
1928 ..Default::default()
1929 };
1930 let mut context = EvaluationContext::new(config);
1931
1932 let matches = evaluate_rules(&rule_set, buffer, &mut context).unwrap();
1933 assert_eq!(matches.len(), 2);
1934 assert_eq!(matches[0].message, "First match");
1935 assert_eq!(matches[1].message, "Second match");
1936}
1937
1938#[test]
1939fn test_evaluate_rules_hierarchical_parent_child() {
1940 let child_rule = MagicRule {
1941 offset: OffsetSpec::Absolute(4),
1942 typ: TypeKind::Byte { signed: true },
1943 op: Operator::Equal,
1944 value: Value::Uint(0x02), message: "64-bit".to_string(),
1946 children: vec![],
1947 level: 1,
1948 strength_modifier: None,
1949 };
1950
1951 let parent_rule = MagicRule {
1952 offset: OffsetSpec::Absolute(0),
1953 typ: TypeKind::Byte { signed: true },
1954 op: Operator::Equal,
1955 value: Value::Uint(0x7f),
1956 message: "ELF".to_string(),
1957 children: vec![child_rule],
1958 level: 0,
1959 strength_modifier: None,
1960 };
1961
1962 let rules = vec![parent_rule];
1963 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let config = EvaluationConfig::default();
1965 let mut context = EvaluationContext::new(config);
1966
1967 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1968 assert_eq!(matches.len(), 2);
1969 assert_eq!(matches[0].message, "ELF");
1970 assert_eq!(matches[0].level, 0);
1971 assert_eq!(matches[1].message, "64-bit");
1972 assert_eq!(matches[1].level, 1);
1973}
1974
1975#[test]
1976fn test_evaluate_rules_hierarchical_parent_no_match() {
1977 let child_rule = MagicRule {
1978 offset: OffsetSpec::Absolute(4),
1979 typ: TypeKind::Byte { signed: true },
1980 op: Operator::Equal,
1981 value: Value::Uint(0x02),
1982 message: "64-bit".to_string(),
1983 children: vec![],
1984 level: 1,
1985 strength_modifier: None,
1986 };
1987
1988 let parent_rule = MagicRule {
1989 offset: OffsetSpec::Absolute(0),
1990 typ: TypeKind::Byte { signed: true },
1991 op: Operator::Equal,
1992 value: Value::Uint(0x50), message: "ZIP".to_string(),
1994 children: vec![child_rule],
1995 level: 0,
1996 strength_modifier: None,
1997 };
1998
1999 let rules = vec![parent_rule];
2000 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let config = EvaluationConfig::default();
2002 let mut context = EvaluationContext::new(config);
2003
2004 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2005 assert!(matches.is_empty()); }
2007
2008#[test]
2009fn test_evaluate_rules_hierarchical_parent_match_child_no_match() {
2010 let child_rule = MagicRule {
2011 offset: OffsetSpec::Absolute(4),
2012 typ: TypeKind::Byte { signed: true },
2013 op: Operator::Equal,
2014 value: Value::Uint(0x01), message: "32-bit".to_string(),
2016 children: vec![],
2017 level: 1,
2018 strength_modifier: None,
2019 };
2020
2021 let parent_rule = MagicRule {
2022 offset: OffsetSpec::Absolute(0),
2023 typ: TypeKind::Byte { signed: true },
2024 op: Operator::Equal,
2025 value: Value::Uint(0x7f),
2026 message: "ELF".to_string(),
2027 children: vec![child_rule],
2028 level: 0,
2029 strength_modifier: None,
2030 };
2031
2032 let rules = vec![parent_rule];
2033 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let config = EvaluationConfig::default();
2035 let mut context = EvaluationContext::new(config);
2036
2037 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2038 assert_eq!(matches.len(), 1); assert_eq!(matches[0].message, "ELF");
2040 assert_eq!(matches[0].level, 0);
2041}
2042
2043#[test]
2044fn test_evaluate_rules_deep_hierarchy() {
2045 let grandchild_rule = MagicRule {
2046 offset: OffsetSpec::Absolute(5),
2047 typ: TypeKind::Byte { signed: true },
2048 op: Operator::Equal,
2049 value: Value::Uint(0x01), message: "little-endian".to_string(),
2051 children: vec![],
2052 level: 2,
2053 strength_modifier: None,
2054 };
2055
2056 let child_rule = MagicRule {
2057 offset: OffsetSpec::Absolute(4),
2058 typ: TypeKind::Byte { signed: true },
2059 op: Operator::Equal,
2060 value: Value::Uint(0x02), message: "64-bit".to_string(),
2062 children: vec![grandchild_rule],
2063 level: 1,
2064 strength_modifier: None,
2065 };
2066
2067 let parent_rule = MagicRule {
2068 offset: OffsetSpec::Absolute(0),
2069 typ: TypeKind::Byte { signed: true },
2070 op: Operator::Equal,
2071 value: Value::Uint(0x7f),
2072 message: "ELF".to_string(),
2073 children: vec![child_rule],
2074 level: 0,
2075 strength_modifier: None,
2076 };
2077
2078 let rules = vec![parent_rule];
2079 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; let config = EvaluationConfig::default();
2081 let mut context = EvaluationContext::new(config);
2082
2083 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2084 assert_eq!(matches.len(), 3);
2085 assert_eq!(matches[0].message, "ELF");
2086 assert_eq!(matches[0].level, 0);
2087 assert_eq!(matches[1].message, "64-bit");
2088 assert_eq!(matches[1].level, 1);
2089 assert_eq!(matches[2].message, "little-endian");
2090 assert_eq!(matches[2].level, 2);
2091}
2092
2093#[test]
2094fn test_evaluate_rules_multiple_children() {
2095 let child1 = MagicRule {
2096 offset: OffsetSpec::Absolute(4),
2097 typ: TypeKind::Byte { signed: true },
2098 op: Operator::Equal,
2099 value: Value::Uint(0x02),
2100 message: "64-bit".to_string(),
2101 children: vec![],
2102 level: 1,
2103 strength_modifier: None,
2104 };
2105
2106 let child2 = MagicRule {
2107 offset: OffsetSpec::Absolute(5),
2108 typ: TypeKind::Byte { signed: true },
2109 op: Operator::Equal,
2110 value: Value::Uint(0x01),
2111 message: "little-endian".to_string(),
2112 children: vec![],
2113 level: 1,
2114 strength_modifier: None,
2115 };
2116
2117 let parent_rule = MagicRule {
2118 offset: OffsetSpec::Absolute(0),
2119 typ: TypeKind::Byte { signed: true },
2120 op: Operator::Equal,
2121 value: Value::Uint(0x7f),
2122 message: "ELF".to_string(),
2123 children: vec![child1, child2],
2124 level: 0,
2125 strength_modifier: None,
2126 };
2127
2128 let rules = vec![parent_rule];
2129 let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01];
2130 let config = EvaluationConfig {
2131 stop_at_first_match: false, ..Default::default()
2133 };
2134 let mut context = EvaluationContext::new(config);
2135
2136 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2137 assert_eq!(matches.len(), 3);
2138 assert_eq!(matches[0].message, "ELF");
2139 assert_eq!(matches[1].message, "64-bit");
2140 assert_eq!(matches[2].message, "little-endian");
2141}
2142
2143#[test]
2144fn test_evaluate_rules_recursion_depth_limit() {
2145 let mut current_rule = MagicRule {
2147 offset: OffsetSpec::Absolute(10),
2148 typ: TypeKind::Byte { signed: true },
2149 op: Operator::Equal,
2150 value: Value::Uint(0x00),
2151 message: "Deep level".to_string(),
2152 children: vec![],
2153 level: 10,
2154 strength_modifier: None,
2155 };
2156
2157 for i in (0u32..10u32).rev() {
2159 current_rule = MagicRule {
2160 offset: OffsetSpec::Absolute(i64::from(i)),
2161 typ: TypeKind::Byte { signed: true },
2162 op: Operator::Equal,
2163 value: Value::Uint(u64::from(i)),
2164 message: format!("Level {i}"),
2165 children: vec![current_rule],
2166 level: i,
2167 strength_modifier: None,
2168 };
2169 }
2170
2171 let rules = vec![current_rule];
2172 let buffer = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; let config = EvaluationConfig {
2174 max_recursion_depth: 5, ..Default::default()
2176 };
2177 let mut context = EvaluationContext::new(config);
2178
2179 let result = evaluate_rules(&rules, buffer, &mut context);
2180 assert!(result.is_err());
2181
2182 match result.unwrap_err() {
2183 LibmagicError::EvaluationError(msg) => {
2184 let error_string = format!("{msg}");
2185 assert!(error_string.contains("Recursion limit exceeded"));
2186 }
2187 _ => panic!("Expected EvaluationError for recursion limit"),
2188 }
2189}
2190
2191#[test]
2192fn test_evaluate_rules_with_config_convenience() {
2193 let rule = MagicRule {
2194 offset: OffsetSpec::Absolute(0),
2195 typ: TypeKind::Byte { signed: true },
2196 op: Operator::Equal,
2197 value: Value::Uint(0x7f),
2198 message: "ELF magic".to_string(),
2199 children: vec![],
2200 level: 0,
2201 strength_modifier: None,
2202 };
2203
2204 let rules = vec![rule];
2205 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2206 let config = EvaluationConfig::default();
2207
2208 let matches = evaluate_rules_with_config(&rules, buffer, &config).unwrap();
2209 assert_eq!(matches.len(), 1);
2210 assert_eq!(matches[0].message, "ELF magic");
2211}
2212
2213#[test]
2214fn test_evaluate_rules_timeout() {
2215 let rule = MagicRule {
2216 offset: OffsetSpec::Absolute(0),
2217 typ: TypeKind::Byte { signed: true },
2218 op: Operator::Equal,
2219 value: Value::Uint(0x7f),
2220 message: "ELF magic".to_string(),
2221 children: vec![],
2222 level: 0,
2223 strength_modifier: None,
2224 };
2225
2226 let rules = vec![rule];
2227 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2228 let config = EvaluationConfig {
2229 timeout_ms: Some(0), ..Default::default()
2231 };
2232 let mut context = EvaluationContext::new(config);
2233
2234 let result = evaluate_rules(&rules, buffer, &mut context);
2236 if let Err(LibmagicError::Timeout { timeout_ms }) = result {
2239 assert_eq!(timeout_ms, 0);
2240 }
2241}
2242
2243#[test]
2244fn test_evaluate_rules_empty_buffer() {
2245 let rule = MagicRule {
2246 offset: OffsetSpec::Absolute(0),
2247 typ: TypeKind::Byte { signed: true },
2248 op: Operator::Equal,
2249 value: Value::Uint(0x7f),
2250 message: "Should not match".to_string(),
2251 children: vec![],
2252 level: 0,
2253 strength_modifier: None,
2254 };
2255
2256 let rules = vec![rule];
2257 let buffer = &[]; let config = EvaluationConfig::default();
2259 let mut context = EvaluationContext::new(config);
2260
2261 let result = evaluate_rules(&rules, buffer, &mut context);
2263 assert!(result.is_ok());
2264
2265 let matches = result.unwrap();
2266 assert_eq!(matches.len(), 0); }
2268
2269#[test]
2270fn test_evaluate_rules_mixed_matching_non_matching() {
2271 let rule1 = MagicRule {
2272 offset: OffsetSpec::Absolute(0),
2273 typ: TypeKind::Byte { signed: true },
2274 op: Operator::Equal,
2275 value: Value::Uint(0x7f),
2276 message: "Matches".to_string(),
2277 children: vec![],
2278 level: 0,
2279 strength_modifier: None,
2280 };
2281
2282 let rule2 = MagicRule {
2283 offset: OffsetSpec::Absolute(1),
2284 typ: TypeKind::Byte { signed: true },
2285 op: Operator::Equal,
2286 value: Value::Uint(0x99), message: "Doesn't match".to_string(),
2288 children: vec![],
2289 level: 0,
2290 strength_modifier: None,
2291 };
2292
2293 let rule3 = MagicRule {
2294 offset: OffsetSpec::Absolute(2),
2295 typ: TypeKind::Byte { signed: true },
2296 op: Operator::Equal,
2297 value: Value::Uint(0x4c),
2298 message: "Also matches".to_string(),
2299 children: vec![],
2300 level: 0,
2301 strength_modifier: None,
2302 };
2303
2304 let rule_collection = vec![rule1, rule2, rule3];
2305 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2306 let config = EvaluationConfig {
2307 stop_at_first_match: false,
2308 ..Default::default()
2309 };
2310 let mut context = EvaluationContext::new(config);
2311
2312 let matches = evaluate_rules(&rule_collection, buffer, &mut context).unwrap();
2313 assert_eq!(matches.len(), 2);
2314 assert_eq!(matches[0].message, "Matches");
2315 assert_eq!(matches[1].message, "Also matches");
2316}
2317
2318#[test]
2319fn test_evaluate_rules_context_state_preservation() {
2320 let rule = MagicRule {
2321 offset: OffsetSpec::Absolute(0),
2322 typ: TypeKind::Byte { signed: true },
2323 op: Operator::Equal,
2324 value: Value::Uint(0x7f),
2325 message: "ELF magic".to_string(),
2326 children: vec![],
2327 level: 0,
2328 strength_modifier: None,
2329 };
2330
2331 let rules = vec![rule];
2332 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2333 let config = EvaluationConfig::default();
2334 let mut context = EvaluationContext::new(config);
2335
2336 context.set_current_offset(100);
2338 let initial_offset = context.current_offset();
2339 let initial_depth = context.recursion_depth();
2340
2341 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2342 assert_eq!(matches.len(), 1);
2343
2344 assert_eq!(context.current_offset(), initial_offset);
2346 assert_eq!(context.recursion_depth(), initial_depth);
2347}
2348
2349#[test]
2350fn test_evaluation_context_state_management_sequence() {
2351 let config = EvaluationConfig::default();
2352 let mut context = EvaluationContext::new(config);
2353
2354 assert_eq!(context.current_offset(), 0);
2356 assert_eq!(context.recursion_depth(), 0);
2357
2358 context.set_current_offset(10);
2360 assert_eq!(context.current_offset(), 10);
2361
2362 context.increment_recursion_depth().unwrap();
2364 assert_eq!(context.recursion_depth(), 1);
2365
2366 context.set_current_offset(25);
2368 assert_eq!(context.current_offset(), 25);
2369
2370 context.increment_recursion_depth().unwrap();
2372 assert_eq!(context.recursion_depth(), 2);
2373
2374 context.decrement_recursion_depth().unwrap();
2376 assert_eq!(context.recursion_depth(), 1);
2377
2378 context.set_current_offset(50);
2380 assert_eq!(context.current_offset(), 50);
2381
2382 context.decrement_recursion_depth().unwrap();
2384 assert_eq!(context.recursion_depth(), 0);
2385
2386 assert_eq!(context.current_offset(), 50);
2388 assert_eq!(context.recursion_depth(), 0);
2389}
2390#[test]
2391fn test_error_recovery_skip_problematic_rules() {
2392 let rules = vec![
2394 MagicRule {
2396 offset: OffsetSpec::Absolute(0),
2397 typ: TypeKind::Byte { signed: true },
2398 op: Operator::Equal,
2399 value: Value::Uint(0x7f),
2400 message: "Valid rule".to_string(),
2401 children: vec![],
2402 level: 0,
2403 strength_modifier: None,
2404 },
2405 MagicRule {
2407 offset: OffsetSpec::Absolute(100), typ: TypeKind::Byte { signed: true },
2409 op: Operator::Equal,
2410 value: Value::Uint(0x00),
2411 message: "Invalid rule".to_string(),
2412 children: vec![],
2413 level: 0,
2414 strength_modifier: None,
2415 },
2416 MagicRule {
2418 offset: OffsetSpec::Absolute(1),
2419 typ: TypeKind::Byte { signed: true },
2420 op: Operator::Equal,
2421 value: Value::Uint(0x45),
2422 message: "Another valid rule".to_string(),
2423 children: vec![],
2424 level: 0,
2425 strength_modifier: None,
2426 },
2427 ];
2428
2429 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let config = EvaluationConfig {
2431 max_recursion_depth: 20,
2432 max_string_length: 8192,
2433 stop_at_first_match: false, enable_mime_types: false,
2435 timeout_ms: None,
2436 };
2437 let mut context = EvaluationContext::new(config);
2438
2439 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2441
2442 assert_eq!(matches.len(), 2);
2444 assert_eq!(matches[0].message, "Valid rule");
2445 assert_eq!(matches[1].message, "Another valid rule");
2446}
2447
2448#[test]
2449fn test_error_recovery_child_rule_failures() {
2450 let rules = vec![MagicRule {
2452 offset: OffsetSpec::Absolute(0),
2453 typ: TypeKind::Byte { signed: true },
2454 op: Operator::Equal,
2455 value: Value::Uint(0x7f),
2456 message: "Parent rule".to_string(),
2457 children: vec![
2458 MagicRule {
2460 offset: OffsetSpec::Absolute(1),
2461 typ: TypeKind::Byte { signed: true },
2462 op: Operator::Equal,
2463 value: Value::Uint(0x45),
2464 message: "Valid child".to_string(),
2465 children: vec![],
2466 level: 1,
2467 strength_modifier: None,
2468 },
2469 MagicRule {
2471 offset: OffsetSpec::Absolute(100), typ: TypeKind::Byte { signed: true },
2473 op: Operator::Equal,
2474 value: Value::Uint(0x00),
2475 message: "Invalid child".to_string(),
2476 children: vec![],
2477 level: 1,
2478 strength_modifier: None,
2479 },
2480 ],
2481 level: 0,
2482 strength_modifier: None,
2483 }];
2484
2485 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; let config = EvaluationConfig::default();
2487 let mut context = EvaluationContext::new(config);
2488
2489 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2491
2492 assert_eq!(matches.len(), 2);
2494 assert_eq!(matches[0].message, "Parent rule");
2495 assert_eq!(matches[1].message, "Valid child");
2496}
2497
2498#[test]
2499fn test_error_recovery_mixed_rule_types() {
2500 let rules = vec![
2502 MagicRule {
2504 offset: OffsetSpec::Absolute(0),
2505 typ: TypeKind::Byte { signed: true },
2506 op: Operator::Equal,
2507 value: Value::Uint(0x7f),
2508 message: "Valid byte".to_string(),
2509 children: vec![],
2510 level: 0,
2511 strength_modifier: None,
2512 },
2513 MagicRule {
2515 offset: OffsetSpec::Absolute(3), typ: TypeKind::Short {
2517 endian: Endianness::Little,
2518 signed: false,
2519 },
2520 op: Operator::Equal,
2521 value: Value::Uint(0x1234),
2522 message: "Invalid short".to_string(),
2523 children: vec![],
2524 level: 0,
2525 strength_modifier: None,
2526 },
2527 MagicRule {
2529 offset: OffsetSpec::Absolute(1),
2530 typ: TypeKind::String {
2531 max_length: Some(3),
2532 },
2533 op: Operator::Equal,
2534 value: Value::String("ELF".to_string()),
2535 message: "Valid string".to_string(),
2536 children: vec![],
2537 level: 0,
2538 strength_modifier: None,
2539 },
2540 ];
2541
2542 let buffer = &[0x7f, b'E', b'L', b'F']; let config = EvaluationConfig {
2544 max_recursion_depth: 20,
2545 max_string_length: 8192,
2546 stop_at_first_match: false, enable_mime_types: false,
2548 timeout_ms: None,
2549 };
2550 let mut context = EvaluationContext::new(config);
2551
2552 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2554
2555 assert_eq!(matches.len(), 2);
2557 assert_eq!(matches[0].message, "Valid byte");
2558 assert_eq!(matches[1].message, "Valid string");
2559}
2560
2561#[test]
2562fn test_error_recovery_all_rules_fail() {
2563 let rules = vec![
2565 MagicRule {
2567 offset: OffsetSpec::Absolute(100),
2568 typ: TypeKind::Byte { signed: true },
2569 op: Operator::Equal,
2570 value: Value::Uint(0x00),
2571 message: "Out of bounds".to_string(),
2572 children: vec![],
2573 level: 0,
2574 strength_modifier: None,
2575 },
2576 MagicRule {
2578 offset: OffsetSpec::Absolute(2),
2579 typ: TypeKind::Long {
2580 endian: Endianness::Little,
2581 signed: false,
2582 },
2583 op: Operator::Equal,
2584 value: Value::Uint(0x1234_5678),
2585 message: "Insufficient bytes".to_string(),
2586 children: vec![],
2587 level: 0,
2588 strength_modifier: None,
2589 },
2590 ];
2591
2592 let buffer = &[0x7f, 0x45]; let config = EvaluationConfig::default();
2594 let mut context = EvaluationContext::new(config);
2595
2596 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2598 assert_eq!(matches.len(), 0);
2599}
2600
2601#[test]
2602fn test_error_recovery_timeout_propagation() {
2603 let rules = vec![MagicRule {
2605 offset: OffsetSpec::Absolute(0),
2606 typ: TypeKind::Byte { signed: true },
2607 op: Operator::Equal,
2608 value: Value::Uint(0x7f),
2609 message: "Test rule".to_string(),
2610 children: vec![],
2611 level: 0,
2612 strength_modifier: None,
2613 }];
2614
2615 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2616 let config = EvaluationConfig {
2617 max_recursion_depth: 10,
2618 max_string_length: 1024,
2619 stop_at_first_match: false,
2620 enable_mime_types: false,
2621 timeout_ms: Some(0), };
2623 let mut context = EvaluationContext::new(config);
2624
2625 let result = evaluate_rules(&rules, buffer, &mut context);
2628
2629 match result {
2631 Ok(_) | Err(LibmagicError::Timeout { .. }) => {
2632 }
2634 Err(e) => {
2635 panic!("Unexpected error type: {e:?}");
2636 }
2637 }
2638}
2639
2640#[test]
2641fn test_error_recovery_recursion_limit_propagation() {
2642 let rules = vec![MagicRule {
2644 offset: OffsetSpec::Absolute(0),
2645 typ: TypeKind::Byte { signed: true },
2646 op: Operator::Equal,
2647 value: Value::Uint(0x7f),
2648 message: "Parent".to_string(),
2649 children: vec![MagicRule {
2650 offset: OffsetSpec::Absolute(1),
2651 typ: TypeKind::Byte { signed: true },
2652 op: Operator::Equal,
2653 value: Value::Uint(0x45),
2654 message: "Child".to_string(),
2655 children: vec![],
2656 level: 1,
2657 strength_modifier: None,
2658 }],
2659 level: 0,
2660 strength_modifier: None,
2661 }];
2662
2663 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2664 let config = EvaluationConfig {
2665 max_recursion_depth: 0, max_string_length: 1024,
2667 stop_at_first_match: false,
2668 enable_mime_types: false,
2669 timeout_ms: None,
2670 };
2671 let mut context = EvaluationContext::new(config);
2672
2673 let result = evaluate_rules(&rules, buffer, &mut context);
2675 assert!(result.is_err());
2676
2677 match result.unwrap_err() {
2678 LibmagicError::EvaluationError(crate::error::EvaluationError::RecursionLimitExceeded {
2679 ..
2680 }) => {
2681 }
2683 _ => panic!("Expected recursion limit error"),
2684 }
2685}
2686
2687#[test]
2688fn test_error_recovery_preserves_context_state() {
2689 let rules = vec![
2691 MagicRule {
2693 offset: OffsetSpec::Absolute(0),
2694 typ: TypeKind::Byte { signed: true },
2695 op: Operator::Equal,
2696 value: Value::Uint(0x7f),
2697 message: "Valid rule".to_string(),
2698 children: vec![],
2699 level: 0,
2700 strength_modifier: None,
2701 },
2702 MagicRule {
2704 offset: OffsetSpec::Absolute(100),
2705 typ: TypeKind::Byte { signed: true },
2706 op: Operator::Equal,
2707 value: Value::Uint(0x00),
2708 message: "Invalid rule".to_string(),
2709 children: vec![],
2710 level: 0,
2711 strength_modifier: None,
2712 },
2713 ];
2714
2715 let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2716 let config = EvaluationConfig::default();
2717 let mut context = EvaluationContext::new(config);
2718
2719 context.set_current_offset(42);
2721 let initial_offset = context.current_offset();
2722 let initial_depth = context.recursion_depth();
2723
2724 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2726 assert_eq!(matches.len(), 1);
2727
2728 assert_eq!(context.current_offset(), initial_offset);
2730 assert_eq!(context.recursion_depth(), initial_depth);
2731}
2732#[test]
2733fn test_debug_error_recovery() {
2734 let rule = MagicRule {
2736 offset: OffsetSpec::Absolute(100), typ: TypeKind::Byte { signed: true },
2738 op: Operator::Equal,
2739 value: Value::Uint(0x00),
2740 message: "Out of bounds rule".to_string(),
2741 children: vec![],
2742 level: 0,
2743 strength_modifier: None,
2744 };
2745
2746 let buffer = &[0x7f, 0x45]; let single_result = evaluate_single_rule(&rule, buffer);
2750 println!("Single rule result: {single_result:?}");
2751 assert!(single_result.is_err());
2752
2753 let rules = vec![rule];
2755 let config = EvaluationConfig::default();
2756 let mut context = EvaluationContext::new(config);
2757
2758 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2759 println!("Rules evaluation matches: {}", matches.len());
2760 assert_eq!(matches.len(), 0);
2761}
2762#[test]
2763fn test_debug_mixed_rules() {
2764 let rules = vec![
2765 MagicRule {
2767 offset: OffsetSpec::Absolute(0),
2768 typ: TypeKind::Byte { signed: true },
2769 op: Operator::Equal,
2770 value: Value::Uint(0x7f),
2771 message: "Valid rule".to_string(),
2772 children: vec![],
2773 level: 0,
2774 strength_modifier: None,
2775 },
2776 MagicRule {
2778 offset: OffsetSpec::Absolute(100), typ: TypeKind::Byte { signed: true },
2780 op: Operator::Equal,
2781 value: Value::Uint(0x00),
2782 message: "Invalid rule".to_string(),
2783 children: vec![],
2784 level: 0,
2785 strength_modifier: None,
2786 },
2787 MagicRule {
2789 offset: OffsetSpec::Absolute(1),
2790 typ: TypeKind::Byte { signed: true },
2791 op: Operator::Equal,
2792 value: Value::Uint(0x45),
2793 message: "Another valid rule".to_string(),
2794 children: vec![],
2795 level: 0,
2796 strength_modifier: None,
2797 },
2798 ];
2799
2800 let buffer = &[0x7f, 0x45, 0x4c, 0x46]; for (i, rule) in rules.iter().enumerate() {
2804 let result = evaluate_single_rule(rule, buffer);
2805 println!("Rule {}: '{}' -> {:?}", i, rule.message, result);
2806 }
2807
2808 let config = EvaluationConfig::default();
2810 let mut context = EvaluationContext::new(config);
2811
2812 let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2813 println!("Total matches: {}", matches.len());
2814 for (i, m) in matches.iter().enumerate() {
2815 println!("Match {}: '{}'", i, m.message);
2816 }
2817}