Skip to content

samples/restApi: sanitize caller-controlled log fields (G706)#125

Open
randomizedcoder wants to merge 1 commit into
NVIDIA:mainfrom
randomizedcoder:fix/g706-log-injection
Open

samples/restApi: sanitize caller-controlled log fields (G706)#125
randomizedcoder wants to merge 1 commit into
NVIDIA:mainfrom
randomizedcoder:fix/g706-log-injection

Conversation

@randomizedcoder

Copy link
Copy Markdown

Hi 👋

Same static-analysis run as #124 — gosec also flagged G706 (log injection via taint analysis) at 15 sites across samples/restApi/handlers/{utils,dcgm}.go. All instances are this pattern:

log.Printf("error: %v%v: %v", req.Host, req.URL, err.Error())

req.Host is set from the Host: header, so a client can stuff \n, \r\n, \x00, ANSI escapes, etc. into it and forge log entries or abuse terminal log tails. Sample code → likely to be copy-pasted → worth fixing.

What this PR does

Adds a tiny helper in handlers/utils.go:

func logRequestError(req *http.Request, msg string) {
    var urlStr string
    if req.URL != nil {
        urlStr = req.URL.String()
    }
    log.Printf("error: host=%q url=%q msg=%q", req.Host, urlStr, msg)
}

%q is gosec's recognized taint-cleanser — Go renders LF/CR as \n/\r, NUL as \x00, ANSI ESC as \x1b, U+2028/U+2029 as /, and backslash-escapes embedded quotes. No raw control character survives into the log line regardless of which field carried the payload.

All 15 G706 sites in utils.go (9) and dcgm.go (6) now call the helper.

TDD + table-driven tests with nasty cases

Added samples/restApi/handlers/utils_test.go (15-case TestLogRequestError + 1 TestLogRequestError_NilURL). Categories:

Category Cases What it covers
positive 1 Benign request renders quoted, readable
boundary 1 Empty fields don't panic
corner 1 Multibyte UTF-8 passes through readable
attacker 12 LF, CRLF, NUL, BEL, BS, ANSI ESC \x1b[2J, U+2028, U+2029, embedded quote, 10 KB string, and a mixed-all-attacks case

Every case enforces a blanket security invariant via a loop over {LF, CR, NUL, ESC, BEL, BS, U+2028, U+2029} — none of those raw bytes may appear anywhere in the captured log line. Tests are GPU-less (log.SetOutput(&buf) + synthetic *http.Request).

Heads-up on log line format

The format changes from error: <host><url>: <msg> (which had no separator between Host and URL — a small wart) to error: host="..." url="..." msg="...". More grep-friendly, but if anything downstream is structurally parsing these sample logs I can do a tighter %v%q swap that preserves the wire format. Happy to switch.

Why this is low risk

  • Helper is package-private, no public API change.
  • Sample-only — no pkg/ code touched.
  • All 9 sample binaries build cleanly (go build ./samples/...).
  • go vet, gofmt, and the 16 new tests all green.

Test plan

  • go build ./samples/restApi/...
  • go test -v -count=1 ./samples/restApi/handlers/... — 16/16 pass
  • go vet ./samples/restApi/...
  • gofmt -l samples/restApi/handlers/ — empty
  • CI green

🤖 Generated with Claude Code

15 log.Printf sites across samples/restApi/handlers/{utils,dcgm}.go
concatenated req.Host, req.URL, and err.Error() into the format string
using %v. Since req.Host comes from an attacker-controlled HTTP header,
this lets a malicious client inject \n/\r\n/\x00/ANSI escapes into the
log stream, forging fake entries or abusing terminal log viewers
(gosec G706, CWE-117).

Introduces a `logRequestError(req *http.Request, msg string)` helper in
utils.go that formats all caller-controlled fields with %q, which is
gosec's recognized taint-cleanser. Go's %q renders LF/CR as \n\r,
nulls as \x00, ANSI ESC as \x1b, Unicode line/paragraph separators
(U+2028/2029) as 
/
, and escapes embedded double quotes —
so no raw control character survives into the log line regardless of
which field carried the payload.

Adds samples/restApi/handlers/utils_test.go with a 15-case table-driven
test exercising positive, boundary, corner, and attacker categories.
Every case asserts the blanket security invariant via a loop over
{LF, CR, NUL, ESC, BEL, BS, U+2028, U+2029} that none of these raw
bytes appears in the captured log output. Attacker payloads include
classic log-forgery (newline-injected fake entry), CRLF, null-byte
truncation, BEL/backspace terminal abuse, ANSI clear-screen, Unicode
line separator, and a mixed all-attacks-at-once case. A separate
TestLogRequestError_NilURL guards the defensive nil-URL path.

Side effect: the log line format changes from
  error: <host><url>: <msg>     (no separator between host and URL — a wart)
to
  error: host="..." url="..." msg="..."
which is more grep-friendly. No external system should be parsing
these sample logs structurally.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: randomizedcoder <dave.seddon.ca@gmail.com>
@randomizedcoder randomizedcoder force-pushed the fix/g706-log-injection branch from fcd6a6b to 5c4df4b Compare May 24, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant