mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-27 05:57:27 +00:00
0180b33431
The /debug/pprof handlers are gated on RequireAdmin plus a settings:read scope that session logins skip. The public demo signs every visitor in as the configured admin, so any visitor could fetch goroutine and heap dumps and the process command line, or start CPU profiles and execution traces that load the whole process. DEMO_MODE already hides diagnostics, logs, admin users and manual discovery through the admin-operations policy; pprof was missing from it. The policy now has the demo guard answer /debug/pprof and every path below it with 404 for every method, ahead of route authentication. Instances outside demo mode keep the routes, and PULSE_PPROF_DISABLED still removes them everywhere. TestContract_DemoModeHidesPprofFromDemoAdminSession signs in as the demo admin through the full router, shows the same session still reads /debug/pprof/cmdline below the demo guard, and gets 404 for eight pprof paths through the served handler. On the previous policy GET and HEAD returned pprof output (200, or the root's 301 into the index) and POST got the generic demo 403. A policy test also fails if a literal route the router files register under /debug/, with or without a method prefix, escapes the policy for any common method.
84 lines
2.5 KiB
Go
84 lines
2.5 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
var publicDemoAdminOperationsPolicies = []publicDemoCommercialRoutePolicy{
|
|
{
|
|
route: "GET /api/diagnostics",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: readDemoCommercialPath("/api/diagnostics"),
|
|
},
|
|
{
|
|
route: "POST /api/diagnostics/docker/prepare-token",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: exactDemoCommercialMethodPath(http.MethodPost, "/api/diagnostics/docker/prepare-token"),
|
|
},
|
|
{
|
|
route: "GET /api/logs/stream",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: readDemoCommercialPath("/api/logs/stream"),
|
|
},
|
|
{
|
|
route: "GET /api/logs/download",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: readDemoCommercialPath("/api/logs/download"),
|
|
},
|
|
{
|
|
route: "GET /api/logs/level",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: readDemoCommercialPath("/api/logs/level"),
|
|
},
|
|
{
|
|
route: "POST /api/logs/level",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: exactDemoCommercialMethodPath(http.MethodPost, "/api/logs/level"),
|
|
},
|
|
{
|
|
route: "GET /api/admin/users",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: readDemoCommercialPath("/api/admin/users"),
|
|
},
|
|
{
|
|
route: "GET /api/discover",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: readDemoCommercialPath("/api/discover"),
|
|
},
|
|
// Go runtime profiling. These routes are gated only on admin auth, which
|
|
// a demo login that is the configured admin passes, so every visitor could
|
|
// otherwise pull goroutine and heap dumps or the command line, or start
|
|
// CPU profiles and traces that cost the whole process. Hidden for every
|
|
// method, so a write probe such as the POST symbol lookup gets the same
|
|
// 404 instead of revealing the route.
|
|
{
|
|
route: "/debug/pprof",
|
|
exposure: publicDemoCommercialExposureHidden,
|
|
matches: familyDemoCommercialPath("/debug/pprof"),
|
|
},
|
|
}
|
|
|
|
func publicDemoAdminOperationsPolicyForRequest(
|
|
r *http.Request,
|
|
) (publicDemoCommercialExposure, bool) {
|
|
for _, policy := range publicDemoAdminOperationsPolicies {
|
|
if policy.matches != nil && policy.matches(r) {
|
|
return policy.exposure, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
// familyDemoCommercialPath matches root and every path below it, for any
|
|
// method.
|
|
func familyDemoCommercialPath(root string) func(*http.Request) bool {
|
|
return func(r *http.Request) bool {
|
|
if r == nil || r.URL == nil {
|
|
return false
|
|
}
|
|
path := normalizeDemoCommercialPath(r.URL.Path)
|
|
return path == root || strings.HasPrefix(path, root+"/")
|
|
}
|
|
}
|