Printf Dynamic Format

ID

go.printf_dynamic_format

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Go

Tags

reliability, suspicious

Description

Reports a Printf-family call (fmt.Printf, fmt.Sprintf, fmt.Fprintf, fmt.Errorf) whose format argument is not a constant string and that has no further arguments — for example fmt.Printf(userInput). The format string is the one place where the % verbs live, so a non-constant value used there is treated as a format and any stray % in it is parsed as a verb.

Rationale

When the format is a plain variable with no following arguments, two things go wrong. The data is interpreted as a format, so a % in it produces garbage such as %!d(MISSING) instead of the intended text. And when the value comes from outside the program it is a format-string vulnerability. The intent is almost always to print the value verbatim, which is what %s expresses.

fmt.Printf(userInput)          // FLAW — userInput is used as the format
fmt.Sprintf(msg)               // FLAW — a stray % in msg is parsed as a verb

fmt.Printf("%s", userInput)    // OK — constant format, value as argument
fmt.Printf("done\n")           // OK — constant format, no arguments
fmt.Printf(tmpl, name)         // OK — dynamic format but arguments are supplied

Remediation

If the value should be printed as-is, pass it through a %s verb: fmt.Printf("%s", value). If the value really is a format, keep the verbs in a constant string and pass the data as following arguments.