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.