Resolve all high-severity architecture review findings

This commit is contained in:
Joseph Doherty
2026-09-09 16:13:58 -04:00
parent 8bfe65512e
commit 146a13238f
192 changed files with 3160 additions and 753 deletions
+79 -4
View File
@@ -1,6 +1,6 @@
# ScadaBridge — architectural / code review (whole system)
**Date:** 2026-09-09 · **Status:** SECOND-PASS REVIEW REPORT (documentation only; no production code changed)
**Date:** 2026-09-09 · **Status:** ALL 13 HIGH FINDINGS COMPLETED (see §9)
**Reviewed revision:** `main` @ `c078bc8f`. Its only difference from the first review's
`d8229916` is the original findings document; production source/configuration is identical.
**Scope:** all 26 `src/` projects, 32 test/support projects, both Docker rig configurations,
@@ -13,7 +13,8 @@ rechecked all High source paths, ran build/test/format/package checks, and execu
probes. Each area revalidated its existing IDs and searched for additional defects. Source-traced
mechanisms, runtime probes, latent risks, and unproven operational impact are distinguished below.
This is a focused whole-system review, not a claim that every line or production topology was
exercised. No rig rebuild, device writes, failover drill, or remote QA/production inspection ran.
exercised. The review phase ran no rig rebuild, device writes, failover drill, or remote QA/production inspection.
Sections 1–8 preserve that reviewed baseline; §9 records the subsequent implementation and local rig verification.
**Second-pass changes:** 11 new findings; UI-3 withdrawn as unsupported; XC-6 merged into CM-5;
UI-4/6/7 reclassified as Low hardening gaps. Existing IDs are preserved for traceability. The
@@ -36,9 +37,11 @@ references; every mapped gRPC service is behind a fail-closed constant-time inte
contract changes since August are additive; all eight migrations are sound and the model snapshot
matches. The test suite is broad; fresh execution evidence and its limits are in §7.
The updated register has **92 unique active findings: 13 High, 33 Medium, 46 Low**.
The reviewed baseline contained **92 unique findings: 13 High, 33 Medium, 46 Low**.
**Remediation status: all 13 High findings completed; 79 findings remain open (33 Medium, 46 Low).**
Completion evidence and limits are recorded in §9.
This includes low-priority test/documentation gaps and latent risks, not just reproduced failures.
The High-priority findings are:
The reviewed High-priority findings were:
| # | Sev | Finding | Area |
|---|---|---|---|
@@ -821,3 +824,75 @@ A `pass` is limited to the cited check, not a blanket claim that the component i
Add these to the repository's chosen CI system with existing build/test gates; keep formatting
cleanup and framework/package migration in separate maintenance work so they do not obscure the
correctness/security remediation.
## 9. High-severity remediation completion
**Completed 2026-09-09. All 13 High findings are resolved and verified.** Implementation
started from `8bfe6551` on `codex/resolve-high-review-findings`, with parallel device/runtime,
transport, and UI/security agents plus primary-agent integration and independent reviews.
The findings above preserve the reviewed baseline; this section supersedes their open status.
Medium/Low findings remain outside this remediation unless a change was required for a High fix.
| Finding | Status | Implementation / verification |
|---|---|---|
| DC-1 | **Completed** | Owner-checked PostStop unsubscribe and DeathWatch release dead demand. Shared/replacement demand survives old-owner termination and in-flight completion. Deterministic lifecycle/race regressions pass. |
| DC-2 | **Completed** | Initial subscribe and resolution exclude each other’s in-flight tags; equal live handles are never released. Mx reverse-map removal is atomic and identity-checked. Adapter and controlled-race regressions pass. |
| DC-3 | **Completed** | Connecting/reconnecting reject single and batch writes immediately; subscription retries are deduplicated by owner/source. No stale setpoint replay after reconnect. Full DCL suite: 365 passed. |
| DC-12 | **Completed** | OPC snapshot start/entries/end retain source and epoch; NativeAlarmActor ignores unrelated/duplicate ends and drops partial snapshots on disconnect. Legacy Mx framing remains supported. Actor tests prove standing-alarm preservation; live two-boiler feed verifies distinct wire scopes and epochs. |
| CM-1 | **Completed** | Servers and all six application channels use explicit 32 MiB send/receive limits. Protobuf-exact 2 MiB audit budgets cover pending/cached drain and reconciliation paging. Both 256×64 KiB lanes drain without loss; real gRPC 17 MiB response passes. Oversize-row policy is explicit below. |
| CM-9 | **Completed** | Authenticated site identity guards RPC payloads, audit rows and notification status. SQL upsert rejects another site’s stored operation ID, including duplicate-insert retry. Real gRPC denial, outbox ownership and 29 SQL Server lifecycle/ownership tests pass. |
| CM-10 | **Completed** | Reads/idempotent ingests may retry; mutations retry only after explicit server pre-dispatch rejection. Ambiguous primary/secondary response loss returns `OUTCOME_UNKNOWN` without replay. Real transport and effect-count tests pass. |
| UI-1 | **Completed** | All 16 configuration repository-bypass pages use typed commands. One transaction spans entity saves and final scrubbed audit flush, including generated IDs; cache publication follows commit. Direct instance/area paths retain domain checks within the same boundary. LDAP CRUD and audit-failure rollback regressions pass; deployed browser CRUD passes. |
| UI-2 | **Completed** | Passive ping/Blazor validation neither slides cookies nor advances activity. Trusted browser input posts with antiforgery; per-login activity reaches existing circuits. Stable NameIdentifier preserves antiforgery across role refresh. Real HTTP fake-clock tests and deployed trusted/synthetic-input browser gate pass. |
| UI-13 | **Completed** | Circuit authority remaps stored groups in fresh scopes; mutations require current roles and persisted-site scope. API keys, reports, failover, parked messages, instances and Topology recheck before dispatch. Revoked-scope and authorized-positive tests pass, including nonnumeric site identifiers. Explicit server-configured auto-login retains synthetic grants under its existing startup guard. |
| SR-1 | **Completed** | Attribute get/set/wait/batch continuations use `ConfigureAwait(false)`. Saturated two-worker synchronous read/write regressions pass without watchdog assistance; wait/trace regression coverage remains green. |
| SR-11 | **Completed** | Sync/async transaction and command connection/transaction readback retain auditing wrappers. SQLite regressions prove persisted writes and exactly one audit through each former escape. |
| MG-1 | **Completed** | Trust gate denies Unsafe, RuntimeHelpers and Runtime.Loader, including alias/static imports, while preserving async and CallerMemberName. ScriptAnalysis suite: 81 passed. |
### Final verification
| Gate | Result / evidence |
|---|---|
| Final solution build | `dotnet build ZB.MOM.WW.ScadaBridge.slnx --no-restore`: **0 warnings, 0 errors**. `/tmp/high-final-build3.log` |
| Selected full solution | `dotnet test ZB.MOM.WW.ScadaBridge.slnx --no-build --no-restore --filter 'FullyQualifiedName!~PlaywrightTests' --logger trx --results-directory /tmp/scadabridge-high-final-green`: **9,310 passed, 0 failed, 0 skipped**; exit 0. `/tmp/high-final-tests2.log` |
| Final UI follow-up | After the final auto-login compatibility fix: **1,392 passed, 0 failed, 0 skipped** in the full CentralUI suite. This reruns the 1,391 UI cases included above plus the new regression; counts are not additive. `/tmp/high-ui-final.log` |
| Deployed browser | LDAP create/edit/delete round trip and trusted-input/passive-input/antiforgery/throttle scenario: **2 passed, 0 failed, 0 skipped**. `/tmp/high-browser-final.log` |
| Independent reviews | Runtime/trust, transport/ownership, and UI transaction/authorization reviews passed after corrections; final auto-login guard follow-up also passed. `/tmp/scadabridge-ui-independent-review.md` |
| Local rig | Final image `sha256:b9d6b60ab77093ee537470992fb891f78cc9888170d290021855c902c0115eb6` on **all eight nodes**. All ready (200), one active per pair (200) and four standbys (503), two members per pair, zero unreachable/restarts, all five central singletons reachable. Site-A replication connected with backlog 0. `/tmp/scadabridge-final-loginfix-postdeploy-health.json` |
| Change hygiene | `git diff --check` clean. Canonical original-repository config/log mounts and named data volumes preserved; env2 remains untouched. Component requirements updated with implementation. |
The local image was built with `bash docker/build.sh` from the implementation worktree, then
all eight nodes were recreated using the canonical original repository’s Compose file. This
keeps existing configuration, logs and data in place rather than rebinding them to the worktree.
No device writes, failover drill, remote deployment, commit or push was performed by this remediation.
### Regression evidence and limits
- SR-1/SR-11 reproduced **five failures before** their fixes; the runtime follow-up passed 39
targeted tests. MG-1 reproduced five forbidden-call failures while two benign cases passed;
all 81 ScriptAnalysis cases pass afterward. Full SiteRuntime: **774 passed**.
- CM-1’s 17 MiB response failed at the previous default limit. CM-9’s stored-owner SQL regression
failed before its guard. Full Communication **756**, ConfigurationDatabase **411**, AuditLog
**396**, Host **590**, and integration **117** tests pass. Two historical Host tests expected
blind mutation retry on `Unavailable`; they now model explicit pre-dispatch rejection, with
a separate real-transport test proving ambiguous failure never dispatches to the peer.
- Device tests control owner death, replacement, shared-tag demand, subscribe/resolve races,
disconnected writes and interleaved snapshots. A stale failover test assertion was corrected:
backup demand must be restored while an original subscribe remains pending. The full DCL
suite subsequently passed. The local two-boiler snapshots were empty; standing-condition
preservation is proven by deterministic actor tests, **not by live raised alarms**.
- UI revocation and Topology denied-mutation tests reproduced failures before correction.
The first LDAP baseline test stopped at an incorrect seeded-row count, so it does **not**
establish a before-fix missing-audit reproduction. Corrected real-SQLite CRUD/audit/rollback
tests pass. A real HTTP regression found antiforgery failure after role refresh; stable user
identity fixed it. The deployed browser found the synthetic auto-login role-loss regression;
the explicit trusted-configuration guard passed its red/green test and repeat live CRUD gate.
- A single audit row above 2 MiB travels alone up to the 32 MiB wire ceiling. A larger row remains
durable/unacknowledged with an explicit operator-action error, without truncation or silent
loss. Stored LDAP groups are remapped live; directory group-membership changes still require
login. Pre-upgrade cookies without stable NameIdentifier may require a fresh sign-in.
- No separate formatter cleanup or framework/package migration was included. The original
Medium/Low register and baseline formatting debt remain. The local rig has an existing
`zztest-conn-5d93ea81` test connection retrying unreachable `localhost:4840`; healthy cluster
readiness does not assert that every configured device endpoint is reachable.