fix(commons): resolve Commons-013,014 — integral JSON index handling, distinguish Malformed vs Legacy OPC UA config

This commit is contained in:
Joseph Doherty
2026-05-17 03:18:17 -04:00
parent 21856a4be7
commit a78c3bcb6f
5 changed files with 190 additions and 18 deletions

View File

@@ -8,7 +8,7 @@
| Last reviewed | 2026-05-17 |
| Reviewer | claude-agent |
| Commit reviewed | `39d737e` |
| Open findings | 2 |
| Open findings | 0 |
## Summary
@@ -587,7 +587,7 @@ each pinned under `de-DE`).
|--|--|
| Severity | Low |
| Category | Correctness & logic bugs |
| Status | Open |
| Status | Resolved |
| Location | `src/ScadaLink.Commons/Types/DynamicJsonElement.cs:40-54` |
**Description**
@@ -611,7 +611,18 @@ single integer before the bounds check. Add a regression test indexing with a `l
**Resolution**
_Unresolved._
Resolved 2026-05-17 — confirmed the `int`-only guard, and during fixing found the
underlying cause was wider than the finding text: `Wrap`'s `TryGetInt64 ? long : double`
ternary unified both branches to `double`, so integral JSON numbers were never actually
boxed as `long``obj.count` always surfaced as `double`. Fixed at the root: `Wrap`
delegates to a new `WrapNumber` helper that boxes integral numbers as `long` and
non-integral as `double` with separate typed returns; `TryGetIndex` now accepts any
integral index via `TryGetIntegralIndex` (`int`/`long`/`short`/`byte`/`sbyte`/`ushort`/
`uint`/`ulong`, plus whole-valued `double`/`decimal`) normalized to `long` before the
bounds check. Internal-only change — no public-API or message-contract change. Regression
tests added in `DynamicJsonElementTests` (`IndexAccess_WithLongIndex_Works`,
`IndexAccess_WithIndexDerivedFromWrappedJsonNumber_Works`,
`IndexAccess_WithWideningIntegralIndex_Works`, `IndexAccess_WithLongIndexOutOfRange_Throws`).
### Commons-014 — `OpcUaEndpointConfigSerializer.Deserialize` can mislabel a corrupt typed row as `Legacy`
@@ -619,7 +630,7 @@ _Unresolved._
|--|--|
| Severity | Low |
| Category | Error handling & resilience |
| Status | Open |
| Status | Resolved |
| Location | `src/ScadaLink.Commons/Serialization/OpcUaEndpointConfigSerializer.cs:107-131` |
**Description**
@@ -651,4 +662,15 @@ field.
**Resolution**
_Unresolved._
Resolved 2026-05-17 — confirmed the swallow-and-fall-through against the source. Split
`Deserialize` into a shape-detection phase and a materialization phase: it first parses
the document to decide whether the row is the current typed shape (root object carrying
`endpointUrl`); only a non-typed-shape row falls through to `LoadLegacy`. A row that *is*
the typed shape but fails `JsonSerializer.Deserialize` (invalid enum, wrong-typed field)
is now classified `Malformed` instead of being mislabelled `Legacy` with the offending
field silently dropped. The `OpcUaConfigParseResult` shape and `(Config, IsLegacy)`
deconstruction are unchanged, so existing callers are unaffected. XML docs updated to
describe the corrupt-typed-row branch. Regression tests added in
`OpcUaEndpointConfigSerializerTests` (`Deserialize_TypedShapeWithInvalidEnum_ReportsMalformedNotLegacy`,
`Deserialize_TypedShapeWithWrongTypeField_ReportsMalformedNotLegacy`,
`Deserialize_ValidTypedRow_StillReportsTyped`).