From ad89edabd9e31c71a119c2b504d687d40d24d24d Mon Sep 17 00:00:00 2001 From: Ulysse Carion Date: Sat, 11 Apr 2020 16:15:18 -0700 Subject: [PATCH] Add fuzzing for Validator::validate --- fuzz/Cargo.toml | 5 +++++ fuzz/fuzz_targets/validate.rs | 20 ++++++++++++++++++++ src/form.rs | 9 +++++++++ src/schema.rs | 17 +++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 fuzz/fuzz_targets/validate.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 9807532..f96c540 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -11,6 +11,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.3.1" +serde_json = "1.0" [dependencies.jtd] path = ".." @@ -23,3 +24,7 @@ members = ["."] [[bin]] name = "serde_schema_try_into" path = "fuzz_targets/serde_schema_try_into.rs" + +[[bin]] +name = "validate" +path = "fuzz_targets/validate.rs" diff --git a/fuzz/fuzz_targets/validate.rs b/fuzz/fuzz_targets/validate.rs new file mode 100644 index 0000000..adb8417 --- /dev/null +++ b/fuzz/fuzz_targets/validate.rs @@ -0,0 +1,20 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use serde_json; + +fuzz_target!(|schema_and_instance: (jtd::schema::Schema, Vec)| { + let validator = jtd::validator::Validator { + max_errors: None, + max_depth: None, + }; + + // We're only interested in fuzzing against valid schemas. + if schema_and_instance.0.validate().is_err() { + return; + } + + if let Ok(instance) = serde_json::from_slice(&schema_and_instance.1) { + let _ = validator.validate(&schema_and_instance.0, &instance); + } +}); diff --git a/src/form.rs b/src/form.rs index 7e1f3d2..a7e08ff 100644 --- a/src/form.rs +++ b/src/form.rs @@ -4,6 +4,7 @@ use std::collections::HashSet; use std::str::FromStr; #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub enum Form { Empty, Ref(Ref), @@ -22,18 +23,21 @@ impl Default for Form { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub struct Ref { pub nullable: bool, pub definition: String, } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub struct Type { pub nullable: bool, pub type_value: TypeValue, } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub enum TypeValue { Boolean, Float32, @@ -70,18 +74,21 @@ impl FromStr for TypeValue { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub struct Enum { pub nullable: bool, pub values: HashSet, } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub struct Elements { pub nullable: bool, pub schema: Box, } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub struct Properties { pub nullable: bool, pub required: HashMap, @@ -91,12 +98,14 @@ pub struct Properties { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub struct Values { pub nullable: bool, pub schema: Box, } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] pub struct Discriminator { pub nullable: bool, pub discriminator: String, diff --git a/src/schema.rs b/src/schema.rs index 000d536..a713ef7 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -11,6 +11,23 @@ pub struct Schema { pub metadata: HashMap, } +#[cfg(feature = "fuzz")] +impl arbitrary::Arbitrary for Schema { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + Ok(Schema { + definitions: arbitrary::Arbitrary::arbitrary(u)?, + form: arbitrary::Arbitrary::arbitrary(u)?, + + // serde_json::Value does not derive Arbitrary. That's ok, because + // for the fuzz tests we're doing, we don't really care about + // manipulating arbitrary JSON values. + // + // So we'll always have metadata be None. + metadata: HashMap::new(), + }) + } +} + #[derive(Debug, PartialEq)] pub enum SerdeConvertError { InvalidForm,