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