fix(agent): reject non-UUID AgentUuid on override + persisted paths#207
Draft
ottobolyos wants to merge 2 commits into
Draft
fix(agent): reject non-UUID AgentUuid on override + persisted paths#207ottobolyos wants to merge 2 commits into
ottobolyos wants to merge 2 commits into
Conversation
ottobolyos
force-pushed
the
fix/agent-uuid-lifecycle-hardening
branch
from
July 22, 2026 11:14
09452c1 to
fadff28
Compare
ottobolyos
force-pushed
the
fix/agent-uuid-lifecycle-hardening
branch
from
July 22, 2026 11:17
fadff28 to
8562d9e
Compare
…n paths (RED) Adds AgentUuidValidationTests + updates the existing AgentUuidConfigOverrideTests / AgentUuidLongitudinalInvariantsTests fixtures to pin the contract that every source of the Agent meta-device UUID (operator-supplied override AND persisted agent.information.json state) MUST parse as an RFC 4122 UUID before it reaches the wire. New coverage in AgentUuidValidationTests: * DeterministicAgentUuid.TryValidate low-level rejection (null, empty, whitespace, unparseable) and acceptance / normalization (canonical D-form unchanged; braced B, parenthesized P, bare-hex N normalized to D). * Malformed operator override on a fresh boot falls through to derived. * Malformed operator override with a valid persisted UUID preserves the persisted UUID. * Malformed persisted state with no override falls through to derived (Path 2 hardening — the missing symmetric guard). * Valid non-canonical operator override is normalized to hyphenated. * Persisted UUID is adopted on the second boot when no override is set. * First boot persists the derived UUID to agent.information.json so subsequent boots hit Path 2. Fixture updates: * AgentUuidConfigOverrideTests + AgentUuidLongitudinalInvariantsTests now route their boot simulation through AgentUuidResolver.Resolve (the shared production helper) instead of inline replay, so the tests cannot silently drift from StartAgent semantics. * Fixture literals converted from non-UUID strings (fixture-stable-uuid-A, from-config-uuid, etc.) to canonical RFC 4122 UUIDs so production validation would accept them (previously these fixtures passed only because their inline replay omitted TryValidate — fake-green). * Every fixture marked [NonParallelizable] and each SetUp sweeps orphan .bak.* / .valbak.* / .longinv.bak.* files from a prior crashed test run so successive TearDowns cannot restore stale state. This commit is compile-error RED — the tests reference AgentUuidResolver.Resolve and the AmE parameter name normalized which land in the following fix commit. The compile error proves the API absence per CONVENTIONS §1.0d-vicies-semel's behavioral-RED default. Claude-Session: https://claude.ai/code/session_015FLSuNX8hZPFiTGtE8GYqH
Hardens the Agent meta-device UUID resolution slice added by TrakHound#168 so malformed input never reaches the wire, closing the gap on both the operator-supplied override AND the persisted agent.information.json paths — the previous single-path validation left Path 2 open. Change: * New AgentUuidResolver.Resolve (public static, MTConnect.NET-Common) implements the three-path algorithm — Path 1 validated override, Path 2 validated persisted state, Path 3 deterministic derivation — with a delegate-based warn hook so MTConnect.NET-Common takes no hard dependency on NLog. The delegate emits the raw AgentUuid value pre-sanitized (CRLF stripped, truncated to 64 chars) to guard against log-injection and paste-in-wrong-field secret leakage. * MTConnectAgentApplication.RunAgent routes through the shared resolver instead of hand-rolling the dual-if resolution. Collapses the two-branch shape that re-tested string.IsNullOrEmpty twice. * DeterministicAgentUuid.TryValidate parameter name normalised renamed to normalized per CONVENTIONS §1.0d-decies (AmE in committed code); XML-doc verbs (normalizes, parenthesized) updated to match. Positional callers and out-var callers are unaffected. The RED tests from the preceding commit turn GREEN because AgentUuidResolver.Resolve now exists and the AmE parameter name lands. The three test fixtures share the same resolver so drift between production and tests is impossible. Claude-Session: https://claude.ai/code/session_015FLSuNX8hZPFiTGtE8GYqH
ottobolyos
force-pushed
the
fix/agent-uuid-lifecycle-hardening
branch
from
July 23, 2026 18:09
8562d9e to
30869b3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hardens the Agent meta-device UUID resolution slice added by #168 so malformed input never reaches the wire — closing the gap on both the operator-supplied override AND the persisted
agent.information.jsonpaths. The prior single-path validation left Path 2 open: a corrupted state file (or a pre-hardening agent version that wrote non-UUID content) would flow straight to the wire and fail every downstream MTConnect Part 1 XSD-validating consumer on typed enum/decimal DataItems.Change
AgentUuidResolver.Resolve(public static,MTConnect.NET-Common) implements the three-path algorithm:ServiceName.Malformed input on Path 1 OR Path 2 logs a warning via a delegate hook (so
MTConnect.NET-Commontakes no hard dependency on NLog) and falls through to the next path. Raw values echoed to the warn log are pre-sanitized (CRLF stripped, truncated to 64 chars) as guards against log-injection and paste-in-wrong-field secret leakage.MTConnectAgentApplication.RunAgentroutes through the shared resolver instead of hand-rolling the dual-ifresolution. Collapses the two-branch shape that re-testedstring.IsNullOrEmptytwice.DeterministicAgentUuid.TryValidateparameter renamednormalised→normalizedper CONVENTIONS §1.0d-decies (AmE in committed code); XML-doc verbs updated to match. Positional /out varcallers unaffected.AgentUuidConfigOverrideTests+AgentUuidLongitudinalInvariantsTestsnow route their boot simulation throughAgentUuidResolver.Resolveinstead of inline replay, and their fixture literals are canonical RFC 4122 UUIDs (previously non-UUID strings that survived only because the inline replay omittedTryValidate— fake-green). Every fixture marked[NonParallelizable]with orphan-.bak.*sweep.Test plan
dotnet test tests/MTConnect.NET-Common-Tests/— allAgentUuid*Tests.*GREEN on the full CI matrix.AgentUuidValidationTestscovers:TryValidatelow-level (null/whitespace/unparseable/canonical/normalization), malformed-override fresh-boot fall-through to derived, malformed-override with valid-persisted preserves-persisted, malformed-persisted with no-override falls through to derived (Path 2 hardening), non-canonical valid override normalized, persisted-adopted-on-second-boot, first-boot persists derived to disk.AgentUuidConfigOverrideTests+AgentUuidLongitudinalInvariantsTestscontinue to pin the config-override wins-over-persisted contract but now via the same code path production uses.docs.yml) — inherits pre-chore(ci): unblock master (docs drift, Scriban CVE, nav-title test) #205 drift on this PR's isolated head; the fix lives in chore(ci): unblock master (docs drift, Scriban CVE, nav-title test) #205 and cascades in once chore(ci): unblock master (docs drift, Scriban CVE, nav-title test) #205 merges.integration/all-fixesunion atbfca1bd2unions chore(ci): unblock master (docs drift, Scriban CVE, nav-title test) #205 + this PR and passes.Commits (RED → GREEN):
test(common-tests): pin AgentUuid RFC 4122 contract on both resolution paths (RED)— compile-error RED per §1.0d-vicies-semel (tests referenceAgentUuidResolverwhich lands in commit 2).fix(agent): reject non-UUID AgentUuid on override AND persisted paths— introduces the resolver, wiresRunAgentthrough it, renamesnormalised→normalized.https://claude.ai/code/session_015FLSuNX8hZPFiTGtE8GYqH