fix: route audit misclassified inlined AuthMiddleware — server refused boot
The compiler inlines middleware.AuthMiddleware across packages, renaming
its closure to "<caller>.AuthMiddleware.func1" — the package-qualified
match ("middleware.AuthMiddleware") never hit, every agent-JWT route
was flagged as naked, and AuditAndExit refused boot (122 container
restarts overnight). Classify on bare names, web before agent since
WebAuthMiddleware contains AuthMiddleware. Regression test now uses the
real production middleware instead of a same-package fake.
This commit is contained in:
parent
e4afe1f605
commit
7269d25823
2 changed files with 38 additions and 6 deletions
|
|
@ -77,19 +77,29 @@ func NewAuditor() *Auditor {
|
|||
// fully-qualified function name (obtained via runtime.FuncForPC). Returns an
|
||||
// empty string if the handler is not a recognised auth middleware.
|
||||
//
|
||||
// The match is on the bare function name, NOT the package-qualified form:
|
||||
// when the compiler inlines a closure-returning constructor across packages,
|
||||
// the closure is renamed after its caller (e.g. middleware.AuthMiddleware's
|
||||
// closure becomes "main.main.AuthMiddleware.func1") and the package qualifier
|
||||
// disappears. Matching "middleware.AuthMiddleware" misclassified every agent
|
||||
// route and refused boot — 122 container restarts before this was caught.
|
||||
//
|
||||
// Order matters: "WebAuthMiddleware" contains "AuthMiddleware", so the web
|
||||
// check runs first.
|
||||
//
|
||||
// Classification rules:
|
||||
// - middleware.AuthMiddleware.* -> agent JWT boundary
|
||||
// - WebAuthMiddleware.* -> web JWT boundary
|
||||
// - MetricsBearerAuth.* -> metrics token boundary
|
||||
// - *WebAuthMiddleware* -> web JWT boundary
|
||||
// - *AuthMiddleware* -> agent JWT boundary
|
||||
// - *MetricsBearerAuth* -> metrics token boundary
|
||||
func classifyHandler(h gin.HandlerFunc) string {
|
||||
name := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
|
||||
|
||||
if strings.Contains(name, "middleware.AuthMiddleware") {
|
||||
return "agent"
|
||||
}
|
||||
if strings.Contains(name, "WebAuthMiddleware") {
|
||||
return "web"
|
||||
}
|
||||
if strings.Contains(name, "AuthMiddleware") {
|
||||
return "agent"
|
||||
}
|
||||
if strings.Contains(name, "MetricsBearerAuth") {
|
||||
return "metrics"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package routeaudit
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Fimeg/RedFlag/server/internal/api/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
|
@ -57,6 +58,27 @@ func TestValidate_PublicAllowlist(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestValidate_RealAgentAuthMiddleware uses the actual production middleware,
|
||||
// not a test fake. Regression for the inlining bug: the compiler inlines
|
||||
// middleware.AuthMiddleware into its caller, renaming the closure to
|
||||
// "<caller>.AuthMiddleware.func1" — the package-qualified match missed it,
|
||||
// flagged every agent route, and the server refused boot (122 restarts).
|
||||
func TestValidate_RealAgentAuthMiddleware(t *testing.T) {
|
||||
auditor := NewAuditor()
|
||||
router := gin.New()
|
||||
|
||||
agents := router.Group("/api/v1/agents")
|
||||
agents.Use(middleware.AuthMiddleware())
|
||||
agents.GET("/:id/commands", func(c *gin.Context) {})
|
||||
|
||||
router.GET("/api/v1/downloads/updates/:package_id", middleware.AuthMiddleware(), func(c *gin.Context) {})
|
||||
|
||||
violations := auditor.Validate(router)
|
||||
if len(violations) != 0 {
|
||||
t.Fatalf("real middleware.AuthMiddleware misclassified — violations: %v", violations)
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidate_ParamRoutes verifies the radix walk reconstructs full paths
|
||||
// through wildcard (param) nodes — the live router is full of :id segments.
|
||||
func TestValidate_ParamRoutes(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue