1use super::{Mutator, MutatorParams};
6use crate::error::Result;
7use once_cell::sync::Lazy;
8use regex::Regex;
9use serde_json::{json, Value as JsonValue};
10use std::net::IpAddr;
11
12static RE_REFANG_HTTPS: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)h[xX]{1,2}ps://").unwrap());
18static RE_REFANG_HTTP: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)h[xX]{1,2}p://").unwrap());
19static RE_REFANG_FTP: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)f[xX]p://").unwrap());
20
21static RE_REFANG_BRACKET_DOT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\[\.\]\s*").unwrap());
23static RE_REFANG_BRACKET_COLON: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\[:\]\s*").unwrap());
24static RE_REFANG_BRACKET_AT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\[@\]\s*").unwrap());
25static RE_REFANG_BRACKET_SLASH: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\[/\]\s*").unwrap());
26
27static RE_REFANG_PAREN_DOT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\(\.\)\s*").unwrap());
29static RE_REFANG_PAREN_COLON: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\(:\)\s*").unwrap());
30static RE_REFANG_PAREN_AT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\(@\)\s*").unwrap());
31static RE_REFANG_PAREN_SLASH: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\(/\)\s*").unwrap());
32
33static RE_REFANG_BRACE_DOT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\{\.\}\s*").unwrap());
35static RE_REFANG_BRACE_COLON: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\{:\}\s*").unwrap());
36static RE_REFANG_BRACE_AT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\{@\}\s*").unwrap());
37static RE_REFANG_BRACE_SLASH: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\{/\}\s*").unwrap());
38
39static RE_REFANG_WORD_AT_BRACKET: Lazy<Regex> =
41 Lazy::new(|| Regex::new(r"(?i)\s*\[at\]\s*").unwrap());
42static RE_REFANG_WORD_AT_PAREN: Lazy<Regex> =
43 Lazy::new(|| Regex::new(r"(?i)\s*\(at\)\s*").unwrap());
44static RE_REFANG_WORD_AT_BRACE: Lazy<Regex> =
45 Lazy::new(|| Regex::new(r"(?i)\s*\{at\}\s*").unwrap());
46static RE_REFANG_WORD_DOT_BRACKET: Lazy<Regex> =
47 Lazy::new(|| Regex::new(r"(?i)\s*\[dot\]\s*").unwrap());
48static RE_REFANG_WORD_DOT_PAREN: Lazy<Regex> =
49 Lazy::new(|| Regex::new(r"(?i)\s*\(dot\)\s*").unwrap());
50static RE_REFANG_WORD_DOT_BRACE: Lazy<Regex> =
51 Lazy::new(|| Regex::new(r"(?i)\s*\{dot\}\s*").unwrap());
52
53static RE_DEFANG_HTTPS: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)https://").unwrap());
55static RE_DEFANG_HTTP: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)http://").unwrap());
56static RE_DEFANG_FTP: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)ftp://").unwrap());
57
58static RE_DEFANG_URL: Lazy<Regex> =
60 Lazy::new(|| Regex::new(r"((?:hxxps?|fxp|https?|ftp)://[^\s]+)").unwrap());
61static RE_DEFANG_PORT: Lazy<Regex> = Lazy::new(|| Regex::new(r":(\d+)").unwrap());
62
63pub struct RefangMutator {
73 _params: MutatorParams,
74}
75
76impl RefangMutator {
77 pub fn new(params: MutatorParams) -> Self {
78 Self { _params: params }
79 }
80
81 fn refang_string(&self, s: &str) -> String {
82 let mut result = s.to_string();
83
84 result = RE_REFANG_HTTPS.replace_all(&result, "https://").to_string();
87 result = RE_REFANG_HTTP.replace_all(&result, "http://").to_string();
88 result = RE_REFANG_FTP.replace_all(&result, "ftp://").to_string();
89
90 let patterns: &[(&Lazy<Regex>, &str)] = &[
92 (&RE_REFANG_BRACKET_DOT, "."),
93 (&RE_REFANG_BRACKET_COLON, ":"),
94 (&RE_REFANG_BRACKET_AT, "@"),
95 (&RE_REFANG_BRACKET_SLASH, "/"),
96 (&RE_REFANG_PAREN_DOT, "."),
97 (&RE_REFANG_PAREN_COLON, ":"),
98 (&RE_REFANG_PAREN_AT, "@"),
99 (&RE_REFANG_PAREN_SLASH, "/"),
100 (&RE_REFANG_BRACE_DOT, "."),
101 (&RE_REFANG_BRACE_COLON, ":"),
102 (&RE_REFANG_BRACE_AT, "@"),
103 (&RE_REFANG_BRACE_SLASH, "/"),
104 ];
105
106 for (re, replacement) in patterns {
107 result = re.replace_all(&result, *replacement).to_string();
108 }
109
110 result = RE_REFANG_WORD_AT_BRACKET
112 .replace_all(&result, "@")
113 .to_string();
114 result = RE_REFANG_WORD_AT_PAREN
115 .replace_all(&result, "@")
116 .to_string();
117 result = RE_REFANG_WORD_AT_BRACE
118 .replace_all(&result, "@")
119 .to_string();
120 result = RE_REFANG_WORD_DOT_BRACKET
121 .replace_all(&result, ".")
122 .to_string();
123 result = RE_REFANG_WORD_DOT_PAREN
124 .replace_all(&result, ".")
125 .to_string();
126 result = RE_REFANG_WORD_DOT_BRACE
127 .replace_all(&result, ".")
128 .to_string();
129
130 result
131 }
132}
133
134impl Mutator for RefangMutator {
135 fn apply(
136 &self,
137 _field_name: &str,
138 _record: &JsonValue,
139 value: &JsonValue,
140 ) -> Result<JsonValue> {
141 match value {
142 JsonValue::String(s) => Ok(JsonValue::String(self.refang_string(s))),
143 JsonValue::Array(arr) => {
144 let transformed: Vec<JsonValue> = arr
145 .iter()
146 .map(|item| {
147 if let JsonValue::String(s) = item {
148 JsonValue::String(self.refang_string(s))
149 } else {
150 item.clone()
151 }
152 })
153 .collect();
154 Ok(JsonValue::Array(transformed))
155 }
156 _ => Ok(value.clone()),
157 }
158 }
159
160 fn name(&self) -> &str {
161 "refang"
162 }
163}
164
165pub struct DefangMutator {
175 _params: MutatorParams,
176}
177
178impl DefangMutator {
179 pub fn new(params: MutatorParams) -> Self {
180 Self { _params: params }
181 }
182
183 fn defang_string(&self, s: &str) -> String {
184 let mut result = s.to_string();
185
186 let has_defanged_protocol =
188 result.to_lowercase().contains("hxxp") || result.to_lowercase().contains("fxp");
189 let has_defanged_dots = result.contains("[.]");
190 let has_defanged_at = result.contains("[at]");
191
192 if has_defanged_protocol && result.contains("://") {
194 if let Some(after_protocol) = result.split("://").nth(1) {
195 if !after_protocol.contains('.') || has_defanged_dots {
196 return result;
197 }
198 }
199 } else if has_defanged_dots && has_defanged_at {
200 return result;
201 }
202
203 result = RE_DEFANG_HTTPS.replace_all(&result, "hxxps://").to_string();
205 result = RE_DEFANG_HTTP.replace_all(&result, "hxxp://").to_string();
206 result = RE_DEFANG_FTP.replace_all(&result, "fxp://").to_string();
207
208 let mut defanged_result = String::new();
209 let mut last_end = 0;
210
211 for cap in RE_DEFANG_URL.captures_iter(&result) {
212 let url_match = cap.get(0).unwrap();
213
214 defanged_result.push_str(&result[last_end..url_match.start()]);
216
217 let url = url_match.as_str();
219 if let Some((protocol, rest)) = url.split_once("://") {
220 defanged_result.push_str(protocol);
221 defanged_result.push_str("://");
222
223 let mut rest_defanged = if !rest.contains("[.]") {
225 rest.replace('.', "[.]")
226 } else {
227 rest.to_string()
228 };
229
230 if !rest_defanged.contains("[at]") {
232 rest_defanged = rest_defanged.replace('@', "[at]");
233 }
234
235 rest_defanged = RE_DEFANG_PORT
237 .replace_all(&rest_defanged, "[:]$1")
238 .to_string();
239
240 defanged_result.push_str(&rest_defanged);
241 } else {
242 defanged_result.push_str(url);
243 }
244
245 last_end = url_match.end();
246 }
247
248 defanged_result.push_str(&result[last_end..]);
250
251 if last_end == 0 {
253 if !defanged_result.contains("[.]") {
255 defanged_result = defanged_result.replace('.', "[.]");
256 }
257 if !defanged_result.contains("[at]") {
258 defanged_result = defanged_result.replace('@', "[at]");
259 }
260 }
261
262 defanged_result
263 }
264}
265
266impl Mutator for DefangMutator {
267 fn apply(
268 &self,
269 _field_name: &str,
270 _record: &JsonValue,
271 value: &JsonValue,
272 ) -> Result<JsonValue> {
273 match value {
274 JsonValue::String(s) => Ok(JsonValue::String(self.defang_string(s))),
275 JsonValue::Array(arr) => {
276 let transformed: Vec<JsonValue> = arr
277 .iter()
278 .map(|item| {
279 if let JsonValue::String(s) = item {
280 JsonValue::String(self.defang_string(s))
281 } else {
282 item.clone()
283 }
284 })
285 .collect();
286 Ok(JsonValue::Array(transformed))
287 }
288 _ => Ok(value.clone()),
289 }
290 }
291
292 fn name(&self) -> &str {
293 "defang"
294 }
295}
296
297const V4_PRIVATE: &[(u32, u8)] = &[
320 (0x0000_0000, 8), (0x0A00_0000, 8), (0x7F00_0000, 8), (0xA9FE_0000, 16), (0xAC10_0000, 12), (0xC000_0000, 24), (0xC000_00AA, 31), (0xC000_0200, 24), (0xC0A8_0000, 16), (0xC612_0000, 15), (0xC633_6400, 24), (0xCB00_7100, 24), (0xF000_0000, 4), (0xFFFF_FFFF, 32), ];
335
336const V4_PRIVATE_EXCEPTIONS: &[(u32, u8)] = &[
338 (0xC000_0009, 32), (0xC000_000A, 32), ];
341
342const V6_PRIVATE: &[(u128, u8)] = &[
344 (0x0000_0000_0000_0000_0000_0000_0000_0001, 128), (0x0000_0000_0000_0000_0000_0000_0000_0000, 128), (0x0000_0000_0000_0000_0000_FFFF_0000_0000, 96), (0x0064_FF9B_0001_0000_0000_0000_0000_0000, 48), (0x0100_0000_0000_0000_0000_0000_0000_0000, 64), (0x2001_0000_0000_0000_0000_0000_0000_0000, 23), (0x2001_0DB8_0000_0000_0000_0000_0000_0000, 32), (0x2002_0000_0000_0000_0000_0000_0000_0000, 16), (0x3FFF_0000_0000_0000_0000_0000_0000_0000, 20), (0xFC00_0000_0000_0000_0000_0000_0000_0000, 7), (0xFE80_0000_0000_0000_0000_0000_0000_0000, 10), ];
356
357const V6_PRIVATE_EXCEPTIONS: &[(u128, u8)] = &[
359 (0x2001_0001_0000_0000_0000_0000_0000_0001, 128), (0x2001_0001_0000_0000_0000_0000_0000_0002, 128), (0x2001_0003_0000_0000_0000_0000_0000_0000, 32), (0x2001_0004_0112_0000_0000_0000_0000_0000, 48), (0x2001_0020_0000_0000_0000_0000_0000_0000, 28), (0x2001_0030_0000_0000_0000_0000_0000_0000, 28), ];
366
367const V6_RESERVED: &[(u128, u8)] = &[
371 (0x0000_0000_0000_0000_0000_0000_0000_0000, 8),
372 (0x0100_0000_0000_0000_0000_0000_0000_0000, 8),
373 (0x0200_0000_0000_0000_0000_0000_0000_0000, 7),
374 (0x0400_0000_0000_0000_0000_0000_0000_0000, 6),
375 (0x0800_0000_0000_0000_0000_0000_0000_0000, 5),
376 (0x1000_0000_0000_0000_0000_0000_0000_0000, 4),
377 (0x4000_0000_0000_0000_0000_0000_0000_0000, 3),
378 (0x6000_0000_0000_0000_0000_0000_0000_0000, 3),
379 (0x8000_0000_0000_0000_0000_0000_0000_0000, 3),
380 (0xA000_0000_0000_0000_0000_0000_0000_0000, 3),
381 (0xC000_0000_0000_0000_0000_0000_0000_0000, 3),
382 (0xE000_0000_0000_0000_0000_0000_0000_0000, 4),
383 (0xF000_0000_0000_0000_0000_0000_0000_0000, 5),
384 (0xF800_0000_0000_0000_0000_0000_0000_0000, 6),
385 (0xFE00_0000_0000_0000_0000_0000_0000_0000, 9),
386];
387
388fn v4_in(addr: u32, network: u32, prefix: u8) -> bool {
390 if prefix == 0 {
391 return true;
392 }
393 let mask = u32::MAX << (32 - prefix);
394 (addr & mask) == (network & mask)
395}
396
397fn v6_in(addr: u128, network: u128, prefix: u8) -> bool {
399 if prefix == 0 {
400 return true;
401 }
402 let mask = u128::MAX << (128 - prefix);
403 (addr & mask) == (network & mask)
404}
405
406fn v4_in_any(addr: u32, nets: &[(u32, u8)]) -> bool {
407 nets.iter().any(|&(n, p)| v4_in(addr, n, p))
408}
409
410fn v6_in_any(addr: u128, nets: &[(u128, u8)]) -> bool {
411 nets.iter().any(|&(n, p)| v6_in(addr, n, p))
412}
413
414fn v4_is_private(addr: u32) -> bool {
416 v4_in_any(addr, V4_PRIVATE) && !v4_in_any(addr, V4_PRIVATE_EXCEPTIONS)
417}
418
419fn v6_is_private(addr: u128) -> bool {
421 v6_in_any(addr, V6_PRIVATE) && !v6_in_any(addr, V6_PRIVATE_EXCEPTIONS)
422}
423
424fn v6_is_reserved(addr: u128) -> bool {
426 v6_in_any(addr, V6_RESERVED)
427}
428
429fn v4_is_reserved(addr: u32) -> bool {
431 v4_in(addr, 0xF000_0000, 4)
432}
433
434pub struct IsPrivateMutator {
453 _params: MutatorParams,
454}
455
456impl IsPrivateMutator {
457 pub fn new(params: MutatorParams) -> Self {
458 Self { _params: params }
459 }
460
461 fn is_private_ip(&self, ip_str: &str) -> bool {
468 match ip_str.parse::<IpAddr>() {
469 Ok(IpAddr::V4(v4)) => {
470 let a = u32::from(v4);
471 v4_is_private(a) || v4.is_loopback() || v4.is_link_local() || v4_is_reserved(a)
472 }
473 Ok(IpAddr::V6(v6)) => {
474 let a = u128::from(v6);
475 v6_is_private(a) || v6.is_loopback() || v6_is_reserved(a)
476 }
477 Err(_) => false,
478 }
479 }
480}
481
482impl Mutator for IsPrivateMutator {
483 fn apply(
484 &self,
485 _field_name: &str,
486 _record: &JsonValue,
487 value: &JsonValue,
488 ) -> Result<JsonValue> {
489 match value {
490 JsonValue::Null => Ok(json!(false)),
491 JsonValue::String(s) => Ok(json!(self.is_private_ip(s))),
492 JsonValue::Array(arr) => {
493 let result = arr.iter().any(|item| {
495 if let JsonValue::String(s) = item {
496 self.is_private_ip(s)
497 } else {
498 false
499 }
500 });
501 Ok(json!(result))
502 }
503 _ => Ok(json!(false)),
504 }
505 }
506
507 fn name(&self) -> &str {
508 "is_private"
509 }
510
511 fn returns_boolean(&self) -> bool {
515 true
516 }
517}
518
519pub struct IsGlobalMutator {
529 _params: MutatorParams,
530}
531
532impl IsGlobalMutator {
533 pub fn new(params: MutatorParams) -> Self {
534 Self { _params: params }
535 }
536
537 fn is_global_ip(&self, ip_str: &str) -> bool {
538 match ip_str.parse::<IpAddr>() {
542 Ok(IpAddr::V4(v4)) => {
543 let a = u32::from(v4);
544 if v4_is_private(a)
545 || v4.is_loopback()
546 || v4.is_link_local()
547 || v4.is_multicast()
548 || v4.is_unspecified()
549 || v4.is_broadcast()
550 || v4_is_reserved(a)
551 {
552 return false;
553 }
554 if v4_in(a, 0x0000_0000, 8) || v4_in(a, 0x6440_0000, 10) || v4_in(a, 0xC612_0000, 15) || v4_in(a, 0xF000_0000, 4)
559 {
561 return false;
562 }
563 true
564 }
565 Ok(IpAddr::V6(v6)) => {
566 let a = u128::from(v6);
567 !(v6_is_private(a)
568 || v6.is_loopback()
569 || v6.is_multicast()
570 || v6.is_unspecified()
571 || v6_is_reserved(a))
572 }
573 Err(_) => false,
574 }
575 }
576}
577
578impl Mutator for IsGlobalMutator {
579 fn apply(
580 &self,
581 _field_name: &str,
582 _record: &JsonValue,
583 value: &JsonValue,
584 ) -> Result<JsonValue> {
585 match value {
586 JsonValue::Null => Ok(json!(false)),
587 JsonValue::String(s) => Ok(json!(self.is_global_ip(s))),
588 JsonValue::Array(arr) => {
589 let result = arr.iter().any(|item| {
591 if let JsonValue::String(s) = item {
592 self.is_global_ip(s)
593 } else {
594 false
595 }
596 });
597 Ok(json!(result))
598 }
599 _ => Ok(json!(false)),
600 }
601 }
602
603 fn name(&self) -> &str {
604 "is_global"
605 }
606
607 fn returns_boolean(&self) -> bool {
611 true
612 }
613}
614
615pub struct IsMulticastMutator {
621 _params: MutatorParams,
622}
623
624impl IsMulticastMutator {
625 pub fn new(params: MutatorParams) -> Self {
626 Self { _params: params }
627 }
628
629 fn is_multicast_ip(&self, ip_str: &str) -> bool {
630 if let Ok(ip) = ip_str.parse::<IpAddr>() {
631 match ip {
632 IpAddr::V4(ipv4) => ipv4.is_multicast(),
633 IpAddr::V6(ipv6) => ipv6.is_multicast(),
634 }
635 } else {
636 false
637 }
638 }
639}
640
641impl Mutator for IsMulticastMutator {
642 fn apply(
643 &self,
644 _field_name: &str,
645 _record: &JsonValue,
646 value: &JsonValue,
647 ) -> Result<JsonValue> {
648 match value {
649 JsonValue::Null => Ok(json!(false)),
650 JsonValue::String(s) => Ok(json!(self.is_multicast_ip(s))),
651 JsonValue::Array(arr) => {
652 let result = arr.iter().any(|item| {
653 if let JsonValue::String(s) = item {
654 self.is_multicast_ip(s)
655 } else {
656 false
657 }
658 });
659 Ok(json!(result))
660 }
661 _ => Ok(json!(false)),
662 }
663 }
664
665 fn name(&self) -> &str {
666 "is_multicast"
667 }
668
669 fn returns_boolean(&self) -> bool {
673 true
674 }
675}
676
677pub struct IsLoopbackMutator {
683 _params: MutatorParams,
684}
685
686impl IsLoopbackMutator {
687 pub fn new(params: MutatorParams) -> Self {
688 Self { _params: params }
689 }
690
691 fn is_loopback_ip(&self, ip_str: &str) -> bool {
692 if let Ok(ip) = ip_str.parse::<IpAddr>() {
693 ip.is_loopback()
694 } else {
695 false
696 }
697 }
698}
699
700impl Mutator for IsLoopbackMutator {
701 fn apply(
702 &self,
703 _field_name: &str,
704 _record: &JsonValue,
705 value: &JsonValue,
706 ) -> Result<JsonValue> {
707 match value {
708 JsonValue::Null => Ok(json!(false)),
709 JsonValue::String(s) => Ok(json!(self.is_loopback_ip(s))),
710 JsonValue::Array(arr) => {
711 let result = arr.iter().any(|item| {
712 if let JsonValue::String(s) = item {
713 self.is_loopback_ip(s)
714 } else {
715 false
716 }
717 });
718 Ok(json!(result))
719 }
720 _ => Ok(json!(false)),
721 }
722 }
723
724 fn name(&self) -> &str {
725 "is_loopback"
726 }
727
728 fn returns_boolean(&self) -> bool {
732 true
733 }
734}
735
736pub struct IsLinkLocalMutator {
742 _params: MutatorParams,
743}
744
745impl IsLinkLocalMutator {
746 pub fn new(params: MutatorParams) -> Self {
747 Self { _params: params }
748 }
749
750 fn is_link_local_ip(&self, ip_str: &str) -> bool {
751 if let Ok(ip) = ip_str.parse::<IpAddr>() {
752 match ip {
753 IpAddr::V4(ipv4) => ipv4.is_link_local(),
754 IpAddr::V6(ipv6) => {
755 let segments = ipv6.segments();
757 (segments[0] & 0xffc0) == 0xfe80
758 }
759 }
760 } else {
761 false
762 }
763 }
764}
765
766impl Mutator for IsLinkLocalMutator {
767 fn apply(
768 &self,
769 _field_name: &str,
770 _record: &JsonValue,
771 value: &JsonValue,
772 ) -> Result<JsonValue> {
773 match value {
774 JsonValue::Null => Ok(json!(false)),
775 JsonValue::String(s) => Ok(json!(self.is_link_local_ip(s))),
776 JsonValue::Array(arr) => {
777 let result = arr.iter().any(|item| {
778 if let JsonValue::String(s) = item {
779 self.is_link_local_ip(s)
780 } else {
781 false
782 }
783 });
784 Ok(json!(result))
785 }
786 _ => Ok(json!(false)),
787 }
788 }
789
790 fn name(&self) -> &str {
791 "is_link_local"
792 }
793
794 fn returns_boolean(&self) -> bool {
798 true
799 }
800}
801
802#[cfg(test)]
803mod tests {
804 use super::*;
805 use serde_json::json;
806 use std::collections::HashMap;
807
808 #[test]
809 fn test_refang_http() {
810 let mutator = RefangMutator::new(HashMap::new());
811 let record = json!({});
812
813 let value = json!("hXXp://example[.]com");
814 let result = mutator.apply("field", &record, &value).unwrap();
815 assert_eq!(result, json!("http://example.com"));
816
817 let value = json!("hxxp://test[.]org");
818 let result = mutator.apply("field", &record, &value).unwrap();
819 assert_eq!(result, json!("http://test.org"));
820 }
821
822 #[test]
823 fn test_refang_https() {
824 let mutator = RefangMutator::new(HashMap::new());
825 let record = json!({});
826
827 let value = json!("hXXps://secure[.]example[.]com");
828 let result = mutator.apply("field", &record, &value).unwrap();
829 assert_eq!(result, json!("https://secure.example.com"));
830 }
831
832 #[test]
833 fn test_refang_email() {
834 let mutator = RefangMutator::new(HashMap::new());
835 let record = json!({});
836
837 let value = json!("user[at]example[.]com");
838 let result = mutator.apply("field", &record, &value).unwrap();
839 assert_eq!(result, json!("user@example.com"));
840 }
841
842 #[test]
843 fn test_refang_brackets_and_parentheses() {
844 let mutator = RefangMutator::new(HashMap::new());
845 let record = json!({});
846
847 let value = json!("example(.)com");
848 let result = mutator.apply("field", &record, &value).unwrap();
849 assert_eq!(result, json!("example.com"));
850
851 let value = json!("test{.}org");
852 let result = mutator.apply("field", &record, &value).unwrap();
853 assert_eq!(result, json!("test.org"));
854 }
855
856 #[test]
857 fn test_refang_array() {
858 let mutator = RefangMutator::new(HashMap::new());
859 let record = json!({});
860
861 let value = json!(["hXXp://example[.]com", "user[at]test[.]org"]);
862 let result = mutator.apply("field", &record, &value).unwrap();
863 assert_eq!(result, json!(["http://example.com", "user@test.org"]));
864 }
865
866 #[test]
867 fn test_defang_http() {
868 let mutator = DefangMutator::new(HashMap::new());
869 let record = json!({});
870
871 let value = json!("http://example.com");
872 let result = mutator.apply("field", &record, &value).unwrap();
873 assert_eq!(result, json!("hxxp://example[.]com"));
874 }
875
876 #[test]
877 fn test_defang_https() {
878 let mutator = DefangMutator::new(HashMap::new());
879 let record = json!({});
880
881 let value = json!("https://secure.example.com");
882 let result = mutator.apply("field", &record, &value).unwrap();
883 assert_eq!(result, json!("hxxps://secure[.]example[.]com"));
884 }
885
886 #[test]
887 fn test_defang_email() {
888 let mutator = DefangMutator::new(HashMap::new());
889 let record = json!({});
890
891 let value = json!("user@example.com");
892 let result = mutator.apply("field", &record, &value).unwrap();
893 assert_eq!(result, json!("user[at]example[.]com"));
894 }
895
896 #[test]
897 fn test_defang_already_defanged() {
898 let mutator = DefangMutator::new(HashMap::new());
899 let record = json!({});
900
901 let value = json!("hxxp://example[.]com");
902 let result = mutator.apply("field", &record, &value).unwrap();
903 assert_eq!(result, json!("hxxp://example[.]com"));
904 }
905
906 #[test]
907 fn test_defang_refang_round_trip() {
908 let defang_mutator = DefangMutator::new(HashMap::new());
909 let refang_mutator = RefangMutator::new(HashMap::new());
910 let record = json!({});
911
912 let original = json!("https://example.com/path");
913 let defanged = defang_mutator.apply("field", &record, &original).unwrap();
914 let refanged = refang_mutator.apply("field", &record, &defanged).unwrap();
915
916 assert_eq!(refanged, original);
918 }
919
920 #[test]
921 fn test_defang_array() {
922 let mutator = DefangMutator::new(HashMap::new());
923 let record = json!({});
924
925 let value = json!(["http://example.com", "user@test.org"]);
926 let result = mutator.apply("field", &record, &value).unwrap();
927 assert_eq!(
928 result,
929 json!(["hxxp://example[.]com", "user[at]test[.]org"])
930 );
931 }
932
933 #[test]
935 fn test_is_private_rfc1918() {
936 let mutator = IsPrivateMutator::new(HashMap::new());
937 let record = json!({});
938
939 assert_eq!(
941 mutator.apply("ip", &record, &json!("10.0.0.1")).unwrap(),
942 json!(true)
943 );
944 assert_eq!(
945 mutator
946 .apply("ip", &record, &json!("10.255.255.254"))
947 .unwrap(),
948 json!(true)
949 );
950
951 assert_eq!(
953 mutator.apply("ip", &record, &json!("172.16.0.1")).unwrap(),
954 json!(true)
955 );
956 assert_eq!(
957 mutator
958 .apply("ip", &record, &json!("172.31.255.254"))
959 .unwrap(),
960 json!(true)
961 );
962
963 assert_eq!(
965 mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
966 json!(true)
967 );
968 assert_eq!(
969 mutator
970 .apply("ip", &record, &json!("192.168.255.254"))
971 .unwrap(),
972 json!(true)
973 );
974
975 assert_eq!(
977 mutator.apply("ip", &record, &json!("172.32.0.1")).unwrap(),
978 json!(false)
979 );
980 assert_eq!(
981 mutator
982 .apply("ip", &record, &json!("172.15.255.254"))
983 .unwrap(),
984 json!(false)
985 );
986 }
987
988 #[test]
989 fn test_is_private_special_ranges() {
990 let mutator = IsPrivateMutator::new(HashMap::new());
991 let record = json!({});
992
993 assert_eq!(
995 mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
996 json!(true)
997 );
998 assert_eq!(
999 mutator
1000 .apply("ip", &record, &json!("127.255.255.254"))
1001 .unwrap(),
1002 json!(true)
1003 );
1004
1005 assert_eq!(
1007 mutator.apply("ip", &record, &json!("169.254.0.1")).unwrap(),
1008 json!(true)
1009 );
1010 assert_eq!(
1011 mutator
1012 .apply("ip", &record, &json!("169.254.255.254"))
1013 .unwrap(),
1014 json!(true)
1015 );
1016 }
1017
1018 #[test]
1019 fn test_is_private_public_ips() {
1020 let mutator = IsPrivateMutator::new(HashMap::new());
1021 let record = json!({});
1022
1023 assert_eq!(
1025 mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
1026 json!(false)
1027 );
1028 assert_eq!(
1029 mutator.apply("ip", &record, &json!("1.1.1.1")).unwrap(),
1030 json!(false)
1031 );
1032 assert_eq!(
1033 mutator
1034 .apply("ip", &record, &json!("93.184.216.34"))
1035 .unwrap(),
1036 json!(false)
1037 );
1038 }
1039
1040 #[test]
1041 fn test_is_private_ipv6() {
1042 let mutator = IsPrivateMutator::new(HashMap::new());
1043 let record = json!({});
1044
1045 assert_eq!(
1047 mutator.apply("ip", &record, &json!("fc00::1")).unwrap(),
1048 json!(true)
1049 );
1050 assert_eq!(
1051 mutator.apply("ip", &record, &json!("fd00::1")).unwrap(),
1052 json!(true)
1053 );
1054
1055 assert_eq!(
1057 mutator.apply("ip", &record, &json!("fe80::1")).unwrap(),
1058 json!(true)
1059 );
1060
1061 assert_eq!(
1063 mutator.apply("ip", &record, &json!("::1")).unwrap(),
1064 json!(true)
1065 );
1066
1067 assert_eq!(
1069 mutator
1070 .apply("ip", &record, &json!("2001:4860:4860::8888"))
1071 .unwrap(),
1072 json!(false)
1073 );
1074 }
1075
1076 #[test]
1077 fn test_is_private_invalid_ips() {
1078 let mutator = IsPrivateMutator::new(HashMap::new());
1079 let record = json!({});
1080
1081 assert_eq!(
1083 mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
1084 json!(false)
1085 );
1086 assert_eq!(
1087 mutator
1088 .apply("ip", &record, &json!("256.256.256.256"))
1089 .unwrap(),
1090 json!(false)
1091 );
1092 assert_eq!(
1093 mutator.apply("ip", &record, &json!("example.com")).unwrap(),
1094 json!(false)
1095 );
1096 }
1097
1098 #[test]
1099 fn test_is_private_lists() {
1100 let mutator = IsPrivateMutator::new(HashMap::new());
1101 let record = json!({});
1102
1103 assert_eq!(
1105 mutator
1106 .apply(
1107 "ips",
1108 &record,
1109 &json!(["8.8.8.8", "192.168.1.1", "1.1.1.1"])
1110 )
1111 .unwrap(),
1112 json!(true)
1113 );
1114
1115 assert_eq!(
1117 mutator
1118 .apply("ips", &record, &json!(["8.8.8.8", "1.1.1.1"]))
1119 .unwrap(),
1120 json!(false)
1121 );
1122
1123 assert_eq!(
1125 mutator.apply("ips", &record, &json!([])).unwrap(),
1126 json!(false)
1127 );
1128 }
1129
1130 #[test]
1131 fn test_is_private_none_input() {
1132 let mutator = IsPrivateMutator::new(HashMap::new());
1133 let record = json!({});
1134 assert_eq!(
1135 mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
1136 json!(false)
1137 );
1138 }
1139
1140 #[test]
1142 fn test_is_global_public_ips() {
1143 let mutator = IsGlobalMutator::new(HashMap::new());
1144 let record = json!({});
1145
1146 assert_eq!(
1148 mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
1149 json!(true)
1150 );
1151 assert_eq!(
1152 mutator.apply("ip", &record, &json!("1.1.1.1")).unwrap(),
1153 json!(true)
1154 );
1155 assert_eq!(
1156 mutator
1157 .apply("ip", &record, &json!("93.184.216.34"))
1158 .unwrap(),
1159 json!(true)
1160 );
1161 assert_eq!(
1162 mutator
1163 .apply("ip", &record, &json!("151.101.1.140"))
1164 .unwrap(),
1165 json!(true)
1166 );
1167 }
1168
1169 #[test]
1170 fn test_is_global_private_ips() {
1171 let mutator = IsGlobalMutator::new(HashMap::new());
1172 let record = json!({});
1173
1174 assert_eq!(
1176 mutator.apply("ip", &record, &json!("10.0.0.1")).unwrap(),
1177 json!(false)
1178 );
1179 assert_eq!(
1180 mutator.apply("ip", &record, &json!("172.16.0.1")).unwrap(),
1181 json!(false)
1182 );
1183 assert_eq!(
1184 mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
1185 json!(false)
1186 );
1187 }
1188
1189 #[test]
1190 fn test_is_global_special_ranges() {
1191 let mutator = IsGlobalMutator::new(HashMap::new());
1192 let record = json!({});
1193
1194 assert_eq!(
1196 mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
1197 json!(false)
1198 );
1199
1200 assert_eq!(
1202 mutator.apply("ip", &record, &json!("169.254.0.1")).unwrap(),
1203 json!(false)
1204 );
1205
1206 assert_eq!(
1208 mutator.apply("ip", &record, &json!("224.0.0.1")).unwrap(),
1209 json!(false)
1210 );
1211 assert_eq!(
1212 mutator
1213 .apply("ip", &record, &json!("239.255.255.255"))
1214 .unwrap(),
1215 json!(false)
1216 );
1217
1218 assert_eq!(
1220 mutator.apply("ip", &record, &json!("0.0.0.0")).unwrap(),
1221 json!(false)
1222 );
1223
1224 assert_eq!(
1226 mutator.apply("ip", &record, &json!("0.1.2.3")).unwrap(),
1227 json!(false)
1228 );
1229
1230 assert_eq!(
1232 mutator.apply("ip", &record, &json!("100.64.0.1")).unwrap(),
1233 json!(false)
1234 );
1235 assert_eq!(
1236 mutator
1237 .apply("ip", &record, &json!("100.127.255.254"))
1238 .unwrap(),
1239 json!(false)
1240 );
1241
1242 assert_eq!(
1244 mutator.apply("ip", &record, &json!("198.18.0.1")).unwrap(),
1245 json!(false)
1246 );
1247 assert_eq!(
1248 mutator
1249 .apply("ip", &record, &json!("198.19.255.254"))
1250 .unwrap(),
1251 json!(false)
1252 );
1253
1254 assert_eq!(
1256 mutator.apply("ip", &record, &json!("240.0.0.1")).unwrap(),
1257 json!(false)
1258 );
1259 assert_eq!(
1260 mutator
1261 .apply("ip", &record, &json!("255.255.255.254"))
1262 .unwrap(),
1263 json!(false)
1264 );
1265
1266 assert_eq!(
1268 mutator.apply("ip", &record, &json!("192.0.2.1")).unwrap(),
1269 json!(false)
1270 );
1271 assert_eq!(
1272 mutator
1273 .apply("ip", &record, &json!("198.51.100.1"))
1274 .unwrap(),
1275 json!(false)
1276 );
1277 assert_eq!(
1278 mutator.apply("ip", &record, &json!("203.0.113.1")).unwrap(),
1279 json!(false)
1280 );
1281 }
1282
1283 #[test]
1284 fn test_is_global_ipv6() {
1285 let mutator = IsGlobalMutator::new(HashMap::new());
1286 let record = json!({});
1287
1288 assert_eq!(
1290 mutator
1291 .apply("ip", &record, &json!("2001:4860:4860::8888"))
1292 .unwrap(),
1293 json!(true)
1294 );
1295 assert_eq!(
1296 mutator
1297 .apply("ip", &record, &json!("2606:4700:4700::1111"))
1298 .unwrap(),
1299 json!(true)
1300 );
1301
1302 assert_eq!(
1304 mutator.apply("ip", &record, &json!("fc00::1")).unwrap(),
1305 json!(false)
1306 ); assert_eq!(
1308 mutator.apply("ip", &record, &json!("fe80::1")).unwrap(),
1309 json!(false)
1310 ); assert_eq!(
1312 mutator.apply("ip", &record, &json!("::1")).unwrap(),
1313 json!(false)
1314 ); assert_eq!(
1316 mutator.apply("ip", &record, &json!("::")).unwrap(),
1317 json!(false)
1318 ); assert_eq!(
1320 mutator.apply("ip", &record, &json!("ff02::1")).unwrap(),
1321 json!(false)
1322 ); }
1324
1325 #[test]
1326 fn test_is_global_invalid_ips() {
1327 let mutator = IsGlobalMutator::new(HashMap::new());
1328 let record = json!({});
1329
1330 assert_eq!(
1332 mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
1333 json!(false)
1334 );
1335 assert_eq!(
1336 mutator
1337 .apply("ip", &record, &json!("256.256.256.256"))
1338 .unwrap(),
1339 json!(false)
1340 );
1341 assert_eq!(
1342 mutator.apply("ip", &record, &json!("example.com")).unwrap(),
1343 json!(false)
1344 );
1345 }
1346
1347 #[test]
1348 fn test_is_global_lists() {
1349 let mutator = IsGlobalMutator::new(HashMap::new());
1350 let record = json!({});
1351
1352 assert_eq!(
1354 mutator
1355 .apply(
1356 "ips",
1357 &record,
1358 &json!(["192.168.1.1", "8.8.8.8", "10.0.0.1"])
1359 )
1360 .unwrap(),
1361 json!(true)
1362 );
1363
1364 assert_eq!(
1366 mutator
1367 .apply("ips", &record, &json!(["192.168.1.1", "10.0.0.1"]))
1368 .unwrap(),
1369 json!(false)
1370 );
1371
1372 assert_eq!(
1374 mutator.apply("ips", &record, &json!([])).unwrap(),
1375 json!(false)
1376 );
1377 }
1378
1379 #[test]
1380 fn test_is_global_none_input() {
1381 let mutator = IsGlobalMutator::new(HashMap::new());
1382 let record = json!({});
1383 assert_eq!(
1384 mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
1385 json!(false)
1386 );
1387 }
1388
1389 #[test]
1390 fn test_boundary_addresses() {
1391 let private_mutator = IsPrivateMutator::new(HashMap::new());
1392 let global_mutator = IsGlobalMutator::new(HashMap::new());
1393 let record = json!({});
1394
1395 assert_eq!(
1397 private_mutator
1398 .apply("ip", &record, &json!("10.0.0.0"))
1399 .unwrap(),
1400 json!(true)
1401 );
1402 assert_eq!(
1403 private_mutator
1404 .apply("ip", &record, &json!("10.255.255.255"))
1405 .unwrap(),
1406 json!(true)
1407 );
1408 assert_eq!(
1409 private_mutator
1410 .apply("ip", &record, &json!("9.255.255.255"))
1411 .unwrap(),
1412 json!(false)
1413 );
1414 assert_eq!(
1415 private_mutator
1416 .apply("ip", &record, &json!("11.0.0.0"))
1417 .unwrap(),
1418 json!(false)
1419 );
1420
1421 assert_eq!(
1423 global_mutator
1424 .apply("ip", &record, &json!("1.0.0.0"))
1425 .unwrap(),
1426 json!(true)
1427 );
1428 assert_eq!(
1429 global_mutator
1430 .apply("ip", &record, &json!("223.255.255.255"))
1431 .unwrap(),
1432 json!(true)
1433 );
1434 assert_eq!(
1435 global_mutator
1436 .apply("ip", &record, &json!("224.0.0.0"))
1437 .unwrap(),
1438 json!(false)
1439 ); }
1441
1442 #[test]
1444 fn test_is_multicast_ipv4() {
1445 let mutator = IsMulticastMutator::new(HashMap::new());
1446 let record = json!({});
1447
1448 assert_eq!(
1450 mutator.apply("ip", &record, &json!("224.0.0.1")).unwrap(),
1451 json!(true)
1452 );
1453 assert_eq!(
1454 mutator
1455 .apply("ip", &record, &json!("239.255.255.255"))
1456 .unwrap(),
1457 json!(true)
1458 );
1459 assert_eq!(
1460 mutator.apply("ip", &record, &json!("230.0.0.1")).unwrap(),
1461 json!(true)
1462 );
1463
1464 assert_eq!(
1466 mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
1467 json!(false)
1468 );
1469 assert_eq!(
1470 mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
1471 json!(false)
1472 );
1473 assert_eq!(
1474 mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
1475 json!(false)
1476 );
1477 }
1478
1479 #[test]
1480 fn test_is_multicast_ipv6() {
1481 let mutator = IsMulticastMutator::new(HashMap::new());
1482 let record = json!({});
1483
1484 assert_eq!(
1486 mutator.apply("ip", &record, &json!("ff02::1")).unwrap(),
1487 json!(true)
1488 );
1489 assert_eq!(
1490 mutator.apply("ip", &record, &json!("ff05::1")).unwrap(),
1491 json!(true)
1492 );
1493
1494 assert_eq!(
1496 mutator
1497 .apply("ip", &record, &json!("2001:4860:4860::8888"))
1498 .unwrap(),
1499 json!(false)
1500 );
1501 assert_eq!(
1502 mutator.apply("ip", &record, &json!("::1")).unwrap(),
1503 json!(false)
1504 );
1505 }
1506
1507 #[test]
1508 fn test_is_multicast_invalid_and_special() {
1509 let mutator = IsMulticastMutator::new(HashMap::new());
1510 let record = json!({});
1511
1512 assert_eq!(
1513 mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
1514 json!(false)
1515 );
1516 assert_eq!(
1517 mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
1518 json!(false)
1519 );
1520 }
1521
1522 #[test]
1524 fn test_is_loopback_ipv4() {
1525 let mutator = IsLoopbackMutator::new(HashMap::new());
1526 let record = json!({});
1527
1528 assert_eq!(
1530 mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
1531 json!(true)
1532 );
1533 assert_eq!(
1534 mutator
1535 .apply("ip", &record, &json!("127.255.255.254"))
1536 .unwrap(),
1537 json!(true)
1538 );
1539
1540 assert_eq!(
1542 mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
1543 json!(false)
1544 );
1545 assert_eq!(
1546 mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
1547 json!(false)
1548 );
1549 }
1550
1551 #[test]
1552 fn test_is_loopback_ipv6() {
1553 let mutator = IsLoopbackMutator::new(HashMap::new());
1554 let record = json!({});
1555
1556 assert_eq!(
1558 mutator.apply("ip", &record, &json!("::1")).unwrap(),
1559 json!(true)
1560 );
1561
1562 assert_eq!(
1564 mutator
1565 .apply("ip", &record, &json!("2001:4860:4860::8888"))
1566 .unwrap(),
1567 json!(false)
1568 );
1569 assert_eq!(
1570 mutator.apply("ip", &record, &json!("fe80::1")).unwrap(),
1571 json!(false)
1572 );
1573 }
1574
1575 #[test]
1576 fn test_is_loopback_invalid_and_special() {
1577 let mutator = IsLoopbackMutator::new(HashMap::new());
1578 let record = json!({});
1579
1580 assert_eq!(
1581 mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
1582 json!(false)
1583 );
1584 assert_eq!(
1585 mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
1586 json!(false)
1587 );
1588 }
1589
1590 #[test]
1592 fn test_is_link_local_ipv4() {
1593 let mutator = IsLinkLocalMutator::new(HashMap::new());
1594 let record = json!({});
1595
1596 assert_eq!(
1598 mutator.apply("ip", &record, &json!("169.254.0.1")).unwrap(),
1599 json!(true)
1600 );
1601 assert_eq!(
1602 mutator
1603 .apply("ip", &record, &json!("169.254.255.254"))
1604 .unwrap(),
1605 json!(true)
1606 );
1607
1608 assert_eq!(
1610 mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
1611 json!(false)
1612 );
1613 assert_eq!(
1614 mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
1615 json!(false)
1616 );
1617 assert_eq!(
1618 mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
1619 json!(false)
1620 );
1621 }
1622
1623 #[test]
1624 fn test_is_link_local_ipv6() {
1625 let mutator = IsLinkLocalMutator::new(HashMap::new());
1626 let record = json!({});
1627
1628 assert_eq!(
1630 mutator.apply("ip", &record, &json!("fe80::1")).unwrap(),
1631 json!(true)
1632 );
1633 assert_eq!(
1634 mutator
1635 .apply("ip", &record, &json!("fe80::abcd:1234"))
1636 .unwrap(),
1637 json!(true)
1638 );
1639
1640 assert_eq!(
1642 mutator
1643 .apply("ip", &record, &json!("2001:4860:4860::8888"))
1644 .unwrap(),
1645 json!(false)
1646 );
1647 assert_eq!(
1648 mutator.apply("ip", &record, &json!("::1")).unwrap(),
1649 json!(false)
1650 );
1651 assert_eq!(
1652 mutator.apply("ip", &record, &json!("fc00::1")).unwrap(),
1653 json!(false)
1654 );
1655 }
1656
1657 #[test]
1658 fn test_is_link_local_invalid_and_special() {
1659 let mutator = IsLinkLocalMutator::new(HashMap::new());
1660 let record = json!({});
1661
1662 assert_eq!(
1663 mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
1664 json!(false)
1665 );
1666 assert_eq!(
1667 mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
1668 json!(false)
1669 );
1670 }
1671
1672 #[test]
1673 fn test_is_link_local_array() {
1674 let mutator = IsLinkLocalMutator::new(HashMap::new());
1675 let record = json!({});
1676
1677 assert_eq!(
1679 mutator
1680 .apply(
1681 "ips",
1682 &record,
1683 &json!(["8.8.8.8", "169.254.1.1", "1.1.1.1"])
1684 )
1685 .unwrap(),
1686 json!(true)
1687 );
1688
1689 assert_eq!(
1691 mutator
1692 .apply("ips", &record, &json!(["8.8.8.8", "1.1.1.1"]))
1693 .unwrap(),
1694 json!(false)
1695 );
1696 }
1697}