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"
}
}
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 {
if let Ok(ip) = ip_str.parse::<IpAddr>() {
match ip {
IpAddr::V4(ipv4) => {
ipv4.is_private() ||
ipv4.is_loopback() ||
ipv4.is_link_local()
}
IpAddr::V6(ipv6) => {
let segments = ipv6.segments();
let is_unique_local = (segments[0] & 0xfe00) == 0xfc00;
is_unique_local ||
ipv6.is_loopback() ||
((segments[0] & 0xffc0) == 0xfe80)
}
}
} else {
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"
}
}
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 {
if let Ok(ip) = ip_str.parse::<IpAddr>() {
match ip {
IpAddr::V4(ipv4) => {
if ipv4.is_private() || ipv4.is_loopback() || ipv4.is_link_local() {
return false;
}
if ipv4.is_multicast() {
return false;
}
if ipv4.is_unspecified() {
return false;
}
if ipv4.is_broadcast() {
return false;
}
let octets = ipv4.octets();
if octets[0] == 0 {
return false;
}
if octets[0] == 100 && (octets[1] & 0xc0) == 0x40 {
return false;
}
if octets[0] == 198 && (octets[1] == 18 || octets[1] == 19) {
return false;
}
if octets[0] >= 240 {
return false;
}
if (octets[0] == 192 && octets[1] == 0 && octets[2] == 2)
|| (octets[0] == 198 && octets[1] == 51 && octets[2] == 100)
|| (octets[0] == 203 && octets[1] == 0 && octets[2] == 113)
{
return false;
}
true
}
IpAddr::V6(ipv6) => {
if ipv6.is_loopback() || ipv6.is_multicast() || ipv6.is_unspecified() {
return false;
}
let segments = ipv6.segments();
if (segments[0] & 0xfe00) == 0xfc00 {
return false;
}
if (segments[0] & 0xffc0) == 0xfe80 {
return false;
}
true
}
}
} else {
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"
}
}
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"
}
}
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"
}
}
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"
}
}
#[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)
);
}
}