diff --git a/default/skills/production-readiness-audit/dimensions/security.md b/default/skills/production-readiness-audit/dimensions/security.md index 2d18db1a..6bb1e184 100644 --- a/default/skills/production-readiness-audit/dimensions/security.md +++ b/default/skills/production-readiness-audit/dimensions/security.md @@ -42,9 +42,10 @@ if err != nil { 1. (HARD GATE) All routes protected via Access Manager integration per security.md 2. (HARD GATE) lib-auth used for JWT validation (not custom JWT parsing) 3. Resource/action authorization granularity per Ring access control model -4. Token expiration enforcement -5. Tenant extraction from JWT claims -6. Auth bypass for health/ready endpoints only +4. RBAC resource names match public route collections when applicable (`/v1/payments` -> `"payments"`, not `"payment"`) +5. Token expiration enforcement +6. Tenant extraction from JWT claims +7. Auth bypass for health/ready endpoints only **Severity Ratings:** - CRITICAL: Unprotected data endpoints (HARD GATE violation per Ring standards) @@ -52,6 +53,7 @@ if err != nil { - CRITICAL: HARD GATE violation — not using lib-auth for access management - HIGH: Missing token expiration check - HIGH: Tenant claims not enforced +- MEDIUM: Plural collection route protected by singular RBAC resource - MEDIUM: Overly broad permissions - LOW: Missing fine-grained actions diff --git a/default/skills/production-readiness-audit/dimensions/structure.md b/default/skills/production-readiness-audit/dimensions/structure.md index 22be45fe..f6dc9335 100644 --- a/default/skills/production-readiness-audit/dimensions/structure.md +++ b/default/skills/production-readiness-audit/dimensions/structure.md @@ -275,9 +275,9 @@ func RegisterRoutes(protected func(resource, action string) fiber.Router, handle if handler == nil { return errors.New("handler is nil") } - protected("resource", "create").Post("/v1/resources", handler.Create) - protected("resource", "read").Get("/v1/resources", handler.List) - protected("resource", "read").Get("/v1/resources/:id", handler.Get) + protected("resources", "create").Post("/v1/resources", handler.Create) + protected("resources", "read").Get("/v1/resources", handler.List) + protected("resources", "read").Get("/v1/resources/:id", handler.Get) return nil } diff --git a/dev-team/docs/standards/golang/security.md b/dev-team/docs/standards/golang/security.md index d6e02d79..f350447b 100644 --- a/dev-team/docs/standards/golang/security.md +++ b/dev-team/docs/standards/golang/security.md @@ -171,6 +171,20 @@ auth.Authorize(applicationName, resource, action) | `resource` | string | Resource being accessed | `"ledgers"`, `"transactions"`, `"packages"` | | `action` | string | HTTP method (lowercase) | `"get"`, `"post"`, `"patch"`, `"delete"` | +### RBAC Resource Naming + +RBAC resource names **MUST** match the stable public route collection when the protected route belongs to a collection. + +| Route family | RBAC resource | Reason | +|--------------|---------------|--------| +| `/v1/boletos`, `/v1/boletos/:id` | `"boletos"` | Collection resource | +| `/v1/payments`, `/v1/payments/:id` | `"payments"` | Collection resource | +| `/v1/dashboards/...` | `"dashboards"` | Collection namespace | + +Use singular resource names only for true singleton or non-collection surfaces whose route namespace is also singular, for example `/v1/admin/...` -> `"admin"`. + +**Forbidden:** protecting plural collection routes with singular RBAC resources such as `auth.Authorize(applicationName, "payment", "post")` for `POST /v1/payments`. + ### Middleware Behavior | Scenario | HTTP Response | @@ -274,11 +288,14 @@ f.Post("/v1/sensitive-data", handler.Create) // Missing auth.Authorize // FORBIDDEN: Using wrong application name auth.Authorize("wrong-app-name", "resource", "post") // Must match identity registration +// FORBIDDEN: Singular RBAC resource for a plural route collection +auth.Authorize(applicationName, "package", "post") // /v1/packages requires "packages" + // FORBIDDEN: Direct calls to plugin-auth API http.Post("http://plugin-auth:4000/v1/authorize", ...) // Use lib-auth instead // CORRECT: Always use lib-auth for auth operations -auth.Authorize(applicationName, "resource", "post") +auth.Authorize(applicationName, "packages", "post") token, _ := auth.GetApplicationToken(ctx, clientID, clientSecret) ```