Skip to content

Commit 30f0dd8

Browse files
committed
Add string replacement sanitizer to log injection
1 parent 015ef4c commit 30f0dd8

File tree

2 files changed

+42
-16
lines changed

2 files changed

+42
-16
lines changed

go/ql/lib/semmle/go/security/LogInjectionCustomizations.qll

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,12 @@ module LogInjection {
4141
}
4242

4343
/**
44-
* A call to `strings.Replace` or `strings.ReplaceAll`, considered as a sanitizer
45-
* for log injection.
44+
* An expression that is equivalent to `strings.ReplaceAll(s, old, new)`,
45+
* where `old` is a newline character, considered as a sanitizer for log
46+
* injection.
4647
*/
47-
class ReplaceSanitizer extends Sanitizer {
48-
ReplaceSanitizer() {
49-
exists(string name, DataFlow::CallNode call |
50-
this = call and
51-
call.getTarget().hasQualifiedName("strings", name) and
52-
call.getArgument(1).getStringValue().matches("%" + ["\r", "\n"] + "%")
53-
|
54-
name = "Replace" and
55-
call.getArgument(3).getNumericValue() < 0
56-
or
57-
name = "ReplaceAll"
58-
)
59-
}
48+
class ReplaceSanitizer extends StringOps::ReplaceAll, Sanitizer {
49+
ReplaceSanitizer() { this.getReplacedString() = ["\r", "\n"] }
6050
}
6151

6252
/**

go/ql/test/query-tests/Security/CWE-117/LogInjection.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package main
1111
//go:generate depstubber -vendor go.uber.org/zap Logger,SugaredLogger NewProduction
1212

1313
import (
14+
"bytes"
1415
"fmt"
1516
"log"
1617
"net/http"
@@ -378,8 +379,43 @@ func handlerGood2(req *http.Request) {
378379
log.Printf("user %s logged in.\n", escapedUsername)
379380
}
380381

382+
// GOOD: The user-provided value is escaped before being written to the log.
383+
func handlerGood3(req *http.Request) {
384+
username := req.URL.Query()["username"][0]
385+
replacer := strings.NewReplacer("\n", "", "\r", "")
386+
log.Printf("user %s logged in.\n", replacer.Replace(username))
387+
log.Printf("user %s logged in.\n", replacerLocal1(username))
388+
log.Printf("user %s logged in.\n", replacerLocal2(username))
389+
log.Printf("user %s logged in.\n", replacerGlobal1(username))
390+
log.Printf("user %s logged in.\n", replacerGlobal2(username))
391+
}
392+
393+
func replacerLocal1(s string) string {
394+
replacer := strings.NewReplacer("\n", "", "\r", "")
395+
return replacer.Replace(s)
396+
}
397+
398+
func replacerLocal2(s string) string {
399+
replacer := strings.NewReplacer("\n", "", "\r", "")
400+
buf := new(bytes.Buffer)
401+
replacer.WriteString(buf, s)
402+
return buf.String()
403+
}
404+
405+
var globalReplacer = strings.NewReplacer("\n", "", "\r", "")
406+
407+
func replacerGlobal1(s string) string {
408+
return globalReplacer.Replace(s)
409+
}
410+
411+
func replacerGlobal2(s string) string {
412+
buf := new(bytes.Buffer)
413+
globalReplacer.WriteString(buf, s)
414+
return buf.String()
415+
}
416+
381417
// GOOD: User-provided values formatted using a %q directive, which escapes newlines
382-
func handlerGood3(req *http.Request, ctx *goproxy.ProxyCtx) {
418+
func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
383419
username := req.URL.Query()["username"][0]
384420
testFlag := req.URL.Query()["testFlag"][0]
385421
log.Printf("user %q logged in.\n", username)

0 commit comments

Comments
 (0)