fix(security): env: credential references follow the per-instance rule (#849)

env:<PREFIX><INSTANCE>_... is the per-instance convention for every prefix of a
driver's scope (OTOPCUA_GALAXY_, GALAXY_MXGW_, OTOPCUA_OPCUACLIENT_,
OTOPCUA_HEIDENHAIN_), INSTANCE = a driver id or name upper-cased with every other
character as '_'. A variable whose part after the prefix equals or starts with
(at a '_' boundary) ANOTHER same-type instance's id or name is refused with
CredentialReferenceOtherInstance at save, deploy, Test Connect and browse - the
same DriverCredentialPolicy.DescribeOtherInstanceProblems the secret: rule uses
(now over CollectInstanceReferences / ReferenceNames). Another-instance matches
win over the owner's, as for secrets. Shared variables that name no instance
(GALAXY_MXGW_API_KEY) keep working. HasInstanceSegments now also gates on env:,
so an env:-only browse needs the ConfigDb (fail-closed without it).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-11-11 06:24:47 -05:00
parent d1b6cad299
commit 4a6229e4d2
7 changed files with 284 additions and 52 deletions
@@ -611,9 +611,9 @@ public sealed class AdminOperationsActor : ReceiveActor
var sw = System.Diagnostics.Stopwatch.StartNew();
try
{
// lmxopcua#835: a reference naming ANOTHER driver instance's secret (secret:galaxy/<other>/…) is refused
// lmxopcua#835/#849: a reference naming ANOTHER driver instance's secret (secret:galaxy/<other>/…, env:OTOPCUA_GALAXY_<OTHER>_…) is refused
// before the probe resolves it — off the mailbox, since it reads the ConfigDb (only when the config
// carries an in-scope secret: reference at all).
// carries an in-scope secret:/env: reference at all).
if (DriverCredentialPolicy.HasInstanceSegments(msg.DriverType, msg.ConfigJson))
{
await using var db = await _dbFactory.CreateDbContextAsync(cts.Token);