Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.
Why this matters
The tainted value is an authorization code, an access_token / refresh_token / id_token, a bearer token, a client_secret, or the raw Authorization header, and the sink is a log.*, slog.*, fmt.Print*, or logger.* call. Logs are written to files, shipped to aggregators (Datadog, Splunk, CloudWatch) and read by people and systems that should never see live credentials. A leaked authorization code or token can be replayed to impersonate the user or complete the OAuth exchange (CWE-532).
Never log the raw credential. Redact or mask it before logging, log a non-sensitive identifier instead (a user id, a key id), or drop the field entirely.
VULNERABLE
vulnerable.go
package mainimport ( "fmt" "log" "log/slog" "net/http")// Inline: the authorization code logged straight from the query string.func logCode(w http.ResponseWriter, r *http.Request) { // ruleid: auth.go.flow.oauth-credential-in-log log.Printf("received code=%s", r.URL.Query().Get("code"))}// Indirection: access token stored in a local, then logged via slog.func logAccessToken(w http.ResponseWriter, r *http.Request) { at := r.FormValue("access_token") // ruleid: auth.go.flow.oauth-credential-in-log slog.Info("token exchange", "token", at)}// The raw Authorization header written to stdout via fmt.func logAuthHeader(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") // ruleid: auth.go.flow.oauth-credential-in-log fmt.Println("auth:", auth)}// A client_secret form value logged through a named logger receiver.func logClientSecret(w http.ResponseWriter, r *http.Request, logger *log.Logger) { // ruleid: auth.go.flow.oauth-credential-in-log logger.Printf("client_secret=%s", r.PostFormValue("client_secret"))}func main() { http.HandleFunc("/code", logCode) http.HandleFunc("/at", logAccessToken) _ = http.ListenAndServe(":8080", nil)}
SAFE
safe.go
package mainimport ( "log" "net/http")// redact replaces all but the first few characters of a secret.func redact(s string) string { if len(s) <= 4 { return "****" } return s[:4] + "****"}// Safe: the credential is masked before it reaches the log sink.func logRedacted(w http.ResponseWriter, r *http.Request) { // ok: auth.go.flow.oauth-credential-in-log log.Printf("code=%s", redact(r.URL.Query().Get("code")))}// Safe trap: a benign, non-credential request field logged verbatim must not// fire — the source list is scoped to OAuth credential names only.func logPage(w http.ResponseWriter, r *http.Request) { // ok: auth.go.flow.oauth-credential-in-log log.Printf("page=%s", r.URL.Query().Get("page"))}func main() { http.HandleFunc("/r", logRedacted) http.HandleFunc("/p", logPage) _ = http.ListenAndServe(":8080", nil)}
Suppressing this rule
If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.