Skip to content

1.3.0

Choose a tag to compare

@maltzsama maltzsama released this 07 Jul 19:28
ea57801

Robustness, Safety, and Error Handling

πŸ›‘οΈ Major Robustness Improvements

This release makes SwiftMsgpack production-ready with comprehensive error handling and protection against malformed inputs.

πŸ”’ Security & Safety

  • Added max nesting depth guard (512 levels) to prevent stack overflow attacks from deeply nested payloads
  • Added corrupt flag to MsgPackScanner for early detection of malformed inputs
  • Bounds checking for all reads β€” no more out-of-bounds access on truncated data
  • Integer overflow guards β€” values that don't fit the target type now throw instead of truncating
  • UTF-8 validation β€” invalid UTF-8 strings now throw DecodingError instead of returning garbage

πŸ› Critical Fixes

  • Nil into non-optional β€” no longer crashes (now throws DecodingError)
  • Empty data β€” now throws instead of crashing
  • Data slices β€” now decode correctly without requiring a copy
  • Truncated payloads β€” fixed-width, variable-length, and container headers all validate remaining bytes
  • Huge claimed lengths (4GB+) β€” fail fast instead of exhausting memory
  • Timestamp validation β€” negative nanoseconds and values β‰₯ 1e9 now throw

⚑ Performance

  • Reserve capacity in encoder to reduce reallocations
  • Container header size pre-calculation for more efficient writes
  • loadUnaligned for safer and faster timestamp reading

πŸ§ͺ Testing

Added comprehensive RobustnessTests suite covering:

  • Truncated inputs
  • Huge claimed lengths
  • Deep nesting (200k levels)
  • Nil into non-optional
  • Invalid UTF-8
  • Integer overflow
  • Cross-format numeric interop
  • Timestamp edge cases
  • Data slice decoding

⚠️ Breaking Change

Decoding nil into non-optional types now throws DecodingError instead of returning nil. This aligns with Swift's standard JSONDecoder behavior and prevents silent data corruption.

// BEFORE: returned nil (crash on force unwrap)
let value = try decoder.decode(Int.self, from: Data([0xC0])) // πŸ’₯

// AFTER: throws DecodingError
let value = try decoder.decode(Int.self, from: Data([0xC0])) // βœ… throws