Skip to content

fix: harden verifier and CLI against malformed-input edge cases#8

Merged
EulBite merged 1 commit into
mainfrom
fix/edge-case-hardening
Jun 1, 2026
Merged

fix: harden verifier and CLI against malformed-input edge cases#8
EulBite merged 1 commit into
mainfrom
fix/edge-case-hardening

Conversation

@EulBite

@EulBite EulBite commented May 31, 2026

Copy link
Copy Markdown
Owner

Summary

A full-program test pass surfaced four reachable edge cases. None of them change the canonical output for any currently-valid input (the demo WAL chain root is unchanged), so existing signatures and cross-language vectors still verify. The changes only refuse or neutralize malformed input.

Fixes

  1. Canonical JSON, integers beyond the JS-safe range. Integers with magnitude over 2^53-1 (Number.MAX_SAFE_INTEGER) are now refused. They are exact in Rust, but a JS verifier that parsed the WAL with JSON.parse already rounded them, so the two canonical forms (and payload hashes) would silently diverge. The former i64 bound is now the JS-safe bound the module docs already assumed.
  2. Canonical JSON, NFC key collision. Two object keys that collapse to the same key after NFC normalization are rejected (DuplicateKeyAfterNfc) instead of emitting duplicate, non-injective JSON that no JS reimplementation reproduces.
  3. Integer overflow in the lenient verifier and inspect --stats. A record carrying sequence = u64::MAX overflowed prev_seq + 1 (panic in debug, silent wrap in release), violating spine-core's no-panic guarantee. Now uses saturating_add. The strict verifier was never affected because its genesis must carry sequence 1.
  4. Syslog export line injection. syslog_escape now escapes CR/LF and other C0 control characters in the MSG field. A newline in a producer-controlled field (event_type) could otherwise split the line and forge an extra syslog record.

Tests

Adds a regression test for each fix (+3 in spine-core, +2 in the CLI smoke suite).

Verification

  • cargo fmt --all --check clean; cargo clippy --workspace --all-targets -- -D warnings clean.
  • cargo test --workspace --all-features: all pass (92 core, 21 CLI smoke, 7 cross-language vectors, 4 parity, 3 wasm).
  • wasm-pack build, demo-seeder, and the Node integration test all pass; demo chain root unchanged (no hash drift).

A full-program test pass surfaced four reachable edge cases. None of them
change the canonical output for any currently-valid input (the demo WAL
chain root is unchanged), so existing signatures and cross-language
vectors still verify; the changes only refuse or neutralize bad input.

1. canonical JSON: refuse integers whose magnitude exceeds 2^53-1
   (Number.MAX_SAFE_INTEGER). Such an integer is exact in Rust but a JS
   verifier that parsed the WAL with JSON.parse already rounded it, so
   the two canonical forms (and payload hashes) would silently diverge.
   The former i64 bound is now the JS-safe bound the docs already assumed.

2. canonical JSON: reject two object keys that collapse to the same key
   after NFC normalization instead of emitting duplicate, non-injective
   JSON that no JS reimplementation reproduces.

3. lenient verifier and inspect --stats: a record carrying
   sequence = u64::MAX overflowed prev_seq + 1 (panic in debug, silent
   wrap in release), violating spine-core's no-panic guarantee. Use
   saturating_add. The strict verifier was never affected because its
   genesis must carry sequence 1.

4. syslog export: escape CR/LF and other C0 control characters in the
   MSG field. A newline in a producer-controlled field (event_type)
   could otherwise split the line and forge an extra syslog record.

Adds a regression test for each.
@EulBite EulBite merged commit 0914c42 into main Jun 1, 2026
3 checks passed
@EulBite EulBite deleted the fix/edge-case-hardening branch June 1, 2026 01:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant