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:
@@ -281,6 +281,55 @@ public class AuditQueryCommandTests
|
||||
Assert.NotEqual(0, exit);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RunQuery_Http403_ReturnsExitCode2()
|
||||
{
|
||||
// CLI-018: an authorization failure on /api/audit/query (HTTP 403) must
|
||||
// produce exit code 2 per the documented CLI exit-code contract — the
|
||||
// legacy bare-1 return masked auth failures as generic command failures.
|
||||
var handler = new StatusHandler(HttpStatusCode.Forbidden, "{\"error\":\"nope\",\"code\":\"UNAUTHORIZED\"}");
|
||||
var client = new ManagementHttpClient(new HttpClient(handler), "http://localhost:9001", "u", "p");
|
||||
var output = new StringWriter();
|
||||
|
||||
var exit = await AuditQueryHelpers.RunQueryAsync(
|
||||
client, new AuditQueryArgs(), fetchAll: false,
|
||||
new JsonLinesAuditFormatter(), output, DateTimeOffset.UtcNow);
|
||||
|
||||
Assert.Equal(2, exit);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RunQuery_UnauthorizedCodeOnNon403_ReturnsExitCode2()
|
||||
{
|
||||
// The server may signal authorization failure via the error code on a
|
||||
// non-403 status (e.g. 400 + code=UNAUTHORIZED). Honour both channels.
|
||||
var handler = new StatusHandler(HttpStatusCode.BadRequest, "{\"error\":\"nope\",\"code\":\"UNAUTHORIZED\"}");
|
||||
var client = new ManagementHttpClient(new HttpClient(handler), "http://localhost:9001", "u", "p");
|
||||
var output = new StringWriter();
|
||||
|
||||
var exit = await AuditQueryHelpers.RunQueryAsync(
|
||||
client, new AuditQueryArgs(), fetchAll: false,
|
||||
new JsonLinesAuditFormatter(), output, DateTimeOffset.UtcNow);
|
||||
|
||||
Assert.Equal(2, exit);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RunQuery_GenericServerError_ReturnsExitCode1()
|
||||
{
|
||||
// Authentication / internal errors (non-403, no auth code) must remain
|
||||
// exit code 1 — exit 2 is reserved for authorization failures.
|
||||
var handler = new StatusHandler(HttpStatusCode.InternalServerError, "{\"error\":\"boom\",\"code\":\"INTERNAL\"}");
|
||||
var client = new ManagementHttpClient(new HttpClient(handler), "http://localhost:9001", "u", "p");
|
||||
var output = new StringWriter();
|
||||
|
||||
var exit = await AuditQueryHelpers.RunQueryAsync(
|
||||
client, new AuditQueryArgs(), fetchAll: false,
|
||||
new JsonLinesAuditFormatter(), output, DateTimeOffset.UtcNow);
|
||||
|
||||
Assert.Equal(1, exit);
|
||||
}
|
||||
|
||||
private sealed class ErrorHandler : HttpMessageHandler
|
||||
{
|
||||
protected override Task<HttpResponseMessage> SendAsync(
|
||||
@@ -291,6 +340,16 @@ public class AuditQueryCommandTests
|
||||
});
|
||||
}
|
||||
|
||||
private sealed class StatusHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly HttpStatusCode _status;
|
||||
private readonly string _body;
|
||||
public StatusHandler(HttpStatusCode status, string body) { _status = status; _body = body; }
|
||||
protected override Task<HttpResponseMessage> SendAsync(
|
||||
HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
=> Task.FromResult(new HttpResponseMessage(_status) { Content = new StringContent(_body) });
|
||||
}
|
||||
|
||||
// ---- CLI parsing -------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user