use serde::{Deserialize, Serialize};
pub use vyre_foundation::soundness::{
validate_dynamic_pipeline, validate_dynamic_primitive, validate_pipeline, validate_primitive,
DynamicPrimitiveSoundness, DynamicSoundnessViolation, PrecisionContract, PrimitiveSoundness,
Soundness, SoundnessTagged, SoundnessViolation,
};
pub const SHARED_FACT_SCHEMA_VERSION: u16 = 1;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum SharedFactKind {
Source,
Sink,
Taint,
Sanitizer,
GraphEdge,
BorrowLoan,
BorrowOrigin,
BorrowSubset,
Dominance,
Range,
Witness,
}
impl SharedFactKind {
#[must_use]
pub const fn wire_tag(self) -> &'static str {
match self {
Self::Source => "source",
Self::Sink => "sink",
Self::Taint => "taint",
Self::Sanitizer => "sanitizer",
Self::GraphEdge => "graph_edge",
Self::BorrowLoan => "borrow_loan",
Self::BorrowOrigin => "borrow_origin",
Self::BorrowSubset => "borrow_subset",
Self::Dominance => "dominance",
Self::Range => "range",
Self::Witness => "witness",
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct SharedFactHeader {
pub schema_version: u16,
pub producer: String,
pub kind: SharedFactKind,
pub fact_id: u64,
pub subject: u64,
pub object: Option<u64>,
pub aux: Option<u64>,
pub file_id: u32,
pub start_byte: u32,
pub end_byte: u32,
pub soundness: Soundness,
}
impl SharedFactHeader {
#[must_use]
pub fn new(
producer: impl Into<String>,
kind: SharedFactKind,
fact_id: u64,
subject: u64,
soundness: Soundness,
) -> Self {
Self {
schema_version: SHARED_FACT_SCHEMA_VERSION,
producer: producer.into(),
kind,
fact_id,
subject,
object: None,
aux: None,
file_id: 0,
start_byte: 0,
end_byte: 0,
soundness,
}
}
#[must_use]
pub const fn with_object(mut self, object: u64) -> Self {
self.object = Some(object);
self
}
#[must_use]
pub const fn with_aux(mut self, aux: u64) -> Self {
self.aux = Some(aux);
self
}
#[must_use]
pub const fn with_span(mut self, file_id: u32, start_byte: u32, end_byte: u32) -> Self {
self.file_id = file_id;
self.start_byte = start_byte;
self.end_byte = end_byte;
self
}
#[must_use]
pub fn wire_header(&self) -> String {
let object_token = self
.object
.map_or_else(|| "-".to_string(), |v| v.to_string());
let aux_token = self
.aux
.map_or_else(|| "-".to_string(), |v| v.to_string());
format!(
"schema=v{};producer={};kind={};fact_id={};subject={};object={};aux={};file={};start={};end={};soundness={:?}",
self.schema_version,
self.producer,
self.kind.wire_tag(),
self.fact_id,
self.subject,
object_token,
aux_token,
self.file_id,
self.start_byte,
self.end_byte,
self.soundness
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn c_security_source_fact_header_is_exact() {
let header = SharedFactHeader::new("c-c11", SharedFactKind::Source, 1, 42, Soundness::Exact)
.with_span(7, 100, 120);
assert_eq!(
header.wire_header(),
"schema=v1;producer=c-c11;kind=source;fact_id=1;subject=42;object=-;aux=-;file=7;start=100;end=120;soundness=Exact"
);
}
#[test]
fn rust_borrow_subset_fact_header_is_exact() {
let header = SharedFactHeader::new(
"rustc-nll",
SharedFactKind::BorrowSubset,
9,
3,
Soundness::Exact,
)
.with_object(5)
.with_aux(11);
assert_eq!(
header.wire_header(),
"schema=v1;producer=rustc-nll;kind=borrow_subset;fact_id=9;subject=3;object=5;aux=11;file=0;start=0;end=0;soundness=Exact"
);
}
#[test]
fn wire_header_distinguishes_absent_object_from_zero_object() {
let no_object =
SharedFactHeader::new("rustc-nll", SharedFactKind::BorrowLoan, 1, 5, Soundness::Exact);
let object_zero = no_object.clone().with_object(0);
assert_ne!(
no_object.wire_header(),
object_zero.wire_header(),
"wire_header must distinguish object=None from object=Some(0)"
);
assert!(
no_object.wire_header().contains("object=-"),
"absent object must encode as 'object=-', got: {}",
no_object.wire_header()
);
assert!(
object_zero.wire_header().contains("object=0"),
"object=Some(0) must encode as 'object=0', got: {}",
object_zero.wire_header()
);
}
#[test]
fn wire_header_distinguishes_absent_aux_from_zero_aux() {
let no_aux =
SharedFactHeader::new("rustc-nll", SharedFactKind::BorrowLoan, 2, 7, Soundness::Exact);
let aux_zero = no_aux.clone().with_aux(0);
assert_ne!(
no_aux.wire_header(),
aux_zero.wire_header(),
"wire_header must distinguish aux=None from aux=Some(0)"
);
assert!(
no_aux.wire_header().contains("aux=-"),
"absent aux must encode as 'aux=-', got: {}",
no_aux.wire_header()
);
assert!(
aux_zero.wire_header().contains("aux=0"),
"aux=Some(0) must encode as 'aux=0', got: {}",
aux_zero.wire_header()
);
}
#[test]
fn weir_witness_fact_header_is_exact() {
let header = SharedFactHeader::new("weir", SharedFactKind::Witness, 13, 21, Soundness::Exact)
.with_object(34)
.with_aux(55);
assert_eq!(
header.wire_header(),
"schema=v1;producer=weir;kind=witness;fact_id=13;subject=21;object=34;aux=55;file=0;start=0;end=0;soundness=Exact"
);
}
}