Documentation
¶
Overview ¶
Package controlbytes catches request-derived strings reaching a log, span-attribute, or header sink without a control-byte scrub.
The bug class is terminal/log/header injection: r.URL.Path and r.Header values arrive PERCENT-DECODED, so %0d%0a, %1b and %00 in a request are real CRLF/ESC/NUL by the time middleware handles them. A raw CRLF forges an entry in any line-oriented log consumer; a raw ESC paints attacker bytes into every operator tail; a NUL in a header value reaches recorders and header-copying proxies verbatim (net/http only collapses CR/LF at write time). The 419-probe audit found this shape four times, each fixed the same way — scrub at the sink — and this rule fires on the shape, not the site:
- battery/log accessMiddleware entries (probe TestAccessEntryScrubbedOfControlBytes, fixed 4b7a25d2),
- core/middleware Idempotency's Finish-failure log (probe TestIdempotencyFinishLogKeyScrubbed, fixed b79942f7),
- core/middleware Tracing's span attributes (probe TestTracing_SpanAttrsScrubControlBytes, fixed b79942f7),
- framework/uihost's Link-header alternate path (probe TestLinkAlternatePathControlBytes, fixed a24928c1).
A value counts as scrubbed when it passes through a callee whose name says so (scrub/sanitize/clean/escape/quote/redact — r.URL.EscapedPath and url.QueryEscape qualify) or through a same-package helper that inspects the value byte by byte (the byte-filter loop the uihost fix shipped inside markdownAlternate; a pass-through helper like TrimRight or truncate never looks at individual bytes and does not clear taint).
Postures it deliberately stays silent on, because they are not this bug: JSON and HTML encoders escape structurally (encoding/json, html/template), so encoder arguments are left alone; the response BODY is not a sink — it is the response; span NAMES (tracer.Start, span.SetName) and log keys are left alone, only VALUES are checked; fmt.Sprint* without a writer, and Fprint* to any writer other than os.Stdout/os.Stderr (an http.ResponseWriter or a bytes.Buffer has its own framing); taint does not cross function boundaries — a request-derived argument to a helper is the helper's business, and the byte-indexing form above is the whole interprocedural concession; and structured values like a whole *http.Request or an ErrorReport struct are not sources, only the string-bearing request selectors are.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var Analyzer = &analysis.Analyzer{
Name: "controlbytes",
Doc: "report request-derived strings reaching log/span/header sinks without a control-byte scrub",
Run: run,
}
Functions ¶
This section is empty.
Types ¶
This section is empty.