The /metrics method label is still unbounded: 300 requests with random method tokens mint 7,525 permanent series #261
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found by the independent review of #258, measured against that branch's build — that is, AFTER the
handlerlabel was fixed.methodis recorded asr.Methodverbatim, andnet/httpaccepts any RFC 9110 token as a method. 300 requests to/using random 12-character method tokens took the scrape from 106 series to 7,631 — roughly 25 permanent series per distinct token, never evicted.Unauthenticated, and
/carries no rate limiter, so this is the same remote memory-exhaustion vector #254 was filed to close, reached through a different label dimension.#254's definition of done was scoped to the
handlerlabel and is fully met by #258; that scoping was mine and it was too narrow. This issue closes the rest.Definition of done
methodis collapsed to the set chi actually routes, with anything else recorded as a fixed sentinel — mirroring the(unmatched)treatment #258 established forhandler. Reuse that sentinel convention rather than inventing a second one./metricsfor attacker-controlled input, and state the result in the PR body. Two dimensions have now been found unbounded by two separate passes; assume a third until proven otherwise.codeis a bounded status code andserviceis static, but verify rather than assume, and check the delivery metrics ininternal/metrics/metrics.goas well as the HTTP middleware.Also in this unit, same README section
The aggregate
http_requests_inflight{handler="(all)"}semantics #258 introduced are documented in its PR body and in code, but not in README's Metrics section — which is the very section that makes the cardinality argument for the delivery metrics. Document it there.Verification
make checkgreen.nextso the probe is known to work, then show it fixed: scrape, drive several hundred distinct method tokens, scrape again, series count flat.Plan:
methodin the same seam #258 added,routePatternRecorderininternal/middleware/metrics.go, by rewritingprops.Methodon both observation calls alongsideprops.ID. The recorder is renamed to reflect that it now bounds two dimensions rather than one.CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE— chi'smethodMap, which is unexported, so it is restated as aswitchover thenet/httpconstants). Anything else is a token chi answers 405 for and can never route, so it carries the sentinel.(unmatched)value, aliased rather than respelled: the same idea (a client-chosen token matching nothing this service registers) must not read as two different states in one scrape. Bound becomes 10 method values.GET/POST/PUT/DELETE/HEAD/OPTIONSstay distinct; a flat-series-count flood. Fail-first against the current code before the fix goes in./metricsexposes (service,handler,method,code, the deliverytarget_type, and the default Go/process collectors) goes in the PR body, with how each was verified.http_requests_inflight{handler="(all)"}aggregate semantics.No new metrics, no changes under
internal/deliveryorinternal/database, noTODO.mdedit.Done in #264 (branch
issue-261-metrics-method-label, basenext). Full audit table and evidence are in the PR body.Built:
methodis now bounded in the same recorder seam that boundshandler. A method chi can route is kept verbatim; anything else carries the existing(unmatched)sentinel, aliased rather than respelled. Bound is 10 values. README's Metrics section gained the inbound HTTP metrics, each label's bound, and the aggregatehttp_requests_inflight{handler="(all)"}semantics.Verified:
nextatfd5966ffirst: a live instance went from 81 to 7,606 scrape lines under 300 random 12-character method tokens (the issue's 7,525-line delta). The same binary from this branch stayed flat at 331 -> 331./metricsexposes was enumerated from a live authenticated scrape, not from reading alone.codeandserviceare confirmed safe by tracing where each is written. No third unbounded dimension found.make checkgreen withGOFLAGS=-count=1; lint clean in the Docker stage.TODO.mduntouched per #112.