Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions cedar-drt/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ cargo-fuzz = true
libfuzzer-sys = "0.4"
cedar-drt = { path = "..", version = "4.4.0" }
cedar-lean-ffi = { path = "../../cedar-lean-ffi", version = "4.4.0" }
cedar-policy = { version = "4.4.0", features = ["permissive-validate", "entity-manifest"] }
cedar-policy = { version = "4.4.0", features = ["permissive-validate", "entity-manifest", "tpe"] }
cedar-policy-core = { version = "4.4.0", features = ["arbitrary", "tpe"] }
cedar-policy-formatter = "4.4.0"
cedar-policy-symcc = "*"
Expand Down Expand Up @@ -249,4 +249,16 @@ doc = false
name = "symcc-cex-pbt"
path = "fuzz_targets/symcc-cex-pbt.rs"
test = false
doc = false
doc = false

[[bin]]
name = "batched-evaluation-drt"
path = "fuzz_targets/batched-evaluation-drt.rs"
test = false
doc = false

[[bin]]
name = "batched-evaluation-pbt"
path = "fuzz_targets/batched-evaluation-pbt.rs"
test = false
doc = false
148 changes: 148 additions & 0 deletions cedar-drt/fuzz/fuzz_targets/batched-evaluation-drt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* 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::{logger::initialize_log, CedarLeanEngine};
use cedar_drt_inner::{fuzz_target, schemas};

use cedar_policy::{Entities, Policy, PolicySet, Schema, TestEntityLoader};

use cedar_policy_generators::{
abac::{ABACPolicy, ABACRequest},
err::Error,
hierarchy::HierarchyGenerator,
schema,
settings::ABACSettings,
};
use libfuzzer_sys::arbitrary::{self, Arbitrary, Unstructured};

/// Input expected by this fuzz target
#[derive(Debug, Clone)]
pub struct FuzzTargetInput {
/// generated schema
pub schema: schema::Schema,
/// generated entity slice
pub entities: Entities,
/// generated policy
pub policy: ABACPolicy,
/// the requests to try for this hierarchy and policy. We try 8 requests per
/// policy/hierarchy
pub requests: [ABACRequest; 8],
}

/// settings for this fuzz target
const SETTINGS: ABACSettings = ABACSettings {
match_types: true,
enable_extensions: true,
max_depth: 7,
max_width: 7,
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,
per_action_request_env_limit: ABACSettings::default_per_action_request_env_limit(),
total_action_request_env_limit: ABACSettings::default_total_action_request_env_limit(),
};

impl<'a> Arbitrary<'a> for FuzzTargetInput {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let schema = schema::Schema::arbitrary(SETTINGS.clone(), u)?;
let hierarchy = schema.arbitrary_hierarchy(u)?;
let policy = schema.arbitrary_policy(&hierarchy, u)?;

let requests = [
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
];
let all_entities = Entities::try_from(hierarchy).map_err(|_| Error::NotEnoughData)?;
let cedar_schema = Schema::try_from(schema.clone()).unwrap();
let entities = schemas::add_actions_to_entities(&cedar_schema, all_entities)?;
Ok(Self {
schema,
entities,
policy,
requests,
})
}

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

// This target tests a property that batched evaluation, if succeeds, should
// produce the same authorization decision based on the Lean model output
fuzz_target!(|input: FuzzTargetInput| {
let ffi = CedarLeanEngine::new();
initialize_log();

if let Ok(schema) = Schema::try_from(input.schema) {
let policy = Policy::from(input.policy);
let mut policyset = PolicySet::new();
policyset.add(policy.clone()).unwrap();
let mut loader = TestEntityLoader::new(&input.entities);
log::debug!("policy: {policyset}");
let iteration = (SETTINGS.max_depth + 1) as u32;

for req in input.requests {
let req = req.into();
log::debug!("req: {req}");
// We need to start with an `Ok` result of Rust because the
// validation DRT property is if Rust validations, then Lean also
// does. `is_authorized_batched` validates policies/request/entities
if let Ok(rust_decision) =
Comment thread
shaobo-he-aws marked this conversation as resolved.
policyset.is_authorized_batched(&req, &schema, &mut loader, iteration)
{
match ffi.get_ffi().batched_evaluation(
&policy,
&schema,
&req,
&input.entities,
iteration,
) {
Ok(lean_decision) => {
assert_eq!(lean_decision, Some(rust_decision));
}
Err(err) => {
panic!("lean failed but rust didn't: {err}");
}
}
}
}
}
});
143 changes: 143 additions & 0 deletions cedar-drt/fuzz/fuzz_targets/batched-evaluation-pbt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* 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::logger::initialize_log;
use cedar_drt_inner::{fuzz_target, schemas};

use cedar_policy::{Authorizer, Entities, Policy, PolicySet, Schema, TestEntityLoader};

use cedar_policy_core::batched_evaluator::err::BatchedEvalError;
use cedar_policy_generators::{
abac::{ABACPolicy, ABACRequest},
err::Error,
hierarchy::HierarchyGenerator,
schema,
settings::ABACSettings,
};
use libfuzzer_sys::arbitrary::{self, Arbitrary, Unstructured};

/// Input expected by this fuzz target
#[derive(Debug, Clone)]
pub struct FuzzTargetInput {
/// generated schema
pub schema: schema::Schema,
/// generated entity slice
pub entities: Entities,
/// generated policy
pub policy: ABACPolicy,
/// the requests to try for this hierarchy and policy. We try 8 requests per
/// policy/hierarchy
pub requests: [ABACRequest; 8],
}

/// settings for this fuzz target
const SETTINGS: ABACSettings = ABACSettings {
match_types: true,
enable_extensions: true,
max_depth: 7,
max_width: 7,
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,
per_action_request_env_limit: ABACSettings::default_per_action_request_env_limit(),
total_action_request_env_limit: ABACSettings::default_total_action_request_env_limit(),
};

impl<'a> Arbitrary<'a> for FuzzTargetInput {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let schema = schema::Schema::arbitrary(SETTINGS.clone(), u)?;
Comment thread
shaobo-he-aws marked this conversation as resolved.
let hierarchy = schema.arbitrary_hierarchy(u)?;
let policy = schema.arbitrary_policy(&hierarchy, u)?;

let requests = [
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
schema.arbitrary_request(&hierarchy, u)?,
];
let all_entities = Entities::try_from(hierarchy).map_err(|_| Error::NotEnoughData)?;
let cedar_schema = Schema::try_from(schema.clone()).unwrap();
let entities = schemas::add_actions_to_entities(&cedar_schema, all_entities)?;
Ok(Self {
schema,
entities,
policy,
requests,
})
}

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

// This target tests a property that batched evaluation, if succeeds, should
// produce the same authorization decision of normal authorization where the
// the entire in-memory entity store is provided
fuzz_target!(|input: FuzzTargetInput| {
initialize_log();

if let Ok(schema) = Schema::try_from(input.schema) {
let policy = Policy::from(input.policy);
let mut policyset = PolicySet::new();
policyset.add(policy.clone()).unwrap();
let mut loader = TestEntityLoader::new(&input.entities);
log::debug!("policy: {policyset}");
let iteration = (SETTINGS.max_depth + 1) as u32;

for req in input.requests {
let req = req.into();
log::debug!("req: {req}");
match policyset.is_authorized_batched(&req, &schema, &mut loader, iteration) {
Ok(batched_decision) => {
let authorizer = Authorizer::new();
assert_eq!(
batched_decision,
authorizer
.is_authorized(&req, &policyset, &input.entities)
.decision()
);
}
Err(BatchedEvalError::InsufficientIterations(_)) => {
panic!("encountered unexpected `InsufficientIterations` error");
}
Err(_) => {}
}
}
}
});
9 changes: 9 additions & 0 deletions cedar-lean-ffi/protobuf_schema/Messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,13 @@ message CheckAssertsRequest {
Asserts asserts = 1;
cedar_policy_validator.Schema schema = 2;
RequestEnv request = 3;
}

// Batched evaluation request
message BatchedEvaluationRequest {
cedar_policy_core.PolicySet policies = 1;
cedar_policy_validator.Schema schema = 2;
cedar_policy_core.Request request = 3;
cedar_policy_core.Entities entities = 4;
uint32 iteration = 5;
}
Loading