package daemon import ( "errors" "fmt" "log/slog" "net" "net/http" "net/http/pprof" "strings" "time" ) // isLoopback reports whether the address binds to a loopback interface // (127.0.0.1, ::1, localhost). REQ-123: pprof must be loopback-only. func isLoopback(addr string) bool { host, _, err := net.SplitHostPort(addr) if err != nil { host = addr } host = strings.TrimSpace(host) if host == "" || host == "localhost" { return true } ip := net.ParseIP(host) if ip != nil { return ip.IsLoopback() } return false } func StartPprof(addr string, log *slog.Logger) (*http.Server, error) { if addr == "" { return nil, nil } // REQ-123: pprof must bind to loopback only. Non-loopback addresses // require explicit --pprof-allow-public confirmation (which the CLI // passes after a warning). We refuse non-loopback here by default. if !isLoopback(addr) { log.Error("pprof refuses non-loopback bind", slog.String("addr", addr), slog.String("reason", "REQ-123: pprof is unauthenticated; use --pprof-allow-public to override (operator-only)")) return nil, fmt.Errorf("pprof: refusing non-loopback bind %s (REQ-123; unauthenticated; use --pprof-allow-public)", addr) } mux := http.NewServeMux() mux.HandleFunc("/debug/pprof/", pprof.Index) mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) mux.HandleFunc("/debug/pprof/profile", pprof.Profile) mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) mux.HandleFunc("/debug/pprof/trace", pprof.Trace) mux.Handle("/debug/pprof/heap", pprof.Handler("heap")) mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine")) mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate")) mux.Handle("/debug/pprof/block", pprof.Handler("block")) mux.Handle("/debug/pprof/mutex", pprof.Handler("mutex")) server := &http.Server{ Addr: addr, Handler: mux, ReadHeaderTimeout: 5 * time.Second, } log.Warn("pprof endpoint exposed", slog.String("addr", addr), slog.String("warning", "unauthenticated, operator-only — do not expose publicly")) go func() { err := server.ListenAndServe() if err != nil && !errors.Is(err, http.ErrServerClosed) { log.Error("pprof server stopped", slog.String("addr", addr), slog.Any("err", err)) } }() return server, nil }