fix(security): close auth & site-scoping gaps across 8 findings
Resolves the auth-theme batch from the 2026-05-28 baseline review (8 findings across Security/CentralUI/ManagementService/CLI). The most consequential gaps: NotificationReport + SiteCallsReport now route through SiteScopeService so a site-scoped Deployment user cannot see or act on other sites' rows (CUI-028); QueryAuditLogCommand is no longer "any authenticated user" — gated Admin-only to match /api/audit/query's strictness (MS-018); RoleMapper preserves the broader grant when a user is in both an unscoped and scoped Deployment LDAP group, instead of silently narrowing to the scoped set (Sec-016); and the dead SiteScopeRequirement/Handler are deleted so SiteScopeService is unambiguously the sole site-scoping mechanism (Sec-017). Pending findings: 172 → 164.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
| Last reviewed | 2026-05-28 |
|
||||
| Reviewer | claude-agent |
|
||||
| Commit reviewed | `1eb6e97` |
|
||||
| Open findings | 6 (1 Deferred — see ManagementService-012) |
|
||||
| Open findings | 4 (1 Deferred — see ManagementService-012) |
|
||||
|
||||
## Summary
|
||||
|
||||
@@ -796,7 +796,7 @@ Deployment user and an Admin user, in- and out-of-scope
|
||||
|--|--|
|
||||
| Severity | High |
|
||||
| Category | Security |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.ManagementService/ManagementActor.cs:153`–`:207`, `:336`, `:1302` |
|
||||
|
||||
**Description**
|
||||
@@ -838,13 +838,26 @@ Recommended: option 1 plus a deprecation comment on `QueryAuditLogCommand` point
|
||||
so the ManagementActor route is redundant. Add a regression test asserting that a
|
||||
no-role / `Deployment`-only caller gets `ManagementUnauthorized` for `QueryAuditLogCommand`.
|
||||
|
||||
**Resolution**
|
||||
|
||||
Resolved 2026-05-28 (commit pending) per recommendation option 1. `QueryAuditLogCommand`
|
||||
was added to the Admin-required group in `GetRequiredRole`, with an inline comment
|
||||
documenting the deliberate strictness vs. the keyset-paged `/api/audit/query`
|
||||
(`OperationalAuditRoles`) and pointing new audit consumers at the REST endpoint.
|
||||
The CentralUI `ConfigurationAuditLog` page reads via `ICentralUiRepository` directly
|
||||
(not through this command), so the gate tightening does not break any UI flow. Two
|
||||
regression tests pin the new behaviour:
|
||||
`QueryAuditLogCommand_WithNoRoles_ReturnsUnauthorized` and
|
||||
`QueryAuditLogCommand_WithDeploymentRole_ReturnsUnauthorized` — both fail on the
|
||||
pre-fix code (the command fell through to "any authenticated user") and pass after.
|
||||
|
||||
### ManagementService-019 — AuditEndpoints builds PermittedSiteIds but never enforces them
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Severity | Medium |
|
||||
| Category | Security |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.ManagementService/AuditEndpoints.cs:358`–`:368`, `:397`–`:437` |
|
||||
|
||||
**Description**
|
||||
@@ -887,6 +900,27 @@ Recommended: option 1, mirroring the `ManagementActor` pattern — same security
|
||||
across both surfaces. Add a regression test that a site-scoped `AuditReadOnly` user
|
||||
filtering on an out-of-scope site gets a 403 (or an empty page).
|
||||
|
||||
**Resolution**
|
||||
|
||||
Resolved 2026-05-28 (commit pending) per recommendation option 1. Added a public
|
||||
helper `AuditEndpoints.ApplySiteScope(AuditLogQueryFilter, AuthenticatedUser)` that
|
||||
returns the restricted filter (or `null` when the caller explicitly asks for an
|
||||
out-of-scope site). Three cases:
|
||||
- Empty `PermittedSiteIds` (Admin or any unscoped role) → filter returned unchanged.
|
||||
- Scoped user with empty caller filter → `SourceSiteIds` set to the permitted set.
|
||||
- Scoped user with explicit `SourceSiteIds` → intersected with the permitted set;
|
||||
empty intersection returns `null` so `HandleQuery` / `HandleExport` emit a 403
|
||||
rather than silently producing an empty page.
|
||||
|
||||
Both `HandleQuery` and `HandleExport` now call the helper after the role check and
|
||||
short-circuit to `Forbidden("OperationalAudit"|"AuditExport")` on a `null` result.
|
||||
Audit roles remain non-site-scoped by design (the design doc unchanged), but the
|
||||
helper honours scope rules if an operator attaches them via the LDAP-mapping UI,
|
||||
matching the existing `ManagementActor` pattern. Regression tests added in
|
||||
`AuditEndpointsTests.ApplySiteScope_*` (5 tests): system-wide unchanged,
|
||||
empty-caller-filter restricted, in-scope kept verbatim, out-of-scope returns null,
|
||||
mixed-set intersected.
|
||||
|
||||
### ManagementService-020 — UpdateSmtpConfig returns and audits the SMTP Credentials field verbatim
|
||||
|
||||
| | |
|
||||
|
||||
Reference in New Issue
Block a user