fix(security): count only this page's references in the browse-filter summary

On a BrowseNext the references list already carries the earlier pages, so the
summary's denominator counted them again. Log accuracy only — the filtering
itself was already scoped to this call's appended range.

Claude-Session: https://claude.ai/code/session_015p7wGqy3YpZNCpDzTpGMKo
This commit is contained in:
Joseph Doherty
2026-08-01 09:39:51 -04:00
parent 00aa486677
commit ce404df970
2 changed files with 16 additions and 1 deletions
+12
View File
@@ -522,6 +522,12 @@ services:
ConfigSource__CentralFetchEndpoints__0: "http://central-1:4055"
ConfigSource__CentralFetchEndpoints__1: "http://central-2:4055"
ConfigSource__ApiKey: "configserve-docker-dev-key"
# #520 stage 6 — node ACL enforcement, the dark switch. Default OFF everywhere, exactly as in
# production; the site-a pair is the demo/live-gate subject, so it reads an env override:
# OTOPCUA_ACL_ENABLED=true docker compose -f docker-dev/docker-compose.yml up -d site-a-1 site-a-2
# With it on, EVERY data-plane operation needs a matching NodeAcl grant — a principal with no
# grant connects and sees nothing. site-b stays hardcoded OFF as the A/B control.
Security__Acl__Enabled: "${OTOPCUA_ACL_ENABLED:-false}"
# Per-cluster mesh Phase 5/6 (gRPC live-telemetry stream) — see central-1/central-2. This
# node carries cluster-SITE-A + driver, so SplitTopologyTransportValidator requires
# Telemetry:Mode=Grpc (mandatory, not a dark switch, as of Phase 6). The dedicated :4056
@@ -631,6 +637,12 @@ services:
ConfigSource__CentralFetchEndpoints__0: "http://central-1:4055"
ConfigSource__CentralFetchEndpoints__1: "http://central-2:4055"
ConfigSource__ApiKey: "configserve-docker-dev-key"
# #520 stage 6 — node ACL enforcement, the dark switch. Default OFF everywhere, exactly as in
# production; the site-a pair is the demo/live-gate subject, so it reads an env override:
# OTOPCUA_ACL_ENABLED=true docker compose -f docker-dev/docker-compose.yml up -d site-a-1 site-a-2
# With it on, EVERY data-plane operation needs a matching NodeAcl grant — a principal with no
# grant connects and sees nothing. site-b stays hardcoded OFF as the A/B control.
Security__Acl__Enabled: "${OTOPCUA_ACL_ENABLED:-false}"
# Per-cluster mesh Phase 5/6 (gRPC live-telemetry stream) — see central-1/site-a-1.
# Telemetry:Mode=Grpc is mandatory as of Phase 6 (cluster-SITE-A + driver); the dedicated
# :4056 listener binds regardless.
@@ -2754,8 +2754,11 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
if (source is not null && !Permits(identity, OpcUaOperation.Browse, source))
throw new ServiceResultException(StatusCodes.BadUserAccessDenied);
// Only the references THIS call appends are ours to filter — on a BrowseNext the list already
// carries the earlier pages, and re-examining them would both waste work and skew the summary.
var before = references.Count;
base.Browse(context, ref continuationPoint, references);
var returned = references.Count - before;
var filtered = 0;
for (var ii = references.Count - 1; ii >= before; ii--)
@@ -2773,7 +2776,7 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
filtered++;
}
LogAclFiltering("Browse", identity, filtered, references.Count + filtered);
LogAclFiltering("Browse", identity, filtered, returned);
}
/// <inheritdoc />