{"record":{"id":"f6f2c9af99948648","repo":"unicity-aos/aos-ce","slug":"canonical-document-exceeds-bound","errorCode":null,"errorMessage":"canonical document exceeds bound","messagePattern":"canonical document exceeds bound","errorType":"validation","errorClass":"serde_json::Error","httpStatus":null,"severity":"error","filePath":"capsules/capsule-surface-model/src/canonical.rs","lineNumber":174,"sourceCode":"pub fn canonical_bytes<T: Serialize>(value: &T) -> Result<Vec<u8>, serde_json::Error> {\n    let raw = serde_json::to_vec(value)?;\n    let parsed = serde_json::from_slice::<CanonicalJson>(&raw).map_err(|error| {\n        serde_json::Error::io(std::io::Error::new(std::io::ErrorKind::InvalidData, error))\n    })?;\n    serde_json::to_vec(&parsed)\n}\n\n/// Serialize to deterministic canonical JSON text.\npub fn canonical_string<T: Serialize>(value: &T) -> Result<String, serde_json::Error> {\n    canonical_bytes(value).map(|bytes| String::from_utf8(bytes).expect(\"JSON is UTF-8\"))\n}\n\n/// Parse once for duplicate keys, canonicalize, then deserialize the typed value.\npub fn parse_canonical<T: serde::de::DeserializeOwned>(\n    input: &[u8],\n) -> Result<T, serde_json::Error> {\n    if input.len() > MAX_CANONICAL_DOCUMENT_BYTES {\n        return Err(serde_json::Error::io(std::io::Error::new(\n            std::io::ErrorKind::InvalidData,\n            \"canonical document exceeds bound\",\n        )));\n    }\n    let parsed = serde_json::from_slice::<CanonicalJson>(input)?;\n    let canonical = serde_json::to_vec(&parsed)?;\n    serde_json::from_slice(&canonical)\n}\n\n/// Canonical digest for one or more ordered semantic values.\npub fn digest_parts<T: Serialize>(parts: &T) -> Result<String, serde_json::Error> {\n    Ok(blake3::hash(&canonical_bytes(parts)?).to_hex().to_string())\n}\n\n/// Validate a BLAKE3 digest in its lowercase hexadecimal form.\npub fn valid_blake3_digest(value: &str) -> bool {\n    value.len() == 64\n        && value","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/capsules/capsule-surface-model/src/canonical.rs#L156-L192","documentation":"parse_canonical rejects any input larger than MAX_CANONICAL_DOCUMENT_BYTES before parsing, returning a serde_json::Error wrapping an InvalidData io error with the message \"canonical document exceeds bound\". The library enforces a hard size cap to guarantee bounded memory use when parsing and canonicalizing untrusted canonical JSON documents.","triggerScenarios":"Calling parse_canonical::<T>() with an input byte slice whose length exceeds MAX_CANONICAL_DOCUMENT_BYTES. Detected before any JSON parsing occurs.","commonSituations":"Accidentally passing a whole file to parse_canonical when it contains multiple concatenated documents or appended data; a client sending an unexpectedly large canonical payload; test fixtures growing past the limit after schema additions (seen by tests like exact_canonical_fixtures_round_trip and canonical_json_rejects_duplicate_oversized_and_unknown_input).","solutions":["Measure input.len() before calling and split, compress, or reject oversized documents upstream.","Check the configured MAX_CANONICAL_DOCUMENT_BYTES constant and, if legitimately too small for your documents, raise it in the source.","Remove extraneous bytes (trailing whitespace, appended blobs) from the input before parsing.","Match on the returned serde_json::Error's io kind InvalidData with this message to surface a clear 'payload too large' error to the caller."],"exampleFix":"// before\nlet value: MyDoc = parse_canonical(&raw)?;\n// after\nconst MAX: usize = MAX_CANONICAL_DOCUMENT_BYTES;\nif raw.len() > MAX {\n    return Err(MyError::PayloadTooLarge(raw.len(), MAX));\n}\nlet value: MyDoc = parse_canonical(&raw)?;","handlingStrategy":"validation","validationCode":"fn ensure_within_bound(input: &[u8]) -> Result<(), String> {\n    if input.len() > MAX_CANONICAL_DOCUMENT_BYTES {\n        return Err(format!(\"input is {} bytes, limit is {}\", input.len(), MAX_CANONICAL_DOCUMENT_BYTES));\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match parse_canonical::<T>(&input) {\n    Err(e) if e.io_error_kind() == Some(std::io::ErrorKind::InvalidData) => /* payload too large: report sizes and reject upstream */,\n    other => other?,\n}","preventionTips":["Check input.len() against MAX_CANONICAL_DOCUMENT_BYTES before every parse call.","Enforce the same limit at the network/queue boundary so oversized payloads never reach parsing.","Add a round-trip test with a fixture at the exact size bound to catch limit regressions."],"tags":["payload-too-large","validation","json","rust"],"backgroundTag":"payload-too-large","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}