use super::{Mutator, MutatorParams};
use crate::error::Result;
use once_cell::sync::Lazy;
use regex::Regex;
use serde_json::{json, Value as JsonValue};
use std::net::IpAddr;
static RE_REFANG_HTTPS: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)h[xX]{1,2}ps://").unwrap());
static RE_REFANG_HTTP: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)h[xX]{1,2}p://").unwrap());
static RE_REFANG_FTP: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)f[xX]p://").unwrap());
static RE_REFANG_BRACKET_DOT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\[\.\]\s*").unwrap());
static RE_REFANG_BRACKET_COLON: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\[:\]\s*").unwrap());
static RE_REFANG_BRACKET_AT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\[@\]\s*").unwrap());
static RE_REFANG_BRACKET_SLASH: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\[/\]\s*").unwrap());
static RE_REFANG_PAREN_DOT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\(\.\)\s*").unwrap());
static RE_REFANG_PAREN_COLON: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\(:\)\s*").unwrap());
static RE_REFANG_PAREN_AT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\(@\)\s*").unwrap());
static RE_REFANG_PAREN_SLASH: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\(/\)\s*").unwrap());
static RE_REFANG_BRACE_DOT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\{\.\}\s*").unwrap());
static RE_REFANG_BRACE_COLON: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\{:\}\s*").unwrap());
static RE_REFANG_BRACE_AT: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\{@\}\s*").unwrap());
static RE_REFANG_BRACE_SLASH: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s*\{/\}\s*").unwrap());
static RE_REFANG_WORD_AT_BRACKET: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?i)\s*\[at\]\s*").unwrap());
static RE_REFANG_WORD_AT_PAREN: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?i)\s*\(at\)\s*").unwrap());
static RE_REFANG_WORD_AT_BRACE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?i)\s*\{at\}\s*").unwrap());
static RE_REFANG_WORD_DOT_BRACKET: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?i)\s*\[dot\]\s*").unwrap());
static RE_REFANG_WORD_DOT_PAREN: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?i)\s*\(dot\)\s*").unwrap());
static RE_REFANG_WORD_DOT_BRACE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?i)\s*\{dot\}\s*").unwrap());
static RE_DEFANG_HTTPS: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)https://").unwrap());
static RE_DEFANG_HTTP: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)http://").unwrap());
static RE_DEFANG_FTP: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)ftp://").unwrap());
static RE_DEFANG_URL: Lazy<Regex> =
Lazy::new(|| Regex::new(r"((?:hxxps?|fxp|https?|ftp)://[^\s]+)").unwrap());
static RE_DEFANG_PORT: Lazy<Regex> = Lazy::new(|| Regex::new(r":(\d+)").unwrap());
pub struct RefangMutator {
_params: MutatorParams,
}
impl RefangMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
fn refang_string(&self, s: &str) -> String {
let mut result = s.to_string();
result = RE_REFANG_HTTPS.replace_all(&result, "https://").to_string();
result = RE_REFANG_HTTP.replace_all(&result, "http://").to_string();
result = RE_REFANG_FTP.replace_all(&result, "ftp://").to_string();
let patterns: &[(&Lazy<Regex>, &str)] = &[
(&RE_REFANG_BRACKET_DOT, "."),
(&RE_REFANG_BRACKET_COLON, ":"),
(&RE_REFANG_BRACKET_AT, "@"),
(&RE_REFANG_BRACKET_SLASH, "/"),
(&RE_REFANG_PAREN_DOT, "."),
(&RE_REFANG_PAREN_COLON, ":"),
(&RE_REFANG_PAREN_AT, "@"),
(&RE_REFANG_PAREN_SLASH, "/"),
(&RE_REFANG_BRACE_DOT, "."),
(&RE_REFANG_BRACE_COLON, ":"),
(&RE_REFANG_BRACE_AT, "@"),
(&RE_REFANG_BRACE_SLASH, "/"),
];
for (re, replacement) in patterns {
result = re.replace_all(&result, *replacement).to_string();
}
result = RE_REFANG_WORD_AT_BRACKET
.replace_all(&result, "@")
.to_string();
result = RE_REFANG_WORD_AT_PAREN
.replace_all(&result, "@")
.to_string();
result = RE_REFANG_WORD_AT_BRACE
.replace_all(&result, "@")
.to_string();
result = RE_REFANG_WORD_DOT_BRACKET
.replace_all(&result, ".")
.to_string();
result = RE_REFANG_WORD_DOT_PAREN
.replace_all(&result, ".")
.to_string();
result = RE_REFANG_WORD_DOT_BRACE
.replace_all(&result, ".")
.to_string();
result
}
}
impl Mutator for RefangMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::String(s) => Ok(JsonValue::String(self.refang_string(s))),
JsonValue::Array(arr) => {
let transformed: Vec<JsonValue> = arr
.iter()
.map(|item| {
if let JsonValue::String(s) = item {
JsonValue::String(self.refang_string(s))
} else {
item.clone()
}
})
.collect();
Ok(JsonValue::Array(transformed))
}
_ => Ok(value.clone()),
}
}
fn name(&self) -> &str {
"refang"
}
}
pub struct DefangMutator {
_params: MutatorParams,
}
impl DefangMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
fn defang_string(&self, s: &str) -> String {
let mut result = s.to_string();
let has_defanged_protocol =
result.to_lowercase().contains("hxxp") || result.to_lowercase().contains("fxp");
let has_defanged_dots = result.contains("[.]");
let has_defanged_at = result.contains("[at]");
if has_defanged_protocol && result.contains("://") {
if let Some(after_protocol) = result.split("://").nth(1) {
if !after_protocol.contains('.') || has_defanged_dots {
return result;
}
}
} else if has_defanged_dots && has_defanged_at {
return result;
}
result = RE_DEFANG_HTTPS.replace_all(&result, "hxxps://").to_string();
result = RE_DEFANG_HTTP.replace_all(&result, "hxxp://").to_string();
result = RE_DEFANG_FTP.replace_all(&result, "fxp://").to_string();
let mut defanged_result = String::new();
let mut last_end = 0;
for cap in RE_DEFANG_URL.captures_iter(&result) {
let url_match = cap.get(0).unwrap();
defanged_result.push_str(&result[last_end..url_match.start()]);
let url = url_match.as_str();
if let Some((protocol, rest)) = url.split_once("://") {
defanged_result.push_str(protocol);
defanged_result.push_str("://");
let mut rest_defanged = if !rest.contains("[.]") {
rest.replace('.', "[.]")
} else {
rest.to_string()
};
if !rest_defanged.contains("[at]") {
rest_defanged = rest_defanged.replace('@', "[at]");
}
rest_defanged = RE_DEFANG_PORT
.replace_all(&rest_defanged, "[:]$1")
.to_string();
defanged_result.push_str(&rest_defanged);
} else {
defanged_result.push_str(url);
}
last_end = url_match.end();
}
defanged_result.push_str(&result[last_end..]);
if last_end == 0 {
if !defanged_result.contains("[.]") {
defanged_result = defanged_result.replace('.', "[.]");
}
if !defanged_result.contains("[at]") {
defanged_result = defanged_result.replace('@', "[at]");
}
}
defanged_result
}
}
impl Mutator for DefangMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::String(s) => Ok(JsonValue::String(self.defang_string(s))),
JsonValue::Array(arr) => {
let transformed: Vec<JsonValue> = arr
.iter()
.map(|item| {
if let JsonValue::String(s) = item {
JsonValue::String(self.defang_string(s))
} else {
item.clone()
}
})
.collect();
Ok(JsonValue::Array(transformed))
}
_ => Ok(value.clone()),
}
}
fn name(&self) -> &str {
"defang"
}
}
const V4_PRIVATE: &[(u32, u8)] = &[
(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), ];
const V4_PRIVATE_EXCEPTIONS: &[(u32, u8)] = &[
(0xC000_0009, 32), (0xC000_000A, 32), ];
const V6_PRIVATE: &[(u128, u8)] = &[
(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), ];
const V6_PRIVATE_EXCEPTIONS: &[(u128, u8)] = &[
(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), ];
const V6_RESERVED: &[(u128, u8)] = &[
(0x0000_0000_0000_0000_0000_0000_0000_0000, 8),
(0x0100_0000_0000_0000_0000_0000_0000_0000, 8),
(0x0200_0000_0000_0000_0000_0000_0000_0000, 7),
(0x0400_0000_0000_0000_0000_0000_0000_0000, 6),
(0x0800_0000_0000_0000_0000_0000_0000_0000, 5),
(0x1000_0000_0000_0000_0000_0000_0000_0000, 4),
(0x4000_0000_0000_0000_0000_0000_0000_0000, 3),
(0x6000_0000_0000_0000_0000_0000_0000_0000, 3),
(0x8000_0000_0000_0000_0000_0000_0000_0000, 3),
(0xA000_0000_0000_0000_0000_0000_0000_0000, 3),
(0xC000_0000_0000_0000_0000_0000_0000_0000, 3),
(0xE000_0000_0000_0000_0000_0000_0000_0000, 4),
(0xF000_0000_0000_0000_0000_0000_0000_0000, 5),
(0xF800_0000_0000_0000_0000_0000_0000_0000, 6),
(0xFE00_0000_0000_0000_0000_0000_0000_0000, 9),
];
fn v4_in(addr: u32, network: u32, prefix: u8) -> bool {
if prefix == 0 {
return true;
}
let mask = u32::MAX << (32 - prefix);
(addr & mask) == (network & mask)
}
fn v6_in(addr: u128, network: u128, prefix: u8) -> bool {
if prefix == 0 {
return true;
}
let mask = u128::MAX << (128 - prefix);
(addr & mask) == (network & mask)
}
fn v4_in_any(addr: u32, nets: &[(u32, u8)]) -> bool {
nets.iter().any(|&(n, p)| v4_in(addr, n, p))
}
fn v6_in_any(addr: u128, nets: &[(u128, u8)]) -> bool {
nets.iter().any(|&(n, p)| v6_in(addr, n, p))
}
fn v4_is_private(addr: u32) -> bool {
v4_in_any(addr, V4_PRIVATE) && !v4_in_any(addr, V4_PRIVATE_EXCEPTIONS)
}
fn v6_is_private(addr: u128) -> bool {
v6_in_any(addr, V6_PRIVATE) && !v6_in_any(addr, V6_PRIVATE_EXCEPTIONS)
}
fn v6_is_reserved(addr: u128) -> bool {
v6_in_any(addr, V6_RESERVED)
}
fn v4_is_reserved(addr: u32) -> bool {
v4_in(addr, 0xF000_0000, 4)
}
pub struct IsPrivateMutator {
_params: MutatorParams,
}
impl IsPrivateMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
fn is_private_ip(&self, ip_str: &str) -> bool {
match ip_str.parse::<IpAddr>() {
Ok(IpAddr::V4(v4)) => {
let a = u32::from(v4);
v4_is_private(a) || v4.is_loopback() || v4.is_link_local() || v4_is_reserved(a)
}
Ok(IpAddr::V6(v6)) => {
let a = u128::from(v6);
v6_is_private(a) || v6.is_loopback() || v6_is_reserved(a)
}
Err(_) => false,
}
}
}
impl Mutator for IsPrivateMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(json!(false)),
JsonValue::String(s) => Ok(json!(self.is_private_ip(s))),
JsonValue::Array(arr) => {
let result = arr.iter().any(|item| {
if let JsonValue::String(s) = item {
self.is_private_ip(s)
} else {
false
}
});
Ok(json!(result))
}
_ => Ok(json!(false)),
}
}
fn name(&self) -> &str {
"is_private"
}
fn returns_boolean(&self) -> bool {
true
}
}
pub struct IsGlobalMutator {
_params: MutatorParams,
}
impl IsGlobalMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
fn is_global_ip(&self, ip_str: &str) -> bool {
match ip_str.parse::<IpAddr>() {
Ok(IpAddr::V4(v4)) => {
let a = u32::from(v4);
if v4_is_private(a)
|| v4.is_loopback()
|| v4.is_link_local()
|| v4.is_multicast()
|| v4.is_unspecified()
|| v4.is_broadcast()
|| v4_is_reserved(a)
{
return false;
}
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)
{
return false;
}
true
}
Ok(IpAddr::V6(v6)) => {
let a = u128::from(v6);
!(v6_is_private(a)
|| v6.is_loopback()
|| v6.is_multicast()
|| v6.is_unspecified()
|| v6_is_reserved(a))
}
Err(_) => false,
}
}
}
impl Mutator for IsGlobalMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(json!(false)),
JsonValue::String(s) => Ok(json!(self.is_global_ip(s))),
JsonValue::Array(arr) => {
let result = arr.iter().any(|item| {
if let JsonValue::String(s) = item {
self.is_global_ip(s)
} else {
false
}
});
Ok(json!(result))
}
_ => Ok(json!(false)),
}
}
fn name(&self) -> &str {
"is_global"
}
fn returns_boolean(&self) -> bool {
true
}
}
pub struct IsMulticastMutator {
_params: MutatorParams,
}
impl IsMulticastMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
fn is_multicast_ip(&self, ip_str: &str) -> bool {
if let Ok(ip) = ip_str.parse::<IpAddr>() {
match ip {
IpAddr::V4(ipv4) => ipv4.is_multicast(),
IpAddr::V6(ipv6) => ipv6.is_multicast(),
}
} else {
false
}
}
}
impl Mutator for IsMulticastMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(json!(false)),
JsonValue::String(s) => Ok(json!(self.is_multicast_ip(s))),
JsonValue::Array(arr) => {
let result = arr.iter().any(|item| {
if let JsonValue::String(s) = item {
self.is_multicast_ip(s)
} else {
false
}
});
Ok(json!(result))
}
_ => Ok(json!(false)),
}
}
fn name(&self) -> &str {
"is_multicast"
}
fn returns_boolean(&self) -> bool {
true
}
}
pub struct IsLoopbackMutator {
_params: MutatorParams,
}
impl IsLoopbackMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
fn is_loopback_ip(&self, ip_str: &str) -> bool {
if let Ok(ip) = ip_str.parse::<IpAddr>() {
ip.is_loopback()
} else {
false
}
}
}
impl Mutator for IsLoopbackMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(json!(false)),
JsonValue::String(s) => Ok(json!(self.is_loopback_ip(s))),
JsonValue::Array(arr) => {
let result = arr.iter().any(|item| {
if let JsonValue::String(s) = item {
self.is_loopback_ip(s)
} else {
false
}
});
Ok(json!(result))
}
_ => Ok(json!(false)),
}
}
fn name(&self) -> &str {
"is_loopback"
}
fn returns_boolean(&self) -> bool {
true
}
}
pub struct IsLinkLocalMutator {
_params: MutatorParams,
}
impl IsLinkLocalMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
fn is_link_local_ip(&self, ip_str: &str) -> bool {
if let Ok(ip) = ip_str.parse::<IpAddr>() {
match ip {
IpAddr::V4(ipv4) => ipv4.is_link_local(),
IpAddr::V6(ipv6) => {
let segments = ipv6.segments();
(segments[0] & 0xffc0) == 0xfe80
}
}
} else {
false
}
}
}
impl Mutator for IsLinkLocalMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(json!(false)),
JsonValue::String(s) => Ok(json!(self.is_link_local_ip(s))),
JsonValue::Array(arr) => {
let result = arr.iter().any(|item| {
if let JsonValue::String(s) = item {
self.is_link_local_ip(s)
} else {
false
}
});
Ok(json!(result))
}
_ => Ok(json!(false)),
}
}
fn name(&self) -> &str {
"is_link_local"
}
fn returns_boolean(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::collections::HashMap;
#[test]
fn test_refang_http() {
let mutator = RefangMutator::new(HashMap::new());
let record = json!({});
let value = json!("hXXp://example[.]com");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("http://example.com"));
let value = json!("hxxp://test[.]org");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("http://test.org"));
}
#[test]
fn test_refang_https() {
let mutator = RefangMutator::new(HashMap::new());
let record = json!({});
let value = json!("hXXps://secure[.]example[.]com");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("https://secure.example.com"));
}
#[test]
fn test_refang_email() {
let mutator = RefangMutator::new(HashMap::new());
let record = json!({});
let value = json!("user[at]example[.]com");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("user@example.com"));
}
#[test]
fn test_refang_brackets_and_parentheses() {
let mutator = RefangMutator::new(HashMap::new());
let record = json!({});
let value = json!("example(.)com");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("example.com"));
let value = json!("test{.}org");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("test.org"));
}
#[test]
fn test_refang_array() {
let mutator = RefangMutator::new(HashMap::new());
let record = json!({});
let value = json!(["hXXp://example[.]com", "user[at]test[.]org"]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(["http://example.com", "user@test.org"]));
}
#[test]
fn test_defang_http() {
let mutator = DefangMutator::new(HashMap::new());
let record = json!({});
let value = json!("http://example.com");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("hxxp://example[.]com"));
}
#[test]
fn test_defang_https() {
let mutator = DefangMutator::new(HashMap::new());
let record = json!({});
let value = json!("https://secure.example.com");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("hxxps://secure[.]example[.]com"));
}
#[test]
fn test_defang_email() {
let mutator = DefangMutator::new(HashMap::new());
let record = json!({});
let value = json!("user@example.com");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("user[at]example[.]com"));
}
#[test]
fn test_defang_already_defanged() {
let mutator = DefangMutator::new(HashMap::new());
let record = json!({});
let value = json!("hxxp://example[.]com");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("hxxp://example[.]com"));
}
#[test]
fn test_defang_refang_round_trip() {
let defang_mutator = DefangMutator::new(HashMap::new());
let refang_mutator = RefangMutator::new(HashMap::new());
let record = json!({});
let original = json!("https://example.com/path");
let defanged = defang_mutator.apply("field", &record, &original).unwrap();
let refanged = refang_mutator.apply("field", &record, &defanged).unwrap();
assert_eq!(refanged, original);
}
#[test]
fn test_defang_array() {
let mutator = DefangMutator::new(HashMap::new());
let record = json!({});
let value = json!(["http://example.com", "user@test.org"]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(
result,
json!(["hxxp://example[.]com", "user[at]test[.]org"])
);
}
#[test]
fn test_is_private_rfc1918() {
let mutator = IsPrivateMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("10.0.0.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("10.255.255.254"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("172.16.0.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("172.31.255.254"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("192.168.255.254"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("172.32.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("172.15.255.254"))
.unwrap(),
json!(false)
);
}
#[test]
fn test_is_private_special_ranges() {
let mutator = IsPrivateMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("127.255.255.254"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("169.254.0.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("169.254.255.254"))
.unwrap(),
json!(true)
);
}
#[test]
fn test_is_private_public_ips() {
let mutator = IsPrivateMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("1.1.1.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("93.184.216.34"))
.unwrap(),
json!(false)
);
}
#[test]
fn test_is_private_ipv6() {
let mutator = IsPrivateMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("fc00::1")).unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("fd00::1")).unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("fe80::1")).unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("::1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("2001:4860:4860::8888"))
.unwrap(),
json!(false)
);
}
#[test]
fn test_is_private_invalid_ips() {
let mutator = IsPrivateMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("256.256.256.256"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("example.com")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_private_lists() {
let mutator = IsPrivateMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator
.apply(
"ips",
&record,
&json!(["8.8.8.8", "192.168.1.1", "1.1.1.1"])
)
.unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ips", &record, &json!(["8.8.8.8", "1.1.1.1"]))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ips", &record, &json!([])).unwrap(),
json!(false)
);
}
#[test]
fn test_is_private_none_input() {
let mutator = IsPrivateMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
json!(false)
);
}
#[test]
fn test_is_global_public_ips() {
let mutator = IsGlobalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("1.1.1.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("93.184.216.34"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("151.101.1.140"))
.unwrap(),
json!(true)
);
}
#[test]
fn test_is_global_private_ips() {
let mutator = IsGlobalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("10.0.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("172.16.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_global_special_ranges() {
let mutator = IsGlobalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("169.254.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("224.0.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("239.255.255.255"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("0.0.0.0")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("0.1.2.3")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("100.64.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("100.127.255.254"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("198.18.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("198.19.255.254"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("240.0.0.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("255.255.255.254"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("192.0.2.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("198.51.100.1"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("203.0.113.1")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_global_ipv6() {
let mutator = IsGlobalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator
.apply("ip", &record, &json!("2001:4860:4860::8888"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("2606:4700:4700::1111"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("fc00::1")).unwrap(),
json!(false)
); assert_eq!(
mutator.apply("ip", &record, &json!("fe80::1")).unwrap(),
json!(false)
); assert_eq!(
mutator.apply("ip", &record, &json!("::1")).unwrap(),
json!(false)
); assert_eq!(
mutator.apply("ip", &record, &json!("::")).unwrap(),
json!(false)
); assert_eq!(
mutator.apply("ip", &record, &json!("ff02::1")).unwrap(),
json!(false)
); }
#[test]
fn test_is_global_invalid_ips() {
let mutator = IsGlobalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
json!(false)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("256.256.256.256"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("example.com")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_global_lists() {
let mutator = IsGlobalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator
.apply(
"ips",
&record,
&json!(["192.168.1.1", "8.8.8.8", "10.0.0.1"])
)
.unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ips", &record, &json!(["192.168.1.1", "10.0.0.1"]))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ips", &record, &json!([])).unwrap(),
json!(false)
);
}
#[test]
fn test_is_global_none_input() {
let mutator = IsGlobalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
json!(false)
);
}
#[test]
fn test_boundary_addresses() {
let private_mutator = IsPrivateMutator::new(HashMap::new());
let global_mutator = IsGlobalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
private_mutator
.apply("ip", &record, &json!("10.0.0.0"))
.unwrap(),
json!(true)
);
assert_eq!(
private_mutator
.apply("ip", &record, &json!("10.255.255.255"))
.unwrap(),
json!(true)
);
assert_eq!(
private_mutator
.apply("ip", &record, &json!("9.255.255.255"))
.unwrap(),
json!(false)
);
assert_eq!(
private_mutator
.apply("ip", &record, &json!("11.0.0.0"))
.unwrap(),
json!(false)
);
assert_eq!(
global_mutator
.apply("ip", &record, &json!("1.0.0.0"))
.unwrap(),
json!(true)
);
assert_eq!(
global_mutator
.apply("ip", &record, &json!("223.255.255.255"))
.unwrap(),
json!(true)
);
assert_eq!(
global_mutator
.apply("ip", &record, &json!("224.0.0.0"))
.unwrap(),
json!(false)
); }
#[test]
fn test_is_multicast_ipv4() {
let mutator = IsMulticastMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("224.0.0.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("239.255.255.255"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("230.0.0.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_multicast_ipv6() {
let mutator = IsMulticastMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("ff02::1")).unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("ff05::1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("2001:4860:4860::8888"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("::1")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_multicast_invalid_and_special() {
let mutator = IsMulticastMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
json!(false)
);
}
#[test]
fn test_is_loopback_ipv4() {
let mutator = IsLoopbackMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("127.255.255.254"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_loopback_ipv6() {
let mutator = IsLoopbackMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("::1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("2001:4860:4860::8888"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("fe80::1")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_loopback_invalid_and_special() {
let mutator = IsLoopbackMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
json!(false)
);
}
#[test]
fn test_is_link_local_ipv4() {
let mutator = IsLinkLocalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("169.254.0.1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("169.254.255.254"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator.apply("ip", &record, &json!("8.8.8.8")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("192.168.1.1")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("127.0.0.1")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_link_local_ipv6() {
let mutator = IsLinkLocalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("fe80::1")).unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("fe80::abcd:1234"))
.unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ip", &record, &json!("2001:4860:4860::8888"))
.unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("::1")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &json!("fc00::1")).unwrap(),
json!(false)
);
}
#[test]
fn test_is_link_local_invalid_and_special() {
let mutator = IsLinkLocalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator.apply("ip", &record, &json!("not-an-ip")).unwrap(),
json!(false)
);
assert_eq!(
mutator.apply("ip", &record, &JsonValue::Null).unwrap(),
json!(false)
);
}
#[test]
fn test_is_link_local_array() {
let mutator = IsLinkLocalMutator::new(HashMap::new());
let record = json!({});
assert_eq!(
mutator
.apply(
"ips",
&record,
&json!(["8.8.8.8", "169.254.1.1", "1.1.1.1"])
)
.unwrap(),
json!(true)
);
assert_eq!(
mutator
.apply("ips", &record, &json!(["8.8.8.8", "1.1.1.1"]))
.unwrap(),
json!(false)
);
}
}