feat(serder)!: #142 strict canonical read-path parser — offset-based SAID verification#146
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…roperty tests (#142) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…#142) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ation (#142) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…#142) VersionString::parse sliced the input &str at fixed byte offsets; a multi-byte UTF-8 char straddling a field boundary (offsets 4, 5, 6, 10, or 16) is not a char boundary, so the slice panicked — a DoS on untrusted input. The panic predates #142: it is reachable on main via validate_version_string in deserialize.rs, which feeds serde_json- unescaped arbitrary UTF-8 into VersionString::parse. Closed at the root: every fixed-offset slice now goes through checked str::get and returns InvalidVersionString. Defense in depth at the strict-parser seam: canonical.rs::head() now rejects any non-ASCII byte in the 17-byte version window with a NonCanonical error reporting the precise violating byte, before the window ever reaches VersionString::parse. Also: document require_ilk's error-offset convention, and add five grammar rejection probes (wrong first byte, oversized ilk, delegator field on icp, missing delegator on dip, corrupt version terminator seam). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… hash (#142) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…142) BREAKING: non-canonical input (whitespace, reordered/duplicate fields, escapes, trailing bytes) now fails with SerderError::NonCanonical; per-ilk deserializers require their exact ilk (deserialize_rotation no longer accepts drt bytes, deserialize_inception no longer accepts dip bytes). The tolerant serde_json path survives as the #[cfg(test)] differential oracle (deserialize::reference). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rror> (#142) BREAKING: verify_said now parses with the strict canonical parser and returns Ok(()) / Err(SaidMismatch) instead of Result<bool, _>; non-canonical input fails with NonCanonical instead of being tolerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-subset properties (#142) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…142) typos gate flagged "abd" as a misspelling of "and"/"bad"; the actual source test in canonical.rs uses "abX" for this mismatch scenario — fix the plan doc's transcription to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t read path (#142) Pins every ParsedSeal/Tholder/Count/Identifier variant, both SAID branches (single d!=i and double d==i), all five ilk-dispatch arms, and each reachable read-path error variant through strict==oracle==roundtrip, replacing probabilistic proptest coverage of these variants with guaranteed deterministic checks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ey extend (#142) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merging this PR will not alter performance
Performance Changes
Comparing Footnotes
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #142. The read-path half of the #79 seam (design §3.3/§3.5).
What
serder::deserialize::canonical): compact JSON, spec field order, no escapes/duplicates/whitespace — rejected by construction with the new typedSerderError::NonCanonical { offset, expected, found }.Valuetrees and a full re-render per ingested event.&strfield views (ParsedIcp<'a>…) — the substrate for C-a C-a · Zero-copy event input — borrow-ify KeriEvent across serder #129's borrow-ifiedKeriEvent<'a>.intive=Trueintegerkt/nt/btstill accepted (their SAIDs are computed over the integer form; rejecting them is a parity gap).VersionString::parsepanicked on multi-byte UTF-8 straddling its fixed slice offsets (non-char-boundary&strslicing), reachable throughdeserialize_eventvia the old tolerant path. Fixed at the root with failing-first regression tests.Breaking (MINOR under 0.x)
SerderError::NonCanonicalvariant; non-canonical inputs now fail with it instead of assortedJson/MissingFielderrors (the strict path never returnsMissingField).said::verify_saidis strict and returnsResult<(), SerderError>(wasResult<bool, _>); its digest-code trust contract is now documented.deserialize_rotationno longer acceptsdrtbytes;deserialize_inceptionno longer silently acceptsdipbytes and drops the delegator;deserialize_interactionno longer acceptsrot/drtbytes as a truncated interaction).Verification
serder_deserialize/icpbench added; bolero+AFL fuzz targetserder_deserialize_event(idempotence oracle) wired into the deep-fuzz matrices.nix flake checkgreen (wasm + no_std included).Discovered during this work (filed separately)
i == don inception (predates this branch; surfaced by the variant matrix trying to construct a basic-derivation icp). Read→write of a keripy basic inception corruptsdandi. Read path in this PR handles those events correctly.The old
serde_json::Valueread path survives only as the#[cfg(test)]differential oracle (deserialize::reference). serde demotion to dev-dependencies is the follow-up card per design §6 staging.🤖 Generated with Claude Code