use vyre::ir::Program;
use vyre_primitives::graph::program_graph::ProgramGraphShape;
use vyre_primitives::predicate::edge_kind;
use crate::security::flow_composition::dataflow_hit_program;
#[cfg(test)]
use crate::security::flows_to::FLOWS_TO_MASK;
pub(crate) const OP_ID: &str = "vyre-libs::security::taint_pollution";
#[must_use]
pub fn taint_pollution(
shape: ProgramGraphShape,
source_buf: &str,
label_set: &str,
reach_buf: &str,
hits_buf: &str,
out_scalar: &str,
) -> Program {
dataflow_hit_program(
OP_ID, shape, source_buf, label_set, reach_buf, hits_buf, out_scalar,
)
}
#[must_use]
#[cfg(test)]
pub(crate) fn cpu_ref(
node_count: u32,
edge_offsets: &[u32],
edge_targets: &[u32],
edge_kind_mask: &[u32],
source: &[u32],
label_set: &[u32],
) -> u32 {
use vyre_primitives::bitset::and::cpu_ref as and_ref;
use vyre_primitives::graph::csr_forward_traverse::cpu_ref as fwd_ref;
let reach = fwd_ref(
node_count,
edge_offsets,
edge_targets,
edge_kind_mask,
source,
FLOWS_TO_MASK,
);
let hits = and_ref(&reach, label_set);
u32::from(hits.iter().any(|w| *w != 0))
}
pub struct TaintPollution;
impl vyre::soundness::SoundnessTagged for TaintPollution {
fn soundness(&self) -> vyre::soundness::Soundness {
vyre::soundness::Soundness::MayOver
}
}
inventory::submit! {
crate::harness::OpEntry {
id: OP_ID,
build: || taint_pollution(ProgramGraphShape::new(4, 3), "source", "label_set", "reach", "hits", "out_scalar"),
test_inputs: Some(|| {
let to_bytes = vyre_primitives::wire::pack_u32_slice;
vec![vec![
to_bytes(&[0, 0, 0, 0]), to_bytes(&[0, 1, 2, 3, 3]), to_bytes(&[1, 2, 3]), to_bytes(&[
edge_kind::ASSIGNMENT,
edge_kind::ASSIGNMENT,
edge_kind::ASSIGNMENT,
]), to_bytes(&[0, 0, 0, 0]), to_bytes(&[0b0001]), to_bytes(&[0b0001]), to_bytes(&[0b0010]), to_bytes(&[0b0000]), to_bytes(&[0b0000]), ]]
}),
expected_output: Some(|| {
let to_bytes = vyre_primitives::wire::pack_u32_slice;
vec![vec![
to_bytes(&[0b0011]), to_bytes(&[0b0010]), to_bytes(&[0b0001]), ]]
}),
category: Some("security"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use vyre_primitives::predicate::edge_kind;
#[test]
fn one_hop_to_labeled_returns_one() {
let off = vec![0u32, 1, 1];
let tgt = vec![1u32];
let msk = vec![edge_kind::ASSIGNMENT];
assert_eq!(cpu_ref(2, &off, &tgt, &msk, &[0b01], &[0b10]), 1);
}
#[test]
fn no_label_hit_returns_zero() {
let off = vec![0u32, 1, 1];
let tgt = vec![1u32];
let msk = vec![edge_kind::ASSIGNMENT];
assert_eq!(cpu_ref(2, &off, &tgt, &msk, &[0b01], &[0]), 0);
}
#[test]
fn empty_source_returns_zero() {
let off = vec![0u32, 1, 1];
let tgt = vec![1u32];
let msk = vec![edge_kind::ASSIGNMENT];
assert_eq!(cpu_ref(2, &off, &tgt, &msk, &[0], &[0xFFFF]), 0);
}
#[test]
fn unreachable_label_returns_zero() {
let off = vec![0u32, 1, 1];
let tgt = vec![1u32];
let msk = vec![edge_kind::ASSIGNMENT];
assert_eq!(cpu_ref(2, &off, &tgt, &msk, &[0b01], &[0b01]), 0);
}
}