use std::collections::HashMap;
use std::hash::Hash;
use secfinding::Finding;
use crate::correlation::utils::normalize_host;
pub(crate) fn host_scope(f: &Finding) -> String {
normalize_host(f.target())
}
pub(crate) fn parent_scope(f: &Finding) -> String {
gossan_core::domain::parent_domain(&normalize_host(f.target()))
}
pub(crate) fn group_by<'a, T, K>(
items: &'a [T],
key: impl Fn(&T) -> K,
) -> HashMap<K, Vec<&'a T>>
where
K: Eq + Hash,
{
let mut m: HashMap<K, Vec<&'a T>> = HashMap::new();
for it in items {
m.entry(key(it)).or_default().push(it);
}
m
}
pub(crate) fn distinct_pair<'a, T>(
items: &[&'a T],
pred_a: impl Fn(&T) -> bool,
pred_b: impl Fn(&T) -> bool,
) -> Option<(&'a T, &'a T)> {
let a = items.iter().copied().find(|x| pred_a(x))?;
let b = items.iter().copied().find(|x| pred_b(x))?;
if !std::ptr::eq(a, b) {
return Some((a, b));
}
if let Some(a2) = items
.iter()
.copied()
.find(|x| !std::ptr::eq(*x, b) && pred_a(x))
{
return Some((a2, b));
}
if let Some(b2) = items
.iter()
.copied()
.find(|x| !std::ptr::eq(*x, a) && pred_b(x))
{
return Some((a, b2));
}
None
}
pub(crate) fn has_distinct_pair<T>(
items: &[&T],
pred_a: impl Fn(&T) -> bool,
pred_b: impl Fn(&T) -> bool,
) -> bool {
distinct_pair(items, pred_a, pred_b).is_some()
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
fn refs<T>(v: &[T]) -> Vec<&T> {
v.iter().collect()
}
#[test]
fn single_item_satisfying_both_is_not_a_pair() {
let v = [7u32];
let g = refs(&v);
assert_eq!(distinct_pair(&g, |_| true, |_| true), None);
assert!(!has_distinct_pair(&g, |&x| x == 7, |&x| x == 7));
}
#[test]
fn two_distinct_items_one_each_is_a_pair() {
let v = [1u32, 2u32];
let g = refs(&v);
let (a, b) = distinct_pair(&g, |&x| x == 1, |&x| x == 2).expect("pair");
assert_eq!((*a, *b), (1, 2));
}
#[test]
fn dual_item_with_distinct_partner_pairs() {
let v = [9u32, 2u32];
let g = refs(&v);
let got = distinct_pair(&g, |&x| x == 9, |&x| x == 2 || x == 9);
let (a, b) = got.expect("distinct partner must pair");
assert_eq!(*a, 9);
assert_eq!(*b, 2);
}
#[test]
fn empty_group_is_none() {
let g: Vec<&u32> = Vec::new();
assert_eq!(distinct_pair(&g, |_| true, |_| true), None);
}
#[test]
fn value_equal_but_distinct_objects_pair() {
let v = [5u32, 5u32];
let g = refs(&v);
assert!(has_distinct_pair(&g, |&x| x == 5, |&x| x == 5));
}
fn brute_force_exists(a_mask: &[bool], b_mask: &[bool]) -> bool {
for i in 0..a_mask.len() {
for j in 0..b_mask.len() {
if i != j && a_mask[i] && b_mask[j] {
return true;
}
}
}
false
}
proptest! {
#[test]
fn matches_brute_force_oracle(
masks in proptest::collection::vec((any::<bool>(), any::<bool>()), 0..40)
) {
let items: Vec<usize> = (0..masks.len()).collect();
let g = refs(&items);
let a_mask: Vec<bool> = masks.iter().map(|m| m.0).collect();
let b_mask: Vec<bool> = masks.iter().map(|m| m.1).collect();
let got = has_distinct_pair(
&g,
|&i| a_mask[i],
|&i| b_mask[i],
);
prop_assert_eq!(got, brute_force_exists(&a_mask, &b_mask));
}
#[test]
fn existence_is_permutation_invariant(
mut masks in proptest::collection::vec((any::<bool>(), any::<bool>()), 1..30),
seed in any::<u64>(),
) {
let items: Vec<usize> = (0..masks.len()).collect();
let g = refs(&items);
let a: Vec<bool> = masks.iter().map(|m| m.0).collect();
let b: Vec<bool> = masks.iter().map(|m| m.1).collect();
let before = has_distinct_pair(&g, |&i| a[i], |&i| b[i]);
let n = masks.len();
let mut s = seed | 1;
for i in (1..n).rev() {
s = s.wrapping_mul(6364136223846793005).wrapping_add(1);
let j = (s >> 33) as usize % (i + 1);
masks.swap(i, j);
}
let items2: Vec<usize> = (0..masks.len()).collect();
let g2 = refs(&items2);
let a2: Vec<bool> = masks.iter().map(|m| m.0).collect();
let b2: Vec<bool> = masks.iter().map(|m| m.1).collect();
let after = has_distinct_pair(&g2, |&i| a2[i], |&i| b2[i]);
prop_assert_eq!(before, after);
}
#[test]
fn returned_pair_is_sound(
masks in proptest::collection::vec((any::<bool>(), any::<bool>()), 0..30)
) {
let items: Vec<usize> = (0..masks.len()).collect();
let g = refs(&items);
let a: Vec<bool> = masks.iter().map(|m| m.0).collect();
let b: Vec<bool> = masks.iter().map(|m| m.1).collect();
if let Some((x, y)) = distinct_pair(&g, |&i| a[i], |&i| b[i]) {
prop_assert!(a[*x], "returned A does not satisfy pred_a");
prop_assert!(b[*y], "returned B does not satisfy pred_b");
prop_assert!(!std::ptr::eq(x, y), "returned the same object twice");
}
}
}
}