lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

parser_robustness.rs (2332B)


      1 use std::panic::{AssertUnwindSafe, catch_unwind};
      2 
      3 use radroots_event::envelope::{EventTag, EventTags};
      4 use radroots_event::id::{DTag, EventId, EventSignature, Nip01Coordinate};
      5 use radroots_event::tag::relay_hint::NostrRelayHint;
      6 use radroots_event::wire::v1::Nip01EventWire;
      7 
      8 const VALID_WIRE_VECTOR: &str = include_str!("fixtures/nip01_wire.v1.json");
      9 
     10 #[test]
     11 fn wire_and_tag_parsers_are_total_over_deterministic_mutation_corpus() {
     12     for case in 0_u64..256 {
     13         let bytes = corpus_bytes(case, ((case * 37) % 2049) as usize);
     14         assert_parser_totality(bytes.as_slice(), case);
     15     }
     16 
     17     let valid_bytes = VALID_WIRE_VECTOR.as_bytes();
     18     for case in 0_u64..256 {
     19         let mut mutated = valid_bytes.to_vec();
     20         let index = ((case * 131) as usize) % mutated.len();
     21         mutated[index] ^= (case as u8).wrapping_mul(29).wrapping_add(1);
     22         assert_parser_totality(mutated.as_slice(), case + 256);
     23     }
     24 }
     25 
     26 fn assert_parser_totality(bytes: &[u8], case: u64) {
     27     let result = catch_unwind(AssertUnwindSafe(|| {
     28         if let Ok(text) = core::str::from_utf8(bytes) {
     29             let _ = Nip01EventWire::parse_json(text);
     30             let _ = DTag::parse(text);
     31             let _ = EventId::parse(text);
     32             let _ = EventSignature::parse(text);
     33             let _ = Nip01Coordinate::parse(text);
     34             let _ = NostrRelayHint::parse(text);
     35         }
     36 
     37         let tags = tags_from_bytes(bytes);
     38         for (index, tag) in tags.iter().cloned().enumerate() {
     39             let _ = EventTag::new(index, tag);
     40         }
     41         let _ = EventTags::new(tags);
     42     }));
     43 
     44     assert!(result.is_ok(), "parser mutation case {case} panicked");
     45 }
     46 
     47 fn corpus_bytes(seed: u64, length: usize) -> Vec<u8> {
     48     let mut state = seed ^ 0x9e37_79b9_7f4a_7c15;
     49     (0..length)
     50         .map(|_| {
     51             state = state
     52                 .wrapping_mul(6_364_136_223_846_793_005)
     53                 .wrapping_add(1_442_695_040_888_963_407);
     54             (state >> 32) as u8
     55         })
     56         .collect()
     57 }
     58 
     59 fn tags_from_bytes(bytes: &[u8]) -> Vec<Vec<String>> {
     60     bytes
     61         .chunks(64)
     62         .take(16)
     63         .map(|tag| {
     64             tag.chunks(8)
     65                 .take(8)
     66                 .map(|element| String::from_utf8_lossy(element).into_owned())
     67                 .collect()
     68         })
     69         .collect()
     70 }