Skip to content

VNX-GO-019: Go debug endpoints (pprof, expvar) served on all interfaces

Detects pprof and expvar debug handlers served on a listener bound to every interface: the default mux when net/http/pprof or expvar is imported, a mux or router carrying the debug handlers (including chi middleware.Profiler), or a gin engine with gin-contrib pprof. An all-interfaces address held in a constant of the same file counts as the literal. Loopback and flag- or config-supplied addresses, and public listeners without debug handlers, are not reported.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-GO-019
Severity Medium
SARIF level warning
Kind sast
Languages Go
CWE CWE-200
CAPEC CAPEC-1
ATT&CK T1046
CWSS vector CWSS:1.0/TI:M/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C
Tags network binding go pprof debug
Source internal/sast/rules/vnx-go-019.rego

Overview

This rule detects Go server bindings using net.Listen(), tls.Listen(), or http.ListenAndServe()/http.ListenAndServeTLS() with an address of 0.0.0.0:PORT, :PORT, or an empty host component. Binding to 0.0.0.0 causes the service to accept connections on every network interface attached to the host, including public-facing interfaces, cloud instance metadata interfaces, and container bridge networks. If the service handles sensitive data, exposes administrative functionality, or lacks authentication, this creates an unnecessarily broad attack surface. This maps to CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor).

Many services are intended to be accessed only from localhost (a metrics endpoint, a debug pprof handler, an internal management API) or only from a specific private network interface. Binding such services to 0.0.0.0 exposes them to any host that can route to the machine. In containerised and cloud environments, this is frequently broader than the developer intends.

Why This Matters

Go’s net/http/pprof debug handlers, Prometheus metrics endpoints, and internal health endpoints are routinely bound to 0.0.0.0 during development and accidentally left there in production. These endpoints can expose profiling data, goroutine stack traces, heap dumps, and application metrics that provide significant reconnaissance value to an attacker. CAPEC-1 (Accessing/Intercepting/Modifying HTTP Communication) and MITRE ATT&CK T1046 (Network Service Discovery) apply when an exposed service is discovered and enumerated.

In cloud environments, binding to 0.0.0.0 in a container may expose the service through the host’s external IP, a load balancer, or a NodePort depending on how networking is configured. A service that was intended to be internal-only becomes publicly accessible. Several real-world incidents, including exposed Kubernetes dashboard and etcd instances, have followed this pattern.

What Gets Flagged

The rule reports a listener on every interface only when it serves the profiling or introspection handlers. Binding an ordinary service to every interface is VNX-1327’s concern.

import _ "net/http/pprof" // registers /debug/pprof/ on the default mux

// FLAGGED: the default mux, and with it pprof, on every interface
log.Println(http.ListenAndServe(":6060", nil))

// FLAGGED: a mux or router carrying the debug handlers (including chi middleware.Profiler), bound publicly
mux.HandleFunc("/debug/pprof/", pprof.Index)
return http.ListenAndServe(":6060", mux)

// FLAGGED: gin-contrib/pprof on a public gin engine
pprof.Register(r)
r.Run(":8080")

// NOT FLAGGED: pprof on loopback, or a public mux without debug handlers
go http.ListenAndServe("localhost:6060", nil)
srv := &http.Server{Addr: ":8080", Handler: api}

Remediation

  1. Bind to 127.0.0.1 for services intended to be localhost-only, such as pprof, metrics, or internal debug endpoints.

    // SAFE: pprof debug server restricted to localhost
    go func() {
        if err := http.ListenAndServe("127.0.0.1:6060", nil); err != nil {
            log.Printf("pprof server error: %v", err)
        }
    }()
  2. Bind to a specific private interface address for services that must be reachable within a cluster but not from public networks.

    // SAFE: bind to a specific internal address
    ln, err := net.Listen("tcp", "10.0.0.1:8080")
    if err != nil {
        return fmt.Errorf("listen failed: %w", err)
    }
  3. Make the bind address configurable and default to localhost in the configuration. Require an explicit operator decision to expose the service more broadly.

    // SAFE: configurable bind address, safe default
    addr := cfg.BindAddr
    if addr == "" {
        addr = "127.0.0.1:8080"
    }
    srv := &http.Server{Addr: addr, Handler: mux}
    if err := srv.ListenAndServe(); err != nil {
        return err
    }
  4. For public-facing services that must bind to 0.0.0.0, ensure authentication and TLS are enforced at the application level and document the binding decision explicitly.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-go-019
// nosec vnx-go-019

Or record an ignore rule: vulnetix ignore add --rule vnx-go-019 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-go-019.rego, cli/internal/sast/docs/vnx-go-019.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.