Skip to content

Reject malformed saved state without losing the live history - #87

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/undoredo-81-validate-loaded-state
Sep 26, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
claude/undoredo-81-validate-loaded-state

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #81

What was wrong

  • LoadStateAsync let NullReferenceException and ArgumentNullException escape on JSON that parses but has missing or null fields. Its contract is to return false for data it cannot load.
  • RestoreFromState cleared the stack and save boundaries before validating the state. A failed load could wipe the user's in-memory history.

Change

  • JsonUndoRedoSerializer.DeserializeAsync:
    • Rejects a null formatVersion as unsupported.
    • New ValidateShape throws InvalidOperationException, which LoadStateAsync already catches, for any of: null commands, null saveBoundaries, a null entry in either list, or a command with no metadata.
  • UndoRedoService.RestoreFromState checks all of the following before clearing anything, and returns false with the current history untouched if any check fails:
    • Commands and SaveBoundaries are non-null and contain no null entries.
    • CurrentPosition is in [-1, Commands.Count - 1].
  • No public API change.

Tests

  • UndoRedoService_LoadStateMalformed_ReturnsFalseAndKeepsHistory has six data rows: the four payloads from the issue, plus a null save-boundary entry and a null format version. Each row asserts false and that commands, position and boundaries are unchanged.
  • UndoRedoService_RestoreFromStateInvalidPosition_ReturnsFalseAndKeepsHistory covers positions 2 and -2.
  • UndoRedoService_RestoreFromStateNullSaveBoundaries_ReturnsFalseAndKeepsHistory calls RestoreFromState directly with a null boundaries list.

Verification

  • Full suite on this branch: 66/66 pass.
  • With the fix reverted, all 9 new cases fail.

🤖 Generated with Claude Code

https://claude.ai/code/session_016ToeUpj3nH61YEnatKdb8d


Generated by Claude Code

…tch]

LoadStateAsync let NullReferenceException and ArgumentNullException
escape on valid JSON with missing or null fields, and RestoreFromState
cleared the stack before validating, so a bad file could wipe the
in-memory history. The JSON serializer now validates the shape and
throws InvalidOperationException, and RestoreFromState checks commands,
save boundaries and position before clearing anything.

Fixes #81

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ToeUpj3nH61YEnatKdb8d
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit aadd1e0 into main Sep 26, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the claude/undoredo-81-validate-loaded-state branch September 26, 2026 09:54
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.

LoadStateAsync throws NullReferenceException/ArgumentNullException on malformed state, and can wipe the live history before throwing

2 participants