fix(management-service): resolve ManagementService-001/002/003 — enforce site scope on query/snapshot handlers and DebugStreamHub
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
| Last reviewed | 2026-05-16 |
|
||||
| Reviewer | claude-agent |
|
||||
| Commit reviewed | `9c60592` |
|
||||
| Open findings | 13 |
|
||||
| Open findings | 10 |
|
||||
|
||||
## Summary
|
||||
|
||||
@@ -51,7 +51,7 @@ authorization bypass with no workaround.
|
||||
|--|--|
|
||||
| Severity | High |
|
||||
| Category | Security |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.ManagementService/ManagementActor.cs:1465`, `:1481`, `:1493`, `:641`, `:649` |
|
||||
|
||||
**Description**
|
||||
@@ -76,7 +76,16 @@ is already loaded — call `EnforceSiteScope(user, instance.SiteId)` (which requ
|
||||
|
||||
**Resolution**
|
||||
|
||||
_Unresolved._
|
||||
Resolved 2026-05-16 (commit `<pending>`). Threaded `AuthenticatedUser` into
|
||||
`HandleQueryEventLogs`, `HandleQueryParkedMessages`, `HandleRetryParkedMessage`,
|
||||
`HandleDiscardParkedMessage`, and `HandleDebugSnapshot`; added an
|
||||
`EnforceSiteScopeForIdentifier` helper that resolves the site by identifier and applies
|
||||
`EnforceSiteScope`. `HandleDebugSnapshot` enforces against the already-loaded instance's
|
||||
`SiteId`. Regression tests: `QueryEventLogs_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`,
|
||||
`QueryParkedMessages_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`,
|
||||
`RetryParkedMessage_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`,
|
||||
`DiscardParkedMessage_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`,
|
||||
`DebugSnapshot_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`.
|
||||
|
||||
### ManagementService-002 — Single-entity query handlers leak data across site scope
|
||||
|
||||
@@ -84,7 +93,7 @@ _Unresolved._
|
||||
|--|--|
|
||||
| Severity | High |
|
||||
| Category | Security |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.ManagementService/ManagementActor.cs:510`, `:673`, `:733`, `:774`, `:631`, `:624` |
|
||||
|
||||
**Description**
|
||||
@@ -106,7 +115,16 @@ the resolved site ID in `HandleGetSite`, `HandleListAreas`, and `HandleGetDataCo
|
||||
|
||||
**Resolution**
|
||||
|
||||
_Unresolved._
|
||||
Resolved 2026-05-16 (commit `<pending>`). `HandleGetInstance`, `HandleGetSite`,
|
||||
`HandleGetDataConnection` now take `AuthenticatedUser` and call `EnforceSiteScope` against
|
||||
the resolved entity's site ID (instance `SiteId`, site `Id`, data-connection `SiteId`);
|
||||
`HandleListAreas` enforces against the requested `SiteId` before querying. Regression tests:
|
||||
`GetInstance_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`,
|
||||
`GetInstance_InScopeForSiteScopedUser_ReturnsSuccess`,
|
||||
`GetSite_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`,
|
||||
`GetSite_OutOfScopeForAdminUser_ReturnsSuccess`,
|
||||
`ListAreas_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`,
|
||||
`GetDataConnection_OutOfScopeForSiteScopedUser_ReturnsUnauthorized`.
|
||||
|
||||
### ManagementService-003 — DebugStreamHub.SubscribeInstance performs no per-instance authorization
|
||||
|
||||
@@ -114,7 +132,7 @@ _Unresolved._
|
||||
|--|--|
|
||||
| Severity | High |
|
||||
| Category | Security |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.ManagementService/DebugStreamHub.cs:104` |
|
||||
|
||||
**Description**
|
||||
@@ -135,7 +153,17 @@ established in `OnConnectedAsync` must be persisted on the connection (e.g. in
|
||||
|
||||
**Resolution**
|
||||
|
||||
_Unresolved._
|
||||
Resolved 2026-05-16 (commit `<pending>`). `OnConnectedAsync` now persists the resolved
|
||||
roles and `PermittedSiteIds` in `Context.Items`. `SubscribeInstance` resolves the
|
||||
instance's site via `ITemplateEngineRepository` and rejects the subscription (sending
|
||||
`OnStreamTerminated`) when the new pure `DebugStreamHub.IsInstanceAccessAllowed` check
|
||||
fails. The check grants access for the Admin role or system-wide Deployment (empty
|
||||
permitted set) and otherwise requires the instance's site in the permitted set. Regression
|
||||
tests: `IsInstanceAccessAllowed_SiteScopedUser_OutOfScopeInstance_Denied`,
|
||||
`IsInstanceAccessAllowed_SiteScopedUser_InScopeInstance_Allowed`,
|
||||
`IsInstanceAccessAllowed_SystemWideDeployment_AnySiteAllowed`,
|
||||
`IsInstanceAccessAllowed_AdminRole_BypassesSiteScope`,
|
||||
`IsInstanceAccessAllowed_AdminRoleCheck_IsCaseInsensitive`.
|
||||
|
||||
### ManagementService-004 — Actor offloads work to Task.Run instead of using PipeTo
|
||||
|
||||
|
||||
Reference in New Issue
Block a user