Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,13 @@ impl<'de, R: dec::Read<'de>> de::MapAccess<'de> for Accessor<'_, R> {
K: de::DeserializeSeed<'de>,
{
if self.len > 0 {
let name = "map key";
self.len -= 1;
Comment thread
vmx marked this conversation as resolved.
Outdated
// Map keys must be strings in DAG-CBOR.
let byte = peek_one(name, &mut self.de.reader)?;
if dec::if_major(byte) != major::STRING {
return Err(DecodeError::Mismatch { name, found: byte });
}
Ok(Some(seed.deserialize(&mut *self.de)?))
} else {
Ok(None)
Expand Down
9 changes: 8 additions & 1 deletion src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ pub use cbor4ii::core::utils::BufWriter;
#[cfg(feature = "std")]
use cbor4ii::core::utils::IoWriter;
use cbor4ii::core::{
dec,
enc::{self, Encode},
types,
major, types,
};
use ipld_core::cid::serde::CID_SERDE_PRIVATE_IDENTIFIER;
use serde::{ser, Serialize};
Expand Down Expand Up @@ -490,6 +491,12 @@ where
let mut mem_serializer = Serializer::new(&mut self.buffer);
key.serialize(&mut mem_serializer)
.map_err(|_| EncodeError::Msg("Map key cannot be serialized.".to_string()))?;
// Map keys must be strings in DAG-CBOR.
if let Some(byte) = self.buffer.buffer().first() {
if dec::if_major(*byte) != major::STRING {
return Err(EncodeError::Msg("Map keys must be strings".into()));
}
}
Ok(())
}

Expand Down
32 changes: 32 additions & 0 deletions tests/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,38 @@ fn test_object() {
assert_eq!(ipld.unwrap(), Ipld::Map(object));
}

#[test]
fn test_map_integer_key_rejected() {
// {1: 2} — integer key (major type 0), not a string.
let ipld: Result<Ipld, _> = de::from_slice(b"\xa1\x01\x02");
assert!(
matches!(
ipld.unwrap_err(),
DecodeError::Mismatch {
name: "map key",
found: 0x01,
}
),
"expected map key mismatch"
);
}

#[test]
fn test_map_bytes_key_rejected() {
// {h'01': 2} — byte-string key (major type 2), not a string.
let ipld: Result<Ipld, _> = de::from_slice(b"\xa1\x41\x01\x02");
assert!(
matches!(
ipld.unwrap_err(),
DecodeError::Mismatch {
name: "map key",
found: 0x41,
}
),
"expected map key mismatch"
);
}

#[test]
fn test_indefinite_object_error() {
let ipld: Result<Ipld, _> = de::from_slice(b"\xbfaa\x01ab\x9f\x02\x03\xff\xff");
Expand Down
28 changes: 27 additions & 1 deletion tests/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde_derive::Serialize;
use serde_ipld_dagcbor::{
from_slice,
ser::{BufWriter, Serializer},
to_vec,
to_vec, EncodeError,
};

#[test]
Expand Down Expand Up @@ -252,3 +252,29 @@ fn test_struct_variant_canonical() {
b"\xa1\x64Data\xa3\x61a\x01\x61b\x02\x63abc\x03"
)
}

#[test]
fn test_map_integer_key_rejected() {
Comment thread
vmx marked this conversation as resolved.
Outdated
let mut object = BTreeMap::new();
object.insert(1u32, "a");
object.insert(2u32, "b");
let err = to_vec(&object).unwrap_err();
assert!(
matches!(&err, EncodeError::Msg(msg) if msg.contains("Map keys must be strings")),
"unexpected error: {:?}",
err
);
}

#[test]
fn test_map_bytes_key_rejected() {
let mut object = BTreeMap::new();
object.insert(ByteBuf::from(vec![1u8]), "a");
object.insert(ByteBuf::from(vec![2u8]), "b");
let err = to_vec(&object).unwrap_err();
assert!(
matches!(&err, EncodeError::Msg(msg) if msg.contains("Map keys must be strings")),
"unexpected error: {:?}",
err
);
}
Loading