diff --git a/src/Backend/AutopilotMonitor.Functions.Tests/EndpointPolicyCatalogCompletenessTests.cs b/src/Backend/AutopilotMonitor.Functions.Tests/EndpointPolicyCatalogCompletenessTests.cs index 0f18913e..67f24aac 100644 --- a/src/Backend/AutopilotMonitor.Functions.Tests/EndpointPolicyCatalogCompletenessTests.cs +++ b/src/Backend/AutopilotMonitor.Functions.Tests/EndpointPolicyCatalogCompletenessTests.cs @@ -174,6 +174,23 @@ public class EndpointPolicyCatalogCompletenessTests Assert.Equal(EndpointPolicy.TenantAdminOrGA, postRules.Policy); } + /// + /// IME log pattern edits write GLOBAL patterns (all tenants) and the handler has no GA + /// re-gate, so the PUT must stay GlobalAdminOnly — a downgrade to TenantAdminOrGA would let + /// any customer tenant admin mutate platform-wide patterns via ?global=true. + /// + [Fact] + public void ImeLogPatternWrites_AreGlobalAdminOnly() + { + var putPattern = EndpointAccessPolicyCatalog.FindPolicy("PUT", "/api/rules/ime-log-patterns/IME-001"); + var reseed = EndpointAccessPolicyCatalog.FindPolicy("POST", "/api/rules/ime-log-patterns/reseed"); + + Assert.NotNull(putPattern); + Assert.NotNull(reseed); + Assert.Equal(EndpointPolicy.GlobalAdminOnly, putPattern.Policy); + Assert.Equal(EndpointPolicy.GlobalAdminOnly, reseed.Policy); + } + /// /// Unregistered routes must return null (fail-closed). /// diff --git a/src/Backend/AutopilotMonitor.Functions/Security/EndpointAccessPolicyCatalog.cs b/src/Backend/AutopilotMonitor.Functions/Security/EndpointAccessPolicyCatalog.cs index 2630e84f..fb507068 100644 --- a/src/Backend/AutopilotMonitor.Functions/Security/EndpointAccessPolicyCatalog.cs +++ b/src/Backend/AutopilotMonitor.Functions/Security/EndpointAccessPolicyCatalog.cs @@ -311,7 +311,6 @@ public static class EndpointAccessPolicyCatalog new("POST", "rules/analyze/{ruleId}/create-from-template", EndpointPolicy.TenantAdminOrGA), new("PUT", "rules/analyze/{ruleId}", EndpointPolicy.TenantAdminOrGA), new("DELETE", "rules/analyze/{ruleId}", EndpointPolicy.TenantAdminOrGA), - new("PUT", "rules/ime-log-patterns/{patternId}", EndpointPolicy.TenantAdminOrGA), new("POST", "sessions/{sessionId}/mark-failed", EndpointPolicy.TenantAdminOrGA), new("POST", "sessions/{sessionId}/mark-succeeded", EndpointPolicy.TenantAdminOrGA), // Operator tier so troubleshooting staff can queue request_diagnostics ("Collect Logs" in @@ -541,6 +540,9 @@ public static class EndpointAccessPolicyCatalog new("GET", "vulnerability/ignored-software", EndpointPolicy.GlobalReadOrAdmin), new("POST", "vulnerability/ignored-software", EndpointPolicy.GlobalAdminOnly), new("DELETE", "vulnerability/ignored-software", EndpointPolicy.GlobalAdminOnly), + // Both routes mutate GLOBAL patterns (all tenants); there is no tenant-scoped variant yet, + // so PUT must not sit on the TenantAdminOrGA tier — the handler has no GA re-gate. + new("PUT", "rules/ime-log-patterns/{patternId}", EndpointPolicy.GlobalAdminOnly), new("POST", "rules/ime-log-patterns/reseed", EndpointPolicy.GlobalAdminOnly), new("GET", "global/mcp-users", EndpointPolicy.GlobalReadOrAdmin), new("POST", "global/mcp-users", EndpointPolicy.GlobalAdminOnly),