sillsdev / sillsdev/harmony

ServerCommit no longer enforces required properties on JSON deserialization (regression rc.241 → rc.266)

Open
#104 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
C#
Stars
14
Forks
4
Avg merge
3d 21h
Merged PRs (30d)
3

Description

Summary

Between SIL.Harmony.Core 0.2.1-rc.241 and 0.2.1-rc.266, ServerCommit stopped enforcing its required init properties during System.Text.Json deserialization. A JSON payload that omits clientId (and even hybridDateTime) now deserializes successfully, silently defaulting ClientId to Guid.Empty, where it previously threw a JsonException.

ClientId identifies the originating client of a CRDT commit, so accepting Guid.Empty is a data-integrity concern for any endpoint that deserializes commits from clients.

Impact

Downstream, lexbox's CRDT sync endpoint POST /api/crdt/{projectId}/add deserializes ServerCommit[] straight from the request body. With rc.266 a malformed or outdated client can upload commits with a missing/empty clientId and the server will accept them instead of rejecting the request.

Root cause

ClientId is unchanged — still public required Guid ClientId { get; init; } (non-nullable, RequiredMemberAttribute present) in both versions. The only relevant IL difference is that rc.266 adds a parameterless constructor to ServerCommit:

rc.241  ServerCommit ctors: [JsonConstructor] (Guid id, HybridDateTime hybridDateTime); (Guid id)
rc.266  ServerCommit ctors: [JsonConstructor] (Guid id, HybridDateTime hybridDateTime); (Guid id); ()   <-- new parameterless ctor

With a parameterless constructor available, STJ constructs the object and sets init properties afterward, and it stops reporting/enforcing the required init property ClientId. This shows up both in JsonSchemaExporter output and in actual deserialization.

Reproduction

Run STJ's schema exporter and a deserialization against each package version's SIL.Harmony.Core.dll:

JsonSchemaExporter.GetJsonSchemaAsNode(opts, typeof(ServerCommit))["required"]

  • rc.241 → ["Id","HybridDateTime","ClientId"]
  • rc.266 → ["Id","HybridDateTime"]

Deserializing a payload with clientId omitted:

{"id":"11111111-1111-1111-1111-111111111111","hybridDateTime":{...},"changeEntities":[]}
  • rc.241 → throws JsonException: ... missing required properties including: 'ClientId'
  • rc.266 → succeeds, ClientId == Guid.Empty

(Note: rc.266 also no longer enforces HybridDateTime, yet the exported schema still lists it as required — the schema and the enforcement have diverged.)

Expected

Deserializing a ServerCommit that omits a required property (e.g. clientId) should fail, as it did in rc.241. If the parameterless constructor is needed (e.g. for EF/materialization), it shouldn't come at the cost of required-property enforcement during JSON deserialization.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ServerCommit in the SIL.Harmony.Core.dll versions and reproduce the JsonSchemaExporter output and deserialization behavior for a payload missing clientId. Compare the constructor paths used by System.Text.Json, including the new parameterless constructor in rc.266. Done means omitted required properties again cause JsonException during deserialization without breaking any required materialization scenario.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.