Skip to content

HTTP metrics middleware records raw r.URL.Path as Prometheus label, breaking exposition on non-UTF-8 paths #3460

Description

@statoon54

Describe the bug
The HTTP Metrics middleware (pkg/gofr/http/middleware/metrics.go) records r.URL.Path verbatim as the value of the Prometheus path label on the app_http_response histogram,
with no UTF-8 validation. Any request whose URL path contains non-UTF-8 bytes (vulnerability scanners like Nessus routinely send IIS Unicode path-traversal probes such as
\xc0.\xc0./winnt/win.ini) poisons the OTel registry. Subsequent scrapes of /metrics then fail with:

An error has occurred while serving metrics:

error collecting metric Desc{fqName: "app_http_response", help: "Response time of HTTP requests in seconds.", constLabels: {}, variableLabels: {method,path,status,otel_scope_name,otel_scope_version,otel_scope_schema_url}}: label value "/\xc0.\xc0./winnt/win.ini" is not valid UTF-8

Per the Prometheus exposition format spec, label values must be valid UTF-8, so this is a violation of contract — not just a cosmetic issue. Once a single bad series enters the
registry, the whole scrape can be reported as failing.

To Reproduce

  1. The code is

Minimal app:

app := gofr.New()
app.GET("/healthz", func(*gofr.Context) (any, error) { return "ok", nil })
app.Run()

Send a non-UTF-8 path and scrape:

curl -i "http://localhost:8000/$(printf '\xc0\x2e\xc0\x2e')/winnt/win.ini"
# → 404 (expected — no such route)

curl -s http://localhost:2121/metrics
# → "An error has occurred while serving metrics: ... label value ...
#    is not valid UTF-8"
  1. The error is
  error collecting metric Desc{fqName: "app_http_response", help: "Response time of HTTP requests in seconds.", constLabels: {}, variableLabels: {method,path,status,otel_scope_name,otel_scope_version,otel_scope_schema_url}}: label value "/\xc0.\xc0./winnt/win.ini" is not valid UTF-8

Expected behavior
app_http_response's path label should always be valid UTF-8
(Prometheus contract) and should have bounded cardinality (best
practice, no DoS surface from a scanner).

Environments (please complete the following information):

  • gofr: reproduced on v1.56.X
  • Go: 1.26
  • Any OS exposing the gofr HTTP port to a network reachable by an HTTP
    scanner (Linux production setups especially)

Proposed fix

A single validation after all assignment branches.

import "unicode/utf8"

// ... after L39 ("if path == "/" || ...") ...
if !utf8.ValidString(path) {
    path = "/<invalid_utf8>"
}

path = strings.TrimSuffix(path, "/")  // existing L41

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageThe issue needs triaging.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions