From ff9d617a1bd61c7d00c9f3ff42530d6a40b71491 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 18 Aug 2026 09:59:03 -0400 Subject: [PATCH] =?UTF-8?q?docs(acl):=20correct=20the=20Browse=20override'?= =?UTF-8?q?s=20'two=20namespace=20root=20folders'=20remark=20=E2=80=94=20o?= =?UTF-8?q?ne=20shared=20anchor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The XML remark above OtOpcUaNodeManager.Browse still carried the exact claim the security.md correction in 22ae1836 removed: there is ONE shared root folder (V3NodeIds.SharedRootFolderId, raw namespace) fronting both realm trees, not one per namespace. Rewritten to name the anchor, the ns=0 listing behaviour, and the zero-segment realm-root mapping that now admits transit holders. Also completes the BuildScope collision remark (code-review Minor): the degenerate raw folder literally named OtOpcUa loses the accidental grant leak onto the anchor's Read/Write that the old one-segment scope allowed — a narrowing, fail-closed side effect, now recorded. Comment-only change; addresses the code review's sole Important + Minor. --- .../OtOpcUaNodeManager.cs | 10 +++++++--- .../Security/DataPlaneAuthorizer.cs | 5 ++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs index 9a050d3f..b12a03bc 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs @@ -2919,9 +2919,13 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2 /// that grant, because navigation requires them. /// /// - /// ⚠️ The two namespace root folders are referenced from the ns=0 Objects folder, whose - /// browse is served by the SDK's own node manager and is not gated here. A session with no - /// grants at all therefore still sees that the two roots exist, and is refused on entry. + /// ⚠️ There is ONE shared root folder (V3NodeIds.SharedRootFolderId, minted in the raw + /// namespace by ) fronting BOTH realm trees — not one root per + /// namespace. It is referenced from the ns=0 Objects folder, whose browse is served by + /// the SDK's own node manager and is not gated here, so a session with no grants at all still + /// sees that the root exists — and is refused on entry. A session holding any Browse grant in + /// either realm enters it via the zero-segment realm-root scope + /// Security.DataPlaneAuthorizer.BuildScope maps the anchor to. /// /// public override void Browse( diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Security/DataPlaneAuthorizer.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Security/DataPlaneAuthorizer.cs index 7206a90e..3f9a64a1 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Security/DataPlaneAuthorizer.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Security/DataPlaneAuthorizer.cs @@ -369,7 +369,10 @@ public sealed class DataPlaneAuthorizer /// literally named OtOpcUa would already mint the identical NodeId as the anchor in the /// served address space itself (EnsureFolder takes the RawPath as the id), so the /// collision — if an operator ever authors one — predates this mapping and lives in the address - /// space rather than in the gate. + /// space rather than in the gate. One narrowing side effect in that degenerate case: a grant + /// scoped to the colliding folder used to leak onto the anchor's own Read/Write (the old + /// one-segment scope descended into it); the zero-segment scope no longer does, which is the + /// fail-closed direction. /// /// /// The node's non-empty s= identifier.