Files
scadalink-design/code-reviews/README.md
Joseph Doherty 3b3760f026 docs(code-reviews): re-review batch 3 at 39d737e — Host, InboundAPI, ManagementService, NotificationService, Security
21 new findings: Host-012..015, InboundAPI-014..017, ManagementService-014..017, NotificationService-014..018, Security-012..015.
2026-05-17 00:48:25 -04:00

12 KiB
Raw Blame History

Code Reviews

Comprehensive, per-module code reviews of the ScadaLink codebase. Each module (one buildable project under src/) has its own folder containing a findings.md. This README is the aggregated index — the single place to see all outstanding work.

Generated by regen-readme.py from the per-module findings.md files. Do not edit by hand — edit the findings files and re-run the script.

How it works

  • Reviews are performed one module at a time against a fixed checklist.
  • Every finding is recorded in the module's findings.md with a severity and status.
  • Findings are never deleted — they are closed by changing their status, keeping a full audit trail.
  • This README aggregates every pending finding (Open / In Progress) across all modules.

See REVIEW-PROCESS.md for the full procedure: the review checklist, severity definitions, finding format, and how to mark items resolved.

Layout

code-reviews/
├── README.md            # this file — process overview + pending findings
├── REVIEW-PROCESS.md     # how to perform a review and track findings
├── regen-readme.py       # regenerates this README from the findings files
├── _template/findings.md # copy-this template for a module review
└── <Module>/findings.md  # one folder per src/ project

Baseline review — 2026-05-16

All 19 modules were reviewed at commit 9c60592 (241 findings: 6 Critical, 46 High, 100 Medium, 89 Low). The tables below track what remains open as findings are resolved and re-triaged; findings discovered after the baseline are appended to their module file and counted in Total.

Severity Open findings
Critical 0
High 8
Medium 20
Low 27
Total 55

Module Status

Module Last reviewed Commit Open (C/H/M/L) Open Total
CLI 2026-05-16 9c60592 0/0/1/2 3 16
CentralUI 2026-05-16 9c60592 0/1/2/3 6 25
ClusterInfrastructure 2026-05-16 9c60592 0/0/1/1 2 10
Commons 2026-05-16 9c60592 0/0/0/2 2 14
Communication 2026-05-16 9c60592 0/1/1/2 4 15
ConfigurationDatabase 2026-05-16 9c60592 0/0/2/1 3 14
DataConnectionLayer 2026-05-16 9c60592 0/1/2/1 4 17
DeploymentManager 2026-05-16 9c60592 0/1/1/1 3 17
ExternalSystemGateway 2026-05-16 9c60592 0/1/1/1 3 17
HealthMonitoring 2026-05-16 9c60592 0/0/1/3 4 16
Host 2026-05-16 9c60592 0/0/1/3 4 15
InboundAPI 2026-05-16 9c60592 0/0/3/1 4 17
ManagementService 2026-05-16 9c60592 0/1/1/2 4 17
NotificationService 2026-05-16 9c60592 0/2/1/2 5 18
Security 2026-05-16 9c60592 0/0/2/2 4 15
SiteEventLogging 2026-05-16 9c60592 0/0/0/0 0 11
SiteRuntime 2026-05-16 9c60592 0/0/0/0 0 16
StoreAndForward 2026-05-16 9c60592 0/0/0/0 0 14
TemplateEngine 2026-05-16 9c60592 0/0/0/0 0 14

Pending Findings

Every Open / In Progress finding across all modules, highest severity first. Resolved findings drop off this list but remain recorded in their module's findings.md (see REVIEW-PROCESS.md §4§5). Full detail — description, location, recommendation — lives in the module's findings.md.

Critical (0)

None open.

High (8)

ID Module Title
CentralUI-020 CentralUI Idle-session redirect never fires: SessionExpiry polls a frozen auth-state snapshot
Communication-012 Communication gRPC client factory ignores the endpoint on a cache hit, breaking NodeA→NodeB stream failover
DataConnectionLayer-014 DataConnectionLayer DCL-012 security warning is never logged in production: RealOpcUaClient is created without a logger
DeploymentManager-015 DeploymentManager Site-query reconciliation marks a deployment Success but skips instance-state and snapshot updates
ExternalSystemGateway-015 ExternalSystemGateway MaxRetries == 0 is buffered as "retry forever", contradicting the ExternalSystemGateway-004 "never retry" claim
ManagementService-014 ManagementService HandleQueryDeployments bypasses site-scope enforcement
NotificationService-014 NotificationService OAuth2 token-fetch failure escapes DeliverBufferedAsync; a permanently-broken config is retried forever
NotificationService-015 NotificationService Unclassified exceptions (OAuth2 token fetch, non-cancellation OCE) escape SendAsync to the calling script

Medium (20)

ID Module Title
CLI-014 CLI update commands require "core" fields, making partial updates impossible
CentralUI-021 CentralUI DebugView stream callback mutates Dictionary off the render thread
CentralUI-022 CentralUI Deployments push handler fires InvokeAsync with no disposal guard
ClusterInfrastructure-009 ClusterInfrastructure DownIfAlone is an inert configuration knob — never consumed by the HOCON builder
Communication-013 Communication Site gRPC address changes are never applied; RemoveSiteAsync has no production caller
ConfigurationDatabase-012 ConfigurationDatabase Inbound-API ApiKey.KeyValue bearer credential stored in plaintext
ConfigurationDatabase-013 ConfigurationDatabase Secret-column encryption silently falls back to an ephemeral (throwaway) key
DataConnectionLayer-015 DataConnectionLayer Initial-connect failures never trigger failover; an unreachable primary at startup never tries the backup
DataConnectionLayer-016 DataConnectionLayer HandleSubscribeCompleted reports SubscribeTagsResponse success even on a connection-level subscribe failure
DeploymentManager-016 DeploymentManager Reconciled prior record keeps its stale RevisionHash
ExternalSystemGateway-016 ExternalSystemGateway ConfigureHttpClientDefaults applies the ESG connection cap to every HttpClient in the host process
HealthMonitoring-015 HealthMonitoring Heartbeat-registered site is left with a year-0001 LastReportReceivedAt
Host-012 Host down-if-alone hard-coded in HOCON; ClusterOptions.DownIfAlone is never read
InboundAPI-014 InboundAPI ReturnDefinition is loaded but never used; script return value is unshaped/unvalidated
InboundAPI-015 InboundAPI ForbiddenApiChecker is purely textual and is bypassable via reflection reachable without a forbidden namespace token
InboundAPI-016 InboundAPI Routed Route.To().Call() invocations are not bound by the method timeout
ManagementService-015 ManagementService HandleSetInstanceOverrides applies overrides non-atomically
NotificationService-016 NotificationService AuthenticateAsync silently sends unauthenticated for an unknown auth type or empty credentials
Security-012 Security Partial LDAP failure during login yields a roleless authenticated session
Security-014 Security RefreshToken re-issues a token without checking the idle timeout

Low (27)

ID Module Title
CLI-015 CLI Component-CLI.md command surface has drifted again in two places
CLI-016 CLI WriteAsTable derives columns from the first array element only
CentralUI-023 CentralUI Residual bare catch {} blocks swallow JS interop errors
CentralUI-024 CentralUI Claim lookups use magic strings instead of JwtTokenService constants
CentralUI-025 CentralUI SessionExpiry polling/redirect path has no test coverage
ClusterInfrastructure-010 ClusterInfrastructure Validator does not enforce DownIfAlone = true despite the design doc requiring it
Commons-013 Commons DynamicJsonElement.TryGetIndex rejects non-int index values
Commons-014 Commons OpcUaEndpointConfigSerializer.Deserialize can mislabel a corrupt typed row as Legacy
Communication-014 Communication Untrusted gRPC correlation_id flows directly into an Akka actor name
Communication-015 Communication No test exercises the real gRPC client factory across a node flip
ConfigurationDatabase-014 ConfigurationDatabase Redundant, inconsistent cast on one HasConversion call
DataConnectionLayer-017 DataConnectionLayer WriteBatchAsync aborts the whole batch on a mid-batch disconnect
DeploymentManager-017 DeploymentManager GetDeploymentStatusAsync XML doc describes behaviour it does not implement
ExternalSystemGateway-017 ExternalSystemGateway BuildUrl appends a bare trailing ? when a GET method's parameters are all null
HealthMonitoring-013 HealthMonitoring Offline-check interval comment claims "shorter timeout" but only ever uses OfflineTimeout
HealthMonitoring-014 HealthMonitoring HealthMonitoringOptions intervals are unvalidated; a zero/negative value crashes the hosted service
HealthMonitoring-016 HealthMonitoring SiteHealthCollector.CollectReport reads DateTimeOffset.UtcNow directly instead of an injected TimeProvider
Host-013 Host :F0 rounding of cluster timing values silently degrades sub-second configuration
Host-014 Host Serilog sinks are hard-coded in Program.cs, not configuration-driven (REQ-HOST-8)
Host-015 Host StartupRetry retries on every exception type, including permanent failures
InboundAPI-017 InboundAPI RouteHelper / RouteTarget has no test coverage
ManagementService-016 ManagementService Unexpected exception messages returned verbatim to HTTP callers
ManagementService-017 ManagementService QueryDeploymentsCommand has no test coverage
NotificationService-017 NotificationService NotificationOptions is bound from configuration but never read (dead config)
NotificationService-018 NotificationService Concurrency limiter: lock-free read of a non-volatile field, never resized on redeployment, never disposed
Security-013 Security ExtractFirstRdnValue mis-parses group DNs containing escaped commas
Security-015 Security Username is not trimmed before use in the LDAP filter, fallback DN, and JWT claims