Skip to content

Commit

Permalink
Add roundtrip targets for entity data (#481)
Browse files Browse the repository at this point in the history
Signed-off-by: John Kastner <[email protected]>
Co-authored-by: Craig Disselkoen <[email protected]>
  • Loading branch information
john-h-kastner-aws and cdisselkoen authored Nov 21, 2024
1 parent 6474688 commit 394acc5
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cedar-drt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ The table below lists all available fuzz targets, including which component of t
| [`convert-schema-human-to-json`](fuzz/fuzz_targets/convert-schema-human-to-json.rs) | Schema parser | PBT | Test we can convert all JSON schemas to an equivalent human format schema. parse-json == parse ∘ pretty-print ∘ parse-json
| [`convert-policy-cedar-to-json`](fuzz/fuzz_targets/convert-policy-cedar-to-json.rs) | Parser, Conversion to JSON | PBT | Test we can convert all policies to an equivalent EST. parse-ast ∘ parse-cst == deserialize ∘ serialize ∘ parse-cst
| [`convert-policy-json-to-cedar`](fuzz/fuzz_targets/convert-policy-json-to-cedar.rs) | Parser, JSON Parser | PBT | Test we can convert all EST to an equivalent policy in the human-readable cedar syntax. deserialize == parse-ast ∘ pretty-print ∘ deserialize
| [`roundtrip-entities`](fuzz/fuzz_targets/roundtrip-entities.rs) | Entity parser | PBT | Test round trip property for entity data. parse-entity-json ∘ serialize-entity == id for entities |
| [`roundtrip-entities-bytes`](fuzz/fuzz_targets/roundtrip-entities.rs) | Entity parser | PBT | Test the same round trip property for entity data, starting from an arbitrary string instead of generating the entities data structure |
| | | | |
| [`partial-eval`](fuzz/fuzz_targets/partial-eval.rs) | Partial evaluator | PBT | Test that residual policies with unknowns substituted are equivalent to original policies with unknowns replaced |
| [`simple-parser`](fuzz/fuzz_targets/simple-parser.rs) | Parser | PBT | Test that parsing doesn't crash with random input strings |
Expand Down
12 changes: 12 additions & 0 deletions cedar-drt/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,15 @@ name = "request-validation"
path = "fuzz_targets/request-validation.rs"
test = false
doc = false

[[bin]]
name = "roundtrip-entities"
path = "fuzz_targets/roundtrip-entities.rs"
test = false
doc = false

[[bin]]
name = "roundtrip-entities-bytes"
path = "fuzz_targets/roundtrip-entities-bytes.rs"
test = false
doc = false
50 changes: 50 additions & 0 deletions cedar-drt/fuzz/fuzz_targets/roundtrip-entities-bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Cedar Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#![no_main]
use cedar_drt::{
entities::{EntityJsonParser, NoEntitiesSchema, TCComputation},
extensions::Extensions,
};
use cedar_policy::{entities_errors::EntitiesError, entities_json_errors::JsonSerializationError};
use libfuzzer_sys::fuzz_target;
use similar_asserts::assert_eq;

fuzz_target!(|input: String| {
let eparser: EntityJsonParser<'_, '_, NoEntitiesSchema> =
EntityJsonParser::new(None, Extensions::all_available(), TCComputation::ComputeNow);
let Ok(entities) = eparser.from_json_str(&input) else {
return;
};
let json = match entities.to_json_value() {
Ok(json) => json,
Err(EntitiesError::Serialization(JsonSerializationError::ReservedKey(_))) => {
// Serializing to JSON is expected to fail when there's a record
// attribute `__entity`, `__expr`, or `__extn`
return;
}
_ => panic!("Should be able to serialize entities to JSON"),
};
let eparser: EntityJsonParser<'_, '_, NoEntitiesSchema> = EntityJsonParser::new(
None,
Extensions::all_available(),
TCComputation::EnforceAlreadyComputed,
);
let rountripped = eparser
.from_json_value(json)
.expect("Should parse serialized entities JSON");
assert_eq!(entities, rountripped);
});
118 changes: 118 additions & 0 deletions cedar-drt/fuzz/fuzz_targets/roundtrip-entities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright Cedar Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#![no_main]

use cedar_drt::{
entities::{EntityJsonParser, NoEntitiesSchema, TCComputation},
extensions::Extensions,
Entities, ValidatorSchema,
};
use cedar_policy::{entities_errors::EntitiesError, entities_json_errors::JsonSerializationError};
use cedar_policy_generators::{
hierarchy::HierarchyGenerator, schema::Schema, settings::ABACSettings,
};
use cedar_policy_validator::CoreSchema;
use libfuzzer_sys::{
arbitrary::{self, Arbitrary, MaxRecursionReached, Unstructured},
fuzz_target,
};
use similar_asserts::assert_eq;

#[derive(Debug)]
struct FuzzTargetInput {
pub schema: ValidatorSchema,
pub entities: Entities,
}

const SETTINGS: ABACSettings = ABACSettings {
match_types: true,
enable_extensions: true,
max_depth: 10,
max_width: 10,
enable_additional_attributes: false,
enable_like: true,
enable_action_groups_and_attrs: true,
enable_arbitrary_func_call: true,
enable_unknowns: false,
enable_action_in_constraints: true,
enable_unspecified_apply_spec: true,
};

impl<'a> Arbitrary<'a> for FuzzTargetInput {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let schema = Schema::arbitrary(SETTINGS.clone(), u)?;
let entities: Entities = schema
.arbitrary_hierarchy(u)?
.try_into()
.expect("Should be able to get entities from hierarchy.");
let validator_schema: ValidatorSchema = schema
.try_into()
.expect("Should be able to get validator schema from schema.");
Ok(Self {
entities,
schema: validator_schema,
})
}

fn try_size_hint(
depth: usize,
) -> std::result::Result<(usize, Option<usize>), MaxRecursionReached> {
Ok(arbitrary::size_hint::and_all(&[
Schema::arbitrary_size_hint(depth)?,
HierarchyGenerator::size_hint(depth),
]))
}
}

fuzz_target!(|input: FuzzTargetInput| {
let json = match input.entities.to_json_value() {
Ok(json) => json,
Err(EntitiesError::Serialization(JsonSerializationError::ReservedKey(_))) => {
// Serializing to JSON is expected to fail when there's a record
// attribute `__entity`, `__expr`, or `__extn`
return;
}
_ => panic!("Should be able to serialize entities to JSON"),
};

let eparser: EntityJsonParser<'_, '_, NoEntitiesSchema> = EntityJsonParser::new(
None,
Extensions::all_available(),
TCComputation::EnforceAlreadyComputed,
);
let roundtripped_entities = eparser
.from_json_value(json.clone())
.expect("Should be able to parse serialized entity JSON");
assert_eq!(input.entities, roundtripped_entities);

let core_schema = CoreSchema::new(&input.schema);
let eparser: EntityJsonParser<'_, '_, _> = EntityJsonParser::new(
Some(&core_schema),
Extensions::all_available(),
TCComputation::EnforceAlreadyComputed,
);
let roundtripped_entities = eparser
.from_json_value(json.clone())
.expect("Should be able to parse serialized entity JSON");
// Weaker assertion for schema based parsing because it adds actions from the schema into entities.
for e in input.entities {
let roundtripped_e = roundtripped_entities
.entity(e.uid())
.expect("Schema-based roundtrip dropped entity");
assert_eq!(&e, roundtripped_e);
}
});
8 changes: 8 additions & 0 deletions cedar-drt/initialize_corpus.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ initialize_corpus() {
cp "$f" "$corpus_dir/$(uuidgen)-$(basename "$f")"
done ;;

roundtrip-entities-bytes)
for f in ../cedar/**/entity.json; do
cp "$f" "$corpus_dir/$(uuidgen)-$(basename "$f")"
done
for f in ../cedar/**/entities.json; do
cp "$f" "$corpus_dir/$(uuidgen)-$(basename "$f")"
done ;;

*)
echo "Nothing to do for fuzz target $1"
return ;;
Expand Down

0 comments on commit 394acc5

Please sign in to comment.