1use super::eval::{DecodeLimits, FilterRecord, HeaderValueRef, PathTrie};
2use super::{ConsumerFilter, FaultReason, FilterCodec};
3use crate::control::{SchemaDef, SchemaSource};
4use crate::error::InvalidError;
5use crate::limits::{MAX_FILTER_SCHEMA_BYTES, MAX_FILTER_SCHEMA_DEPTH};
6use apache_avro::Schema;
7use apache_avro::reader::datum::GenericDatumReader;
8use apache_avro::schema::{InnerDecimalSchema, ResolvedSchema, UuidSchema};
9use apache_avro::types::Value as Avro;
10use prost_reflect::{
11 DescriptorPool, DynamicMessage, Kind, MapKey, MessageDescriptor, ReflectMessage, Value as Proto,
12};
13use serde_json::{Map, Number, Value};
14use std::collections::BTreeMap;
15use std::fmt;
16use std::sync::Arc;
17
18#[derive(Clone, Debug, Default)]
20pub struct PayloadDecoders {
21 schemas: BTreeMap<u32, WriterSchema>,
22}
23
24#[derive(Clone, Debug)]
25enum WriterSchema {
26 Avro(Arc<AvroDecoder>),
27 Protobuf(MessageDescriptor),
28}
29
30#[ouroboros::self_referencing]
31struct AvroDecoder {
32 schema: Schema,
33 #[borrows(schema)]
34 #[covariant]
35 names: ResolvedSchema<'this>,
36 #[borrows(schema)]
37 #[covariant]
38 reader: GenericDatumReader<'this>,
39}
40
41impl fmt::Debug for AvroDecoder {
42 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
43 formatter
44 .debug_struct("AvroDecoder")
45 .field("schema", self.borrow_schema())
46 .finish_non_exhaustive()
47 }
48}
49
50impl AvroDecoder {
51 fn compile(source: &str) -> Result<Self, String> {
52 AvroDecoderTryBuilder {
55 schema: physical_avro_schema(source)?,
56 names_builder: |schema| ResolvedSchema::new(schema),
57 reader_builder: |schema| GenericDatumReader::builder(schema).build(),
58 }
59 .try_build()
60 .map_err(|error| error.to_string())
61 }
62}
63
64impl PayloadDecoders {
65 pub fn compile(filter: &ConsumerFilter, schemas: &[SchemaDef]) -> Result<Self, InvalidError> {
66 let mut prepared = BTreeMap::new();
67 for id in &filter.schema_refs {
68 let schema = schemas
69 .iter()
70 .find(|schema| schema.id == *id)
71 .ok_or_else(|| InvalidError::new(format!("writer schema {id} was not supplied")))?;
72 let decoder = match (&schema.source, filter.codec) {
73 (SchemaSource::Avro { schema }, FilterCodec::Avro) => {
74 WriterSchema::Avro(Arc::new(AvroDecoder::compile(schema).map_err(|error| {
75 InvalidError::new(format!("Avro schema {id}: {error}"))
76 })?))
77 }
78 (
79 SchemaSource::Protobuf {
80 descriptor_set,
81 message_type,
82 },
83 FilterCodec::Protobuf,
84 ) => {
85 if descriptor_set.len() > MAX_FILTER_SCHEMA_BYTES {
86 return Err(InvalidError::new(format!(
87 "Protobuf descriptor {id} exceeds {MAX_FILTER_SCHEMA_BYTES} bytes"
88 )));
89 }
90 let pool =
91 DescriptorPool::decode(descriptor_set.as_slice()).map_err(|error| {
92 InvalidError::new(format!("Protobuf schema {id}: {error}"))
93 })?;
94 WriterSchema::Protobuf(
95 pool.get_message_by_name(message_type.trim_start_matches('.'))
96 .ok_or_else(|| {
97 InvalidError::new(format!(
98 "schema {id} has no message {message_type}"
99 ))
100 })?,
101 )
102 }
103 _ => {
104 return Err(InvalidError::new(format!(
105 "schema {id} does not match codec {}",
106 filter.codec
107 )));
108 }
109 };
110 prepared.insert(*id, decoder);
111 }
112 Ok(Self { schemas: prepared })
113 }
114
115 pub fn decode(
116 &self,
117 codec: FilterCodec,
118 record: &FilterRecord<'_>,
119 limits: &DecodeLimits,
120 ) -> Result<Value, FaultReason> {
121 self.decode_paths(codec, record, limits, &PathTrie::whole())
122 }
123
124 pub(super) fn decode_paths(
125 &self,
126 codec: FilterCodec,
127 record: &FilterRecord<'_>,
128 limits: &DecodeLimits,
129 paths: &PathTrie,
130 ) -> Result<Value, FaultReason> {
131 if record.payload.len() > limits.max_payload_bytes {
132 return Err(FaultReason::TooLarge);
133 }
134 if codec == FilterCodec::Cbor {
135 return decode_cbor(record.payload, limits);
136 }
137 let id = record
138 .headers
139 .iter()
140 .rev()
141 .find(|header| header.key == crate::headers::SCHEMA_ID)
142 .and_then(|header| match header.value {
143 HeaderValueRef::Uint(id) => u32::try_from(id).ok(),
144 _ => None,
145 })
146 .ok_or(FaultReason::MissingSchema)?;
147 match self.schemas.get(&id).ok_or(FaultReason::SchemaNotAllowed)? {
148 WriterSchema::Avro(decoder) if codec == FilterCodec::Avro => {
149 let mut input = Input::new(record.payload, limits);
150 input.avro(
151 decoder.borrow_schema(),
152 decoder.borrow_names().get_names(),
153 0,
154 )?;
155 input.finish()?;
156 let mut bytes = record.payload;
157 let decoded = decoder
158 .borrow_reader()
159 .read_value(&mut bytes)
160 .map_err(|_| FaultReason::Malformed)?;
161 avro_value(decoded, paths)
162 }
163 WriterSchema::Protobuf(descriptor) if codec == FilterCodec::Protobuf => {
164 let mut input = Input::new(record.payload, limits);
165 input.protobuf(descriptor, 0)?;
166 let message = DynamicMessage::decode(descriptor.clone(), record.payload)
167 .map_err(|_| FaultReason::Malformed)?;
168 protobuf_message(&message)
169 }
170 _ => Err(FaultReason::SchemaMismatch),
171 }
172 }
173}
174
175pub fn decode_cbor(payload: &[u8], limits: &DecodeLimits) -> Result<Value, FaultReason> {
177 if payload.len() > limits.max_payload_bytes {
178 return Err(FaultReason::TooLarge);
179 }
180 let mut checked = Input::new(payload, limits);
181 checked.cbor(0)?;
182 checked.finish()?;
183 let mut bytes = payload;
184 let value = ciborium::de::from_reader_with_recursion_limit::<super::cbor::CborValue, _>(
185 &mut bytes,
186 limits
187 .max_depth
188 .min(crate::limits::MAX_FILTER_PARSE_DEPTH)
189 .saturating_add(2),
190 )
191 .map_err(|error| match error {
192 ciborium::de::Error::RecursionLimitExceeded => FaultReason::TooDeep,
193 _ => FaultReason::Malformed,
194 })?;
195 if !bytes.is_empty() {
196 return Err(FaultReason::Malformed);
197 }
198 Ok(value.0)
199}
200
201#[cfg(test)]
202fn cbor_value(value: ciborium::Value) -> Result<Value, FaultReason> {
203 Ok(match value {
204 ciborium::Value::Null => Value::Null,
205 ciborium::Value::Bool(value) => Value::Bool(value),
206 ciborium::Value::Integer(value) => {
207 let integer = i128::from(value);
208 if let Ok(value) = i64::try_from(integer) {
209 Value::from(value)
210 } else if let Ok(value) = u64::try_from(integer) {
211 Value::from(value)
212 } else {
213 return Err(FaultReason::Malformed);
214 }
215 }
216 ciborium::Value::Float(value) => number(value)?,
217 ciborium::Value::Text(value) => Value::String(value),
218 ciborium::Value::Bytes(value) => bytes_value(&value),
219 ciborium::Value::Array(items) => Value::Array(
220 items
221 .into_iter()
222 .map(cbor_value)
223 .collect::<Result<_, _>>()?,
224 ),
225 ciborium::Value::Map(items) => {
226 let mut object = Map::new();
227 for (key, value) in items {
228 let ciborium::Value::Text(key) = key else {
229 return Err(FaultReason::Malformed);
230 };
231 if object.insert(key, cbor_value(value)?).is_some() {
232 return Err(FaultReason::Malformed);
233 }
234 }
235 Value::Object(object)
236 }
237 _ => return Err(FaultReason::Malformed),
239 })
240}
241
242fn physical_avro_schema(schema: &str) -> Result<Schema, String> {
243 fn strip(value: &mut Value) {
246 match value {
247 Value::Object(map) => {
248 map.remove("logicalType");
249 for value in map.values_mut() {
250 strip(value);
251 }
252 }
253 Value::Array(items) => {
254 for value in items {
255 strip(value);
256 }
257 }
258 _ => {}
259 }
260 }
261 let mut value = super::eval::decode_json(
262 schema.as_bytes(),
263 &DecodeLimits {
264 max_payload_bytes: MAX_FILTER_SCHEMA_BYTES,
265 max_depth: MAX_FILTER_SCHEMA_DEPTH,
266 },
267 )
268 .map_err(|reason| format!("schema JSON is {reason}"))?;
269 strip(&mut value);
270 Schema::parse_str(&value.to_string()).map_err(|error| error.to_string())
271}
272
273fn number(value: f64) -> Result<Value, FaultReason> {
274 Number::from_f64(value)
275 .map(Value::Number)
276 .ok_or(FaultReason::Malformed)
277}
278
279fn bytes_value(bytes: &[u8]) -> Value {
280 Value::Array(bytes.iter().copied().map(Value::from).collect())
281}
282
283fn avro_value(value: apache_avro::types::Value, paths: &PathTrie) -> Result<Value, FaultReason> {
284 let whole = PathTrie::whole();
285 Ok(match value {
286 Avro::Null => Value::Null,
287 Avro::Boolean(value) => Value::Bool(value),
288 Avro::Int(value) => Value::from(value),
289 Avro::Long(value) => Value::from(value),
290 Avro::Float(value) => number(f64::from(value))?,
291 Avro::Double(value) => number(value)?,
292 Avro::String(value) | Avro::Enum(_, value) => Value::String(value),
293 Avro::Bytes(value) | Avro::Fixed(_, value) => bytes_value(&value),
294 Avro::Union(_, value) => avro_value(*value, paths)?,
295 Avro::Record(fields) => Value::Object(
296 fields
297 .into_iter()
298 .filter_map(|(key, value)| {
299 let selected = if paths.whole {
300 Some(&whole)
301 } else {
302 paths.child(&key)
303 };
304 selected.map(|child| avro_value(value, child).map(|value| (key, value)))
305 })
306 .collect::<Result<_, _>>()?,
307 ),
308 Avro::Map(fields) => Value::Object(
309 fields
310 .into_iter()
311 .filter_map(|(key, value)| {
312 let selected = if paths.whole {
313 Some(&whole)
314 } else {
315 paths.child(&key)
316 };
317 selected.map(|child| avro_value(value, child).map(|value| (key, value)))
318 })
319 .collect::<Result<_, _>>()?,
320 ),
321 Avro::Array(values) => {
322 let mut selected = Vec::new();
323 for (index, value) in values.into_iter().enumerate() {
324 let child = if paths.whole {
325 Some(&whole)
326 } else {
327 u32::try_from(index)
328 .ok()
329 .and_then(|index| paths.index(index))
330 };
331 if let Some(child) = child {
332 selected.resize(index, Value::Null);
333 selected.push(avro_value(value, child)?);
334 }
335 }
336 Value::Array(selected)
337 }
338 _ => return Err(FaultReason::Malformed),
339 })
340}
341
342fn protobuf_message(message: &DynamicMessage) -> Result<Value, FaultReason> {
346 let mut object = Map::new();
347 for field in message.descriptor().fields() {
348 let set = message.has_field(&field);
349 if field.is_required() && !set {
350 return Err(FaultReason::Malformed);
351 }
352 if set || !field.supports_presence() {
353 object.insert(
354 field.name().to_owned(),
355 protobuf_value(&message.get_field(&field))?,
356 );
357 }
358 }
359 Ok(Value::Object(object))
360}
361
362fn protobuf_value(value: &Proto) -> Result<Value, FaultReason> {
363 Ok(match value {
364 Proto::Bool(value) => Value::Bool(*value),
365 Proto::I32(value) | Proto::EnumNumber(value) => Value::from(*value),
366 Proto::I64(value) => Value::from(*value),
367 Proto::U32(value) => Value::from(*value),
368 Proto::U64(value) => Value::from(*value),
369 Proto::F32(value) => number(f64::from(*value))?,
370 Proto::F64(value) => number(*value)?,
371 Proto::String(value) => Value::String(value.clone()),
372 Proto::Bytes(value) => bytes_value(value),
373 Proto::Message(value) => protobuf_message(value)?,
374 Proto::List(values) => Value::Array(
375 values
376 .iter()
377 .map(protobuf_value)
378 .collect::<Result<_, _>>()?,
379 ),
380 Proto::Map(values) => Value::Object(
381 values
382 .iter()
383 .map(|(key, value)| {
384 let key = match key {
385 MapKey::Bool(value) => value.to_string(),
386 MapKey::I32(value) => value.to_string(),
387 MapKey::I64(value) => value.to_string(),
388 MapKey::U32(value) => value.to_string(),
389 MapKey::U64(value) => value.to_string(),
390 MapKey::String(value) => value.clone(),
391 };
392 Ok((key, protobuf_value(value)?))
393 })
394 .collect::<Result<_, FaultReason>>()?,
395 ),
396 })
397}
398
399struct Input<'a> {
402 bytes: &'a [u8],
403 limits: &'a DecodeLimits,
404 remaining_values: usize,
405}
406
407impl<'a> Input<'a> {
408 fn new(bytes: &'a [u8], limits: &'a DecodeLimits) -> Self {
409 Self {
410 bytes,
411 limits,
412 remaining_values: limits.max_payload_bytes.max(1),
413 }
414 }
415
416 fn finish(&self) -> Result<(), FaultReason> {
417 if self.bytes.is_empty() {
418 Ok(())
419 } else {
420 Err(FaultReason::Malformed)
421 }
422 }
423
424 fn take(&mut self, count: usize) -> Result<&'a [u8], FaultReason> {
425 let result = self.bytes.get(..count).ok_or(FaultReason::Malformed)?;
426 self.bytes = &self.bytes[count..];
427 Ok(result)
428 }
429
430 fn varint(&mut self) -> Result<u64, FaultReason> {
431 let mut result = 0u64;
432 for shift in (0..70).step_by(7) {
433 let byte = self.take(1)?[0];
434 if shift == 63 && byte > 1 {
435 return Err(FaultReason::Malformed);
436 }
437 result |= u64::from(byte & 127) << shift;
438 if byte < 128 {
439 return Ok(result);
440 }
441 }
442 Err(FaultReason::Malformed)
443 }
444
445 fn long(&mut self) -> Result<i64, FaultReason> {
446 let raw = self.varint()?;
447 Ok(((raw >> 1) as i64) ^ -((raw & 1) as i64))
448 }
449
450 fn sized(&mut self) -> Result<(), FaultReason> {
451 let count = usize::try_from(self.long()?).map_err(|_| FaultReason::Malformed)?;
452 self.take(count)?;
453 Ok(())
454 }
455
456 fn visit(&mut self, depth: usize) -> Result<(), FaultReason> {
457 if depth
458 > self
459 .limits
460 .max_depth
461 .min(crate::limits::MAX_FILTER_PARSE_DEPTH)
462 {
463 return Err(FaultReason::TooDeep);
464 }
465 self.remaining_values = self
466 .remaining_values
467 .checked_sub(1)
468 .ok_or(FaultReason::TooLarge)?;
469 Ok(())
470 }
471
472 fn avro(
473 &mut self,
474 schema: &Schema,
475 names: &apache_avro::schema::NamesRef<'_>,
476 depth: usize,
477 ) -> Result<(), FaultReason> {
478 self.visit(depth)?;
479 match schema {
480 Schema::Null => {}
481 Schema::Boolean => {
482 if self.take(1)?[0] > 1 {
483 return Err(FaultReason::Malformed);
484 }
485 }
486 Schema::Int
487 | Schema::Long
488 | Schema::Date
489 | Schema::TimeMillis
490 | Schema::TimeMicros
491 | Schema::TimestampMillis
492 | Schema::TimestampMicros
493 | Schema::TimestampNanos
494 | Schema::LocalTimestampMillis
495 | Schema::LocalTimestampMicros
496 | Schema::LocalTimestampNanos
497 | Schema::Enum(_) => {
498 self.long()?;
499 }
500 Schema::Float => {
501 let bytes: [u8; 4] = self
502 .take(4)?
503 .try_into()
504 .map_err(|_| FaultReason::Malformed)?;
505 if !f32::from_le_bytes(bytes).is_finite() {
506 return Err(FaultReason::Malformed);
507 }
508 }
509 Schema::Double => {
510 let bytes: [u8; 8] = self
511 .take(8)?
512 .try_into()
513 .map_err(|_| FaultReason::Malformed)?;
514 if !f64::from_le_bytes(bytes).is_finite() {
515 return Err(FaultReason::Malformed);
516 }
517 }
518 Schema::Bytes | Schema::String | Schema::BigDecimal => self.sized()?,
519 Schema::Fixed(fixed) | Schema::Duration(fixed) => {
520 self.take(fixed.size)?;
521 }
522 Schema::Decimal(decimal) => match &decimal.inner {
523 InnerDecimalSchema::Bytes => self.sized()?,
524 InnerDecimalSchema::Fixed(fixed) => {
525 self.take(fixed.size)?;
526 }
527 },
528 Schema::Uuid(uuid) => match uuid {
529 UuidSchema::Fixed(fixed) => {
530 self.take(fixed.size)?;
531 }
532 _ => self.sized()?,
533 },
534 Schema::Union(union) => {
535 let index = usize::try_from(self.long()?).map_err(|_| FaultReason::Malformed)?;
536 self.avro(
537 union.variants().get(index).ok_or(FaultReason::Malformed)?,
538 names,
539 depth + 1,
540 )?;
541 }
542 Schema::Record(record) => {
543 for field in &record.fields {
544 self.avro(&field.schema, names, depth + 1)?;
545 }
546 }
547 Schema::Ref { name } => {
548 self.avro(names.get(name).ok_or(FaultReason::Malformed)?, names, depth)?
549 }
550 Schema::Array(array) => self.blocks(&array.items, names, depth, false)?,
551 Schema::Map(map) => self.blocks(&map.types, names, depth, true)?,
552 }
553 Ok(())
554 }
555
556 fn blocks(
557 &mut self,
558 item: &Schema,
559 names: &apache_avro::schema::NamesRef<'_>,
560 depth: usize,
561 map: bool,
562 ) -> Result<(), FaultReason> {
563 loop {
564 let count = self.long()?;
565 if count == 0 {
566 return Ok(());
567 }
568 let block_end = if count < 0 {
569 let size = usize::try_from(self.long()?).map_err(|_| FaultReason::Malformed)?;
570 Some(
571 self.bytes
572 .len()
573 .checked_sub(size)
574 .ok_or(FaultReason::Malformed)?,
575 )
576 } else {
577 None
578 };
579 let count = usize::try_from(count.checked_abs().ok_or(FaultReason::TooLarge)?)
580 .map_err(|_| FaultReason::TooLarge)?;
581 if count > self.remaining_values {
582 return Err(FaultReason::TooLarge);
583 }
584 for _ in 0..count {
585 if map {
586 self.sized()?;
587 }
588 self.avro(item, names, depth + 1)?;
589 }
590 if block_end.is_some_and(|end| self.bytes.len() != end) {
591 return Err(FaultReason::Malformed);
592 }
593 }
594 }
595
596 fn cbor(&mut self, depth: usize) -> Result<(), FaultReason> {
597 self.visit(depth)?;
598 let head = self.take(1)?[0];
599 let major = head >> 5;
600 let info = head & 31;
601 if major == 6 || info == 31 {
602 return Err(FaultReason::Malformed);
603 }
604 let mut value = u64::from(info);
605 if info >= 24 {
606 if info > 27 {
607 return Err(FaultReason::Malformed);
608 }
609 value = 0;
610 for byte in self.take(1 << (info - 24))? {
611 value = (value << 8) | u64::from(*byte);
612 }
613 }
614 if major == 2 || major == 3 {
615 self.take(usize::try_from(value).map_err(|_| FaultReason::Malformed)?)?;
616 }
617 if major == 4 || major == 5 {
618 let count = value
619 .checked_mul(if major == 5 { 2 } else { 1 })
620 .ok_or(FaultReason::Malformed)?;
621 if count > self.bytes.len() as u64 {
622 return Err(FaultReason::Malformed);
623 }
624 for _ in 0..count {
625 self.cbor(depth + 1)?;
626 }
627 }
628 Ok(())
629 }
630
631 fn protobuf(
632 &mut self,
633 descriptor: &MessageDescriptor,
634 depth: usize,
635 ) -> Result<(), FaultReason> {
636 self.visit(depth)?;
637 while !self.bytes.is_empty() {
638 let key = self.varint()?;
639 let field = u32::try_from(key >> 3).map_err(|_| FaultReason::Malformed)?;
640 if field == 0 || field > 0x1fff_ffff {
641 return Err(FaultReason::Malformed);
642 }
643 match key & 7 {
644 0 => {
645 self.varint()?;
646 }
647 1 => {
648 self.take(8)?;
649 }
650 2 => {
651 let count =
652 usize::try_from(self.varint()?).map_err(|_| FaultReason::TooLarge)?;
653 let data = self.take(count)?;
654 if let Some(field) = descriptor.get_field(field)
655 && let Kind::Message(nested) = field.kind()
656 {
657 let mut child = Self {
658 bytes: data,
659 limits: self.limits,
660 remaining_values: self.remaining_values,
661 };
662 child.protobuf(&nested, depth + 1)?;
663 self.remaining_values = child.remaining_values;
664 }
665 }
666 5 => {
667 self.take(4)?;
668 }
669 _ => return Err(FaultReason::Malformed),
670 }
671 }
672 Ok(())
673 }
674}
675
676#[cfg(test)]
677mod cbor_tests {
678 use super::*;
679 use ciborium::Value as Cbor;
680
681 #[test]
682 fn given_cbor_scalars_and_collections_when_decoded_directly_then_should_match_the_previous_value_rules()
683 {
684 let values = [
685 Cbor::Integer(i64::MIN.into()),
686 Cbor::Integer(u64::MAX.into()),
687 Cbor::Float(-0.0),
688 Cbor::Float(0.25),
689 Cbor::Float(f64::NAN),
690 Cbor::Float(f64::INFINITY),
691 Cbor::Bytes(vec![0, 128, 255]),
692 Cbor::Text("λ".to_owned()),
693 Cbor::Array(vec![
694 Cbor::Null,
695 Cbor::Bool(true),
696 Cbor::Array(vec![Cbor::Integer(7.into())]),
697 ]),
698 Cbor::Map(vec![(
699 Cbor::Text("key".to_owned()),
700 Cbor::Integer(42.into()),
701 )]),
702 Cbor::Map(vec![(Cbor::Integer(1.into()), Cbor::Null)]),
703 Cbor::Map(vec![
704 (Cbor::Text("key".to_owned()), Cbor::Null),
705 (Cbor::Text("key".to_owned()), Cbor::Bool(true)),
706 ]),
707 Cbor::Tag(1, Box::new(Cbor::Integer(0.into()))),
708 ];
709 for value in values {
710 let expected = cbor_value(value.clone());
711 let mut bytes = Vec::new();
712 ciborium::ser::into_writer(&value, &mut bytes).expect("CBOR fixture");
713 assert_eq!(
714 decode_cbor(&bytes, &DecodeLimits::default()),
715 expected,
716 "{value:?}"
717 );
718 }
719 }
720
721 #[test]
722 fn given_cbor_bytes_when_decoded_then_should_preserve_exact_unsigned_values_and_byte_arrays() {
723 let bytes = [
724 0x82, 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x42, 0x00, 0xff,
725 ];
726 assert_eq!(
727 decode_cbor(&bytes, &DecodeLimits::default()).expect("bounded CBOR"),
728 serde_json::json!([u64::MAX, [0, 255]])
729 );
730 for malformed in [
731 &[0x61, 0xff][..],
732 &[0x9a, 1, 0, 0, 0][..],
733 &[0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..],
734 ] {
735 assert_eq!(
736 decode_cbor(malformed, &DecodeLimits::default()),
737 Err(FaultReason::Malformed)
738 );
739 }
740 }
741}