Files
lmxopcua/code-reviews/Driver.Historian.Wonderware.Client.Contracts/findings.md
T
Joseph Doherty 78542ab2d2 review(Driver.Historian.Wonderware.Client.Contracts): redact SharedSecret in ToString (High)
First review at 7286d320. -001 (High): record ToString() leaked SharedSecret into logs ->
override ToString() that omits it. -003 (Medium): [Range(1,65535)] on Port. -004 thumbprint
doc. -002 (SHA-1 pin -> SHA-256, lives in FrameChannel) deferred cross-module.
2026-06-19 12:22:53 -04:00

6.5 KiB
Raw Blame History

Code Review — Driver.Historian.Wonderware.Client.Contracts

Field Value
Module src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Contracts
Reviewer Claude Code
Review date 2026-06-19
Commit reviewed a19b0f86
Status Reviewed
Open findings 0

Checklist coverage

A comprehensive review completes every category, recording "No issues found" where a category produced nothing rather than leaving it blank.

# Category Result
1 Correctness & logic bugs Driver.Historian.Wonderware.Client.Contracts-003
2 OtOpcUa conventions No issues found
3 Concurrency & thread safety No issues found
4 Error handling & resilience No issues found
5 Security Driver.Historian.Wonderware.Client.Contracts-001, Driver.Historian.Wonderware.Client.Contracts-002
6 Performance & resource management No issues found
7 Design-document adherence No issues found
8 Code organization & conventions No issues found
9 Testing coverage No test project — per review instructions; verified by build only
10 Documentation & comments Driver.Historian.Wonderware.Client.Contracts-004

Findings

Driver.Historian.Wonderware.Client.Contracts-001

Field Value
Severity High
Category Security
Location WonderwareHistorianClientOptions.cs:24-30
Status Resolved

Description: WonderwareHistorianClientOptions is a sealed record. The C# compiler auto-generates a ToString() for records that includes every positional constructor parameter by name and value — in this case including SharedSecret. If the options object is ever passed to structured logging (e.g. _logger.LogDebug("{opts}", options)), printed in an exception message, surfaced in a health-check endpoint, or serialized by a framework that calls ToString(), the shared secret will appear in plain text in log files or HTTP responses. There is currently no ToString() override to redact it. The FrameChannel.ConnectInternalAsync log line in the sibling project does not log _options directly, so there is no active leak today, but the type is a latent trap for any future logging addition.

Recommendation: Override ToString() to redact the secret, including only the non-sensitive connection fields (Host, Port, PeerName, UseTls, ServerCertThumbprint).

Resolution: Resolved 2026-06-19 — added override ToString() to the record that outputs Host, Port, PeerName, UseTls, and ServerCertThumbprint only, omitting SharedSecret; verified by build (no test project for this module). dotnet build on both the Contracts project and the consumer Client project passes with 0 errors/warnings.


Driver.Historian.Wonderware.Client.Contracts-002

Field Value
Severity Medium
Category Security
Location WonderwareHistorianClientOptions.cs:50-54
Status Deferred

Description: ServerCertThumbprint is documented as a "SHA-1 thumbprint". The consumer (FrameChannel.DefaultTcpConnectFactory, sibling project) pins via the no-arg GetCertHashString() overload, which returns a 40-character SHA-1 hex string. SHA-1 is a deprecated hash algorithm for certificate identification. An operator following current tooling (Windows Certificate Manager, certutil -sha256, openssl x509 -fingerprint -sha256) would supply a 64-character SHA-256 thumbprint, which will silently never match and cause every TLS handshake to fail with a generic auth error and no indication that the algorithm mismatch is the cause.

Recommendation: Upgrade the consumer to cert.GetCertHashString(HashAlgorithmName.SHA256) and update the doc to require a 64-char SHA-256 thumbprint. This requires a coordinated change to FrameChannel.DefaultTcpConnectFactory in the Client project (not a wire-contract change). Until then the doc has been updated (finding 004) to explicitly warn operators that only 40-char SHA-1 thumbprints are accepted.

Resolution: Deferred — the fix requires editing FrameChannel.DefaultTcpConnectFactory in the sibling Client project, outside this Contracts module's scope. The documentation has been updated (finding 004 fix) to warn operators of the SHA-1-only constraint. Track as a follow-up in the Client module's review.


Driver.Historian.Wonderware.Client.Contracts-003

Field Value
Severity Medium
Category Correctness & logic bugs
Location WonderwareHistorianClientOptions.cs:26
Status Resolved

Description: The Port positional constructor parameter has no [Range] validation attribute (unlike ProbeTimeoutSeconds, which carries [Range(1, 60)]). A misconfigured zero, negative, or out-of-range port (valid TCP range 165535) passes construction silently. The consumer then fails at TcpClient.ConnectAsync time with a SocketException whose error gives no hint that the port value is invalid. The sibling ServerHistorianOptions.Validate() warns on Port <= 0 at startup, but that fires only if the read-path historian is wired; write-path-only or probe-only usage has no guard.

Recommendation: Add [Range(1, 65535)] on the Port positional parameter.

Resolution: Resolved 2026-06-19 — added [Range(1, 65535)] to the Port positional parameter; also updated the <param name="Port"> XML doc to state the valid range. Build passes with 0 errors/warnings on both the Contracts and consumer Client projects.


Driver.Historian.Wonderware.Client.Contracts-004

Field Value
Severity Low
Category Documentation & comments
Location WonderwareHistorianClientOptions.cs:50-54
Status Resolved

Description: The ServerCertThumbprint XML doc said "SHA-1 thumbprint (hex, no spaces)" but did not state the expected length (40 hex characters for SHA-1). An operator supplying a SHA-256 thumbprint (64 hex chars — the format shown by modern tooling) would get a silent TLS handshake failure. The doc also did not explain which hash algorithm the consumer uses or why SHA-256 thumbprints will not work.

Recommendation: Update the XML doc to state the 40-character SHA-1 constraint explicitly and warn that SHA-256 thumbprints will silently fail.

Resolution: Resolved 2026-06-19 — updated the ServerCertThumbprint XML doc to state "40 hex characters, no spaces, case-insensitive" and added a <remarks> block explaining that the consumer uses GetCertHashString() (SHA-1) and that a 64-char SHA-256 thumbprint will silently never match. Build passes with 0 errors/warnings.