lib

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

parser_robustness_corpus.rs (4973B)


      1 #![cfg(feature = "json")]
      2 
      3 use radroots_event::wire::{
      4     DEFAULT_EXTRA_MAX_FIELDS, DEFAULT_RAW_JSON_MAX_BYTES, DEFAULT_TAG_MAX_COUNT, EventWireError,
      5 };
      6 use radroots_event_codec::{DecodeError, decode, verify};
      7 use serde_json::{Map, Value, json};
      8 
      9 const VALID_ID: &str = "762bee187e9e645b81ec26ade05a69b5e8398caf527be8de0d9a45311ed0c7a0";
     10 const VALID_PUBKEY: &str = "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df";
     11 const VALID_SIGNATURE: &str = "4290da0bb6422986647bc8cd5f63bd52d49f41e7b665d3b47105b8109183e8d596f322c531d4061df53e1d2b70fda12d5d1c14f3720d7a56d9d0a03746af5109";
     12 
     13 fn valid_event() -> Value {
     14     json!({
     15         "id": VALID_ID,
     16         "pubkey": VALID_PUBKEY,
     17         "created_at": 1_800_000_100u64,
     18         "kind": 0u32,
     19         "tags": [],
     20         "content": "{\"display_name\":\"Moss Street Farm\",\"bot\":false,\"website\":\"https://mossstreet.example\",\"picture\":42}",
     21         "sig": VALID_SIGNATURE
     22     })
     23 }
     24 
     25 fn encode(value: &Value) -> String {
     26     serde_json::to_string(value).expect("corpus value must serialize")
     27 }
     28 
     29 fn object(value: &mut Value) -> &mut Map<String, Value> {
     30     value.as_object_mut().expect("event corpus object")
     31 }
     32 
     33 #[test]
     34 fn corpus_rejects_oversized_input_before_json_allocation() {
     35     let raw = " ".repeat(DEFAULT_RAW_JSON_MAX_BYTES + 1);
     36     let error = decode::event(raw.as_str()).expect_err("oversized event");
     37 
     38     assert_eq!(error.code(), "input_too_large");
     39     assert_eq!(
     40         error,
     41         DecodeError::InputTooLarge {
     42             max: DEFAULT_RAW_JSON_MAX_BYTES,
     43             actual: DEFAULT_RAW_JSON_MAX_BYTES + 1,
     44         }
     45     );
     46 }
     47 
     48 #[test]
     49 fn signed_event_decoder_accepts_valid_input_and_rejects_oversize_before_parsing() {
     50     let encoded = encode(&valid_event());
     51     decode::signed_event(encoded.as_str()).expect("ID-valid signed event");
     52 
     53     let raw = " ".repeat(DEFAULT_RAW_JSON_MAX_BYTES + 1);
     54     assert_eq!(
     55         decode::signed_event(raw.as_str()).expect_err("oversized signed event"),
     56         DecodeError::InputTooLarge {
     57             max: DEFAULT_RAW_JSON_MAX_BYTES,
     58             actual: DEFAULT_RAW_JSON_MAX_BYTES + 1,
     59         }
     60     );
     61 }
     62 
     63 #[test]
     64 fn corpus_rejects_tag_count_overflow_before_id_verification() {
     65     let mut value = valid_event();
     66     object(&mut value).insert(
     67         "tags".to_owned(),
     68         Value::Array(vec![json!(["t"]); DEFAULT_TAG_MAX_COUNT + 1]),
     69     );
     70     let error = decode::event(encode(&value).as_str()).expect_err("tag count overflow");
     71 
     72     assert!(matches!(
     73         error,
     74         DecodeError::InvalidWire(EventWireError::TooManyTags {
     75             max: DEFAULT_TAG_MAX_COUNT,
     76             actual,
     77         }) if actual == DEFAULT_TAG_MAX_COUNT + 1
     78     ));
     79 }
     80 
     81 #[test]
     82 fn corpus_bounds_unknown_fields_and_nested_json() {
     83     let mut accepted = valid_event();
     84     object(&mut accepted).insert("client".to_owned(), json!({"nested": [1, 2, 3]}));
     85     decode::event(encode(&accepted).as_str()).expect("bounded extension field");
     86 
     87     let mut too_many = valid_event();
     88     for index in 0..=DEFAULT_EXTRA_MAX_FIELDS {
     89         object(&mut too_many).insert(format!("extension_{index}"), Value::Null);
     90     }
     91     assert!(matches!(
     92         decode::event(encode(&too_many).as_str()),
     93         Err(DecodeError::InvalidWire(
     94             EventWireError::TooManyExtraFields {
     95                 max: DEFAULT_EXTRA_MAX_FIELDS,
     96                 actual,
     97             }
     98         )) if actual == DEFAULT_EXTRA_MAX_FIELDS + 1
     99     ));
    100 
    101     let nested = format!(
    102         "{{\"id\":\"{VALID_ID}\",\"pubkey\":\"{VALID_PUBKEY}\",\"created_at\":1800000100,\"kind\":0,\"tags\":[],\"content\":\"\",\"sig\":\"{VALID_SIGNATURE}\",\"extension\":{} }}",
    103         "[".repeat(160) + "0" + "]".repeat(160).as_str()
    104     );
    105     assert_eq!(
    106         decode::event(nested.as_str())
    107             .expect_err("excessive nesting")
    108             .code(),
    109         "invalid_json"
    110     );
    111 }
    112 
    113 #[test]
    114 fn corpus_rejects_malformed_signature_and_identifier_shapes() {
    115     let mut malformed_signature = valid_event();
    116     object(&mut malformed_signature).insert("sig".to_owned(), json!("00"));
    117     assert!(matches!(
    118         decode::event(encode(&malformed_signature).as_str()),
    119         Err(DecodeError::InvalidWire(
    120             EventWireError::InvalidIdentifier { field: "sig", .. }
    121         ))
    122     ));
    123 
    124     let mut malformed_id = valid_event();
    125     object(&mut malformed_id).insert("id".to_owned(), json!("00"));
    126     assert!(matches!(
    127         decode::event(encode(&malformed_id).as_str()),
    128         Err(DecodeError::InvalidWire(
    129             EventWireError::InvalidIdentifier { field: "id", .. }
    130         ))
    131     ));
    132 }
    133 
    134 #[test]
    135 fn corpus_keeps_identifier_mismatch_in_the_verifier_stage() {
    136     let mut mismatched = valid_event();
    137     object(&mut mismatched).insert("id".to_owned(), json!("a".repeat(64)));
    138 
    139     let raw = decode::event(encode(&mismatched).as_str()).expect("structurally valid event");
    140     let error = verify::id(raw).expect_err("identifier mismatch");
    141 
    142     assert_eq!(error.code(), "id_mismatch");
    143 }