matrix-fuzz

git clone git://archive.git.mtrnord.blog/MTRNord/matrix-fuzz.git
Log | Files | Refs | README | LICENSE

commit d888faed5a4a5004a1fcdb7efb071ca840254b3e
parent b7d278484ebbb8c923fb3b956dcf2a3dbaf4dfee
Author: MTRNord <mtrnord1@gmail.com>
Date:   Sun, 14 Aug 2022 02:24:33 +0200

Properly do the arbitrary json

Diffstat:
MCargo.toml | 7+++++++
Msrc/fuzzTargets/createRoom.rs | 11++++++-----
Msrc/lib.rs | 75+++++++++++++++++++++++++++++++--------------------------------------------
Msrc/types/create_room.rs | 143+++++++++++++-------------------------------------------------------------------
4 files changed, 66 insertions(+), 170 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -16,11 +16,18 @@ afl = "*" arbitrary = {version = "1", features = ["derive"]} cfg-if = "0.1" fuzzcheck = {git = "https://github.com/MTRNord/fuzzcheck-rs.git", branch = "patch-1"} +fuzzcheck_serde_json_generator = {version = "0.1.0", git = "https://github.com/teymour-aldridge/fuzzcheck_generators.git", branch = "main"} once_cell = "1.13.0" reqwest = {version = "0.11.11", features = ["blocking", "json", "gzip"]} serde = {version = "1.0", features = ["derive"]} serde_json = "1.0.83" +[patch.crates-io] +fuzzcheck = {git = "https://github.com/MTRNord/fuzzcheck-rs.git", branch = "patch-1"} + +[patch.'https://github.com/loiclec/fuzzcheck-rs'] +fuzzcheck = {git = "https://github.com/MTRNord/fuzzcheck-rs.git", branch = "patch-1"} + [features] default = ["fuzzing", "password_auth"] fuzzing = [] diff --git a/src/fuzzTargets/createRoom.rs b/src/fuzzTargets/createRoom.rs @@ -1,7 +1,8 @@ #![allow(non_snake_case)] -use matrix_fuzz::{client, secrets::ACCESS_TOKEN, types::create_room::CreateRoomMagicJSON}; +//use matrix_fuzz::{client, secrets::ACCESS_TOKEN, types::create_room::CreateRoomMagicJSON}; fn main() { + /* afl::fuzz_nohook!(|data: CreateRoomMagicJSON| { let mut data = data; // HACK due to https://github.com/matrix-org/synapse/issues/13510 @@ -51,9 +52,9 @@ fn main() { if content.contains("M_ROOM_IN_USE") || content.contains("Invalid characters in room alias") || content.contains("':' is not permitted in the room alias name. Please note this expects a local part — 'wombat', not '#wombat:example.com'.") - || content.contains("M_UNSUPPORTED_ROOM_VERSION") - || content.contains("Invalid user_id") - || content.contains("is not a valid preset") + || content.contains("M_UNSUPPORTED_ROOM_VERSION") + || content.contains("Invalid user_id") + || content.contains("is not a valid preset") || content.contains("You are not allowed to set others state") { return; @@ -63,5 +64,5 @@ fn main() { panic!("Content: {:?}", content); } } - }); + });*/ } diff --git a/src/lib.rs b/src/lib.rs @@ -61,7 +61,7 @@ fn login() -> String { mod tests { use reqwest::header::{HeaderValue, CONTENT_TYPE}; - use crate::types::create_room::{CreateRoomMagic, CreateRoomMagicJSON}; + use crate::types::create_room::CreateRoomMagicJSON; #[test] #[no_coverage] @@ -77,7 +77,7 @@ mod tests { #[test] #[no_coverage] fn null_in_room() { - let content = CreateRoomMagic { + let content = CreateRoomMagicJSON { name: Some("a".to_string()), room_alias_name: Some("\0".to_string()), visibility: Some("a".to_string()), @@ -112,26 +112,11 @@ mod tests { .send(); assert!(resp.is_err()) } - - #[test] - #[no_coverage] - fn converter() { - let content = std::fs::read_to_string( - "./fuzz/tests::fuzz_create_room/artifacts/36d21e863bdd02f6.json", - ) - .unwrap(); - let typed = serde_json::from_str::<CreateRoomMagic>(&content).unwrap(); - let typed_json: CreateRoomMagicJSON = (&typed).into(); - println!("{}", serde_json::to_string(&typed_json).unwrap()); - } } #[cfg(all(fuzzing, test))] mod tests { - use crate::types::{ - create_room::{CreateRoomMagic, CreateRoomMagicJSON}, - LoginPostReq, - }; + use crate::types::{create_room::CreateRoomMagicJSON, LoginPostReq}; fn login(data: &LoginPostReq) -> bool { let mut json_data = data.clone(); @@ -152,28 +137,28 @@ mod tests { } if let Some(user) = &json_data.user { - if user.contains("\0") { - json_data.user = Some(user.replace("\0", "")); + if user.contains('\0') { + json_data.user = Some(user.replace('\0', "")); } } if let Some(medium) = &json_data.medium { - if medium.contains("\0") { - json_data.medium = Some(medium.replace("\0", "")); + if medium.contains('\0') { + json_data.medium = Some(medium.replace('\0', "")); } } if let Some(address) = &json_data.address { - if address.contains("\0") { - json_data.address = Some(address.replace("\0", "")); + if address.contains('\0') { + json_data.address = Some(address.replace('\0', "")); } } if let Some(user) = &json_data.user { - if user.contains("\0") { - json_data.user = Some(user.replace("\0", "")); + if user.contains('\0') { + json_data.user = Some(user.replace('\0', "")); } } /*if let Some(password) = &json_data.password { - if password.contains("\0") { - json_data.password = Some(password.replace("\0", "")); + if password.contains('\0' { + json_data.password = Some(password.replace('\0', "")); } }*/ @@ -222,28 +207,29 @@ mod tests { assert!(!result.found_test_failure); } - fn create_room(data: &CreateRoomMagic) -> bool { - let mut json_data: CreateRoomMagicJSON = data.into(); + fn create_room(data: &CreateRoomMagicJSON) -> bool { + let mut json_data = data.clone(); + for mut state in &mut json_data.initial_state { + if state.content.is_array() + || state.content.is_boolean() + || state.content.is_null() + || state.content.is_string() + { + state.content = serde_json::Value::Object(serde_json::Map::new()); + } + } + // FIXME: We probably should set it to null and not do a false positive // HACK due to https://github.com/matrix-org/synapse/issues/13510 if let Some(room_alias_name) = &json_data.room_alias_name { if room_alias_name.contains('\0') { - json_data.room_alias_name = Some(room_alias_name.replace("\0", "")); + json_data.room_alias_name = Some(room_alias_name.replace('\0', "")); } } // HACK due to NUL in type or state_key - if let Some(initial_state) = &mut json_data.initial_state { - initial_state - .retain(|state| !(state._type.contains('\0') || state.state_key.contains('\0'))); - // for state in initial_state { - // if state._type.contains('\0') { - // return; - // } - // if state.state_key.contains('\0') { - // return; - // } - // } - } + json_data + .initial_state + .retain(|state| !(state._type.contains('\0') || state.state_key.contains('\0'))); /*// HACK due to https://github.com/matrix-org/synapse/issues/13511 if let Some(pids) = &data.invite_3pid { @@ -274,7 +260,8 @@ mod tests { || content.contains("M_UNSUPPORTED_ROOM_VERSION") || content.contains("Invalid user_id") || content.contains("is not a valid preset") - || content.contains("You are not allowed to set others state") + || content.contains("You are not allowed to set others state") + || content.contains("JSON integer out of range") { return true; } diff --git a/src/types/create_room.rs b/src/types/create_room.rs @@ -1,88 +1,28 @@ use arbitrary::Arbitrary; -use fuzzcheck::DefaultMutator; +use fuzzcheck::{DefaultMutator, Mutator}; +use fuzzcheck_serde_json_generator::json_value_mutator; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use serde_json::Value; -#[derive(Clone, Serialize, Deserialize, Debug, Default, Arbitrary)] -pub struct CreateRoomMagicJSON { - #[serde(skip_serializing_if = "Option::is_none")] - pub creation_content: Option<HashMap<String, String>>, - #[serde(skip_serializing_if = "Option::is_none")] - pub initial_state: Option<Vec<StateEventJSON>>, - #[serde(skip_serializing_if = "Option::is_none")] - pub invite: Option<Vec<String>>, - // Due to https://github.com/matrix-org/synapse/issues/13512 - //#[serde(skip_serializing_if = "Option::is_none")] - //pub invite_3pid: Option<Vec<Invite3pid>>, - #[serde(skip_serializing_if = "Option::is_none")] - pub is_direct: Option<bool>, - #[serde(skip_serializing_if = "Option::is_none")] - pub name: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub preset: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub room_alias_name: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub room_version: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub topic: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub visibility: Option<String>, -} - -// FIXME: This is stupid hacky. -impl From<&CreateRoomMagic> for CreateRoomMagicJSON { - #[no_coverage] - fn from(item: &CreateRoomMagic) -> Self { - let mut creation_content = HashMap::new(); - if let Some(mut creation_content_keys) = item.creation_content_keys.clone() { - if let Some(mut creation_content_values) = item.creation_content_values.clone() { - if creation_content_keys.len() > creation_content_values.len() { - creation_content_keys.truncate(creation_content_values.len()); - } else { - creation_content_values.truncate(creation_content_keys.len()); - } - for (key, value) in creation_content_keys - .iter() - .zip(creation_content_values.iter()) - { - creation_content.insert(key.to_string(), value.to_string()); - } - } - } +type ValueMutator = impl Mutator<Value>; - CreateRoomMagicJSON { - invite: item.invite.clone(), - initial_state: item - .initial_state - .clone() - .map(|initial_state| initial_state.into_iter().map(Into::into).collect()), - is_direct: item.is_direct, - name: item.name.clone(), - preset: item.preset.clone(), - room_alias_name: item.room_alias_name.clone(), - room_version: item.room_version.clone(), - topic: item.topic.clone(), - visibility: item.visibility.clone(), - creation_content: if item.creation_content_keys.is_none() - || item.creation_content_values.is_none() - { - None - } else { - Some(creation_content) - }, - } - } +fn creation_content_skip(value: &Value) -> bool { + value.is_string() + || value.is_i64() + || value.is_boolean() + || value.is_array() + || value.is_null() + || value.is_u64() } -#[derive(Clone, Serialize, Deserialize, Debug, Default, DefaultMutator, Arbitrary)] -pub struct CreateRoomMagic { - #[serde(skip_serializing_if = "Option::is_none")] - pub creation_content_keys: Option<Vec<String>>, - #[serde(skip_serializing_if = "Option::is_none")] - pub creation_content_values: Option<Vec<String>>, - #[serde(skip_serializing_if = "Option::is_none")] - pub initial_state: Option<Vec<StateEvent>>, +#[derive(Clone, Serialize, Deserialize, Debug, Default, DefaultMutator)] +pub struct CreateRoomMagicJSON { + #[serde(skip_serializing_if = "creation_content_skip")] + #[field_mutator(ValueMutator = { json_value_mutator() })] + pub creation_content: Value, + //#[serde(skip_serializing_if = "Option::is_none")] + // Required for more fuzzing results + pub initial_state: Vec<StateEventJSON>, #[serde(skip_serializing_if = "Option::is_none")] pub invite: Option<Vec<String>>, // Due to https://github.com/matrix-org/synapse/issues/13512 @@ -112,49 +52,10 @@ pub struct Invite3pid { pub medium: String, } -#[derive(Clone, Serialize, Deserialize, Debug, Default, Arbitrary)] +#[derive(Clone, Serialize, Deserialize, Debug, Default, DefaultMutator)] pub struct StateEventJSON { - pub content: HashMap<String, String>, - #[serde(rename = "type")] - pub _type: String, - pub state_key: String, -} - -// FIXME: This is stupid hacky. -impl From<StateEvent> for StateEventJSON { - #[no_coverage] - fn from(item: StateEvent) -> Self { - let mut content = HashMap::new(); - let mut item_clone = item.clone(); - if item_clone.content_keys.len() > item_clone.content_values.len() { - item_clone - .content_keys - .truncate(item_clone.content_values.len()); - } else { - item_clone - .content_values - .truncate(item_clone.content_keys.len()); - } - for (key, value) in item_clone - .content_keys - .iter() - .zip(item_clone.content_values.iter()) - { - content.insert(key.to_string(), value.to_string()); - } - - StateEventJSON { - content, - _type: item._type.clone(), - state_key: item.state_key, - } - } -} - -#[derive(Clone, Serialize, Deserialize, Debug, DefaultMutator, Default, Arbitrary)] -pub struct StateEvent { - pub content_keys: Vec<String>, - pub content_values: Vec<String>, + #[field_mutator(ValueMutator = {json_value_mutator()})] + pub content: Value, #[serde(rename = "type")] pub _type: String, pub state_key: String,