#![allow(missing_docs)]
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use codec_cbor::{
decode_dag_cbor, encode_dag_cbor, CborError, CborValue, MAX_DAG_CBOR_CONTAINER_ENTRIES,
MAX_DAG_CBOR_INPUT_LEN, MAX_DAG_CBOR_NODES, MAX_NESTING_DEPTH,
};
#[test]
fn dag_container_entry_limit_accepts_boundary_and_rejects_next() {
let mut accepted = vec![0x99, 0x40, 0x00];
accepted.extend(core::iter::repeat_n(0x00, MAX_DAG_CBOR_CONTAINER_ENTRIES));
let value = decode_dag_cbor(&accepted).expect("entry limit is accepted");
assert_eq!(encode_dag_cbor(&value), Ok(accepted));
let mut rejected = vec![0x99, 0x40, 0x01];
rejected.extend(core::iter::repeat_n(
0x00,
MAX_DAG_CBOR_CONTAINER_ENTRIES + 1,
));
assert_eq!(
decode_dag_cbor(&rejected),
Err(CborError::ContainerEntriesExceeded),
);
let semantic = CborValue::Array(
core::iter::repeat_with(|| CborValue::Null)
.take(MAX_DAG_CBOR_CONTAINER_ENTRIES + 1)
.collect(),
);
assert_eq!(
encode_dag_cbor(&semantic),
Err(CborError::ContainerEntriesExceeded),
);
}
#[test]
fn absolute_input_size_cap_rejects_before_parsing() {
let bytes = vec![0_u8; MAX_DAG_CBOR_INPUT_LEN + 1];
assert_eq!(decode_dag_cbor(&bytes), Err(CborError::InputTooLarge));
}
#[test]
fn debug_output_never_contains_document_bytes() {
let value = CborValue::Map(vec![(
"private-name".to_owned(),
CborValue::Bytes(b"private-bytes".to_vec()),
)]);
let debug = format!("{value:?}");
assert!(!debug.contains("private-name"));
assert!(!debug.contains("private-bytes"));
}
#[test]
fn oversized_array_length_rejected_without_allocation() {
let bytes = [0x9b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
assert_eq!(
decode_dag_cbor(&bytes),
Err(CborError::ContainerLengthExceedsInput)
);
}
#[test]
fn array_length_of_2_pow_32_rejected() {
let bytes = [0x9b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00];
assert_eq!(
decode_dag_cbor(&bytes),
Err(CborError::ContainerLengthExceedsInput)
);
}
#[test]
fn oversized_map_length_rejected_without_allocation() {
let bytes = [0xbb, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
assert_eq!(
decode_dag_cbor(&bytes),
Err(CborError::ContainerLengthExceedsInput)
);
}
#[test]
fn deeply_nested_arrays_rejected_before_stack_overflow() {
let bytes = vec![0x81u8; MAX_NESTING_DEPTH + 8];
assert_eq!(decode_dag_cbor(&bytes), Err(CborError::DepthExceeded));
}
#[test]
fn nesting_exactly_at_limit_is_accepted() {
let mut bytes = vec![0x81u8; MAX_NESTING_DEPTH];
bytes.push(0x00); let decoded = decode_dag_cbor(&bytes).expect("depth at the limit must decode");
let mut cursor = &decoded;
for _ in 0..MAX_NESTING_DEPTH {
match cursor {
codec_cbor::CborValue::Array(items) => {
assert_eq!(items.len(), 1);
cursor = &items[0];
}
other => panic!("expected array, found {other:?}"),
}
}
assert_eq!(cursor, &codec_cbor::CborValue::Int(0));
}
#[test]
fn encode_nesting_exactly_at_limit_is_accepted_and_next_is_rejected() {
let mut at_limit = CborValue::Int(0);
for _ in 0..MAX_NESTING_DEPTH {
at_limit = CborValue::Array(vec![at_limit]);
}
let encoded = encode_dag_cbor(&at_limit).expect("depth at the limit must encode");
let mut expected = vec![0x81; MAX_NESTING_DEPTH];
expected.push(0x00);
assert_eq!(encoded.as_slice(), expected);
let over_limit = CborValue::Array(vec![at_limit]);
assert_eq!(encode_dag_cbor(&over_limit), Err(CborError::DepthExceeded));
}
#[test]
fn deeply_nested_maps_rejected() {
let mut bytes = Vec::new();
for _ in 0..(MAX_NESTING_DEPTH + 4) {
bytes.push(0xa1); bytes.push(0x60); }
bytes.push(0x00); assert_eq!(decode_dag_cbor(&bytes), Err(CborError::DepthExceeded));
}
#[test]
fn truncated_array_still_rejected() {
let bytes = [0x83u8]; assert!(decode_dag_cbor(&bytes).is_err());
}
#[test]
fn nested_declared_arrays_share_the_global_node_budget() {
let mut bytes = Vec::new();
for _ in 0..5 {
bytes.extend_from_slice(&[0x99, 0x40, 0x00]);
}
bytes.extend(core::iter::repeat_n(0x00, MAX_DAG_CBOR_CONTAINER_ENTRIES));
assert_eq!(decode_dag_cbor(&bytes), Err(CborError::NodeLimitExceeded));
}
#[test]
fn dag_global_node_budget_accepts_exact_limit_and_rejects_next_in_both_directions() {
let partitions = [16_383, 16_383, 16_383, 16_382];
assert_eq!(
1 + partitions.len() + partitions.iter().sum::<usize>(),
MAX_DAG_CBOR_NODES
);
let mut value = CborValue::Array(
partitions
.into_iter()
.map(|count| CborValue::Array((0..count).map(|_| CborValue::Int(0)).collect()))
.collect(),
);
let exact = encode_dag_cbor(&value).expect("node limit must be reachable");
let decoded = decode_dag_cbor(&exact);
assert_eq!(decoded.as_ref(), Ok(&value));
let CborValue::Array(outer) = &mut value else {
panic!("constructed array changed variant");
};
let CborValue::Array(last) = &mut outer[3] else {
panic!("constructed child array changed variant");
};
last.push(CborValue::Int(0));
assert_eq!(encode_dag_cbor(&value), Err(CborError::NodeLimitExceeded));
let mut over = exact;
let fourth_header = 1 + 3 * (3 + 16_383);
assert_eq!(&over[fourth_header..fourth_header + 3], &[0x99, 0x3f, 0xfe]);
over[fourth_header + 2] = 0xff;
over.push(0x00);
assert_eq!(decode_dag_cbor(&over), Err(CborError::NodeLimitExceeded));
}
#[test]
fn dag_map_entry_cap_rejects_before_constructing_entries() {
let over_count = MAX_DAG_CBOR_CONTAINER_ENTRIES + 1;
let value = CborValue::Map(
(0..over_count)
.map(|index| (index.to_string(), CborValue::Int(0)))
.collect(),
);
assert_eq!(
encode_dag_cbor(&value),
Err(CborError::ContainerEntriesExceeded)
);
let mut bytes = vec![0xb9, 0x40, 0x01];
for _ in 0..over_count {
bytes.extend_from_slice(&[0x60, 0x00]);
}
assert_eq!(
decode_dag_cbor(&bytes),
Err(CborError::ContainerEntriesExceeded)
);
}
#[test]
fn float_rejection_metadata_never_contains_payload_bits() {
for initial in [0xf9, 0xfa, 0xfb] {
for fill in [0x61, 0x00, 0xff] {
let mut bytes = vec![fill; 9];
bytes[0] = initial;
assert_eq!(
decode_dag_cbor(&bytes),
Err(CborError::DisallowedSimpleValue {
value: u64::from(initial & 0x1f)
})
);
}
}
}
#[test]
fn dag_cbor_values_support_explicit_recursive_zeroization() {
use codec_cbor::CborValue;
use zeroize::Zeroize;
let mut value = CborValue::Map(vec![(
"identity-key".to_owned(),
CborValue::Array(vec![
CborValue::String("example".to_owned()),
CborValue::Bytes(vec![0xa5; 32]),
]),
)]);
value.zeroize();
assert_eq!(value, CborValue::Map(Vec::new()));
let mut scalar = CborValue::Int(42);
scalar.zeroize();
assert_eq!(scalar, CborValue::Int(0));
}