Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit bff19d5

Browse files
committed
Move and extend Log module for package log with taint-tracking
1 parent c61881a commit bff19d5

File tree

3 files changed

+368
-19
lines changed

3 files changed

+368
-19
lines changed

ql/src/semmle/go/frameworks/Stdlib.qll

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import semmle.go.frameworks.stdlib.NetHttp
3737
import semmle.go.frameworks.stdlib.NetHttpHttputil
3838
import semmle.go.frameworks.stdlib.NetMail
3939
import semmle.go.frameworks.stdlib.NetTextproto
40+
import semmle.go.frameworks.stdlib.Log
4041
import semmle.go.frameworks.stdlib.Path
4142
import semmle.go.frameworks.stdlib.PathFilepath
4243
import semmle.go.frameworks.stdlib.Reflect
@@ -481,31 +482,67 @@ module URL {
481482
}
482483
}
483484

484-
/** Provides models of commonly used functions in the `log` package. */
485-
module Log {
486-
private class LogCall extends LoggerCall::Range, DataFlow::CallNode {
487-
LogCall() {
488-
exists(string fn |
489-
fn.matches("Fatal%")
490-
or
491-
fn.matches("Panic%")
492-
or
493-
fn.matches("Print%")
494-
|
495-
this.getTarget().hasQualifiedName("log", fn)
496-
or
497-
this.getTarget().(Method).hasQualifiedName("log", "Logger", fn)
485+
/** Provides models of commonly used APIs in the `regexp` package. */
486+
module Regexp {
487+
private class Pattern extends RegexpPattern::Range, DataFlow::ArgumentNode {
488+
string fnName;
489+
490+
Pattern() {
491+
exists(Function fn | fnName.matches("Match%") or fnName.matches("%Compile%") |
492+
fn.hasQualifiedName("regexp", fnName) and
493+
this = fn.getACall().getArgument(0)
498494
)
499495
}
500496

501-
override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
497+
override DataFlow::Node getAParse() { result = this.getCall() }
498+
499+
override string getPattern() { result = this.asExpr().getStringValue() }
500+
501+
override DataFlow::Node getAUse() {
502+
fnName.matches("MustCompile%") and
503+
result = this.getCall().getASuccessor*()
504+
or
505+
fnName.matches("Compile%") and
506+
result = this.getCall().getResult(0).getASuccessor*()
507+
or
508+
result = this
509+
}
502510
}
503511

504-
/** A fatal log function, which calls `os.Exit`. */
505-
private class FatalLogFunction extends Function {
506-
FatalLogFunction() { exists(string fn | fn.matches("Fatal%") | hasQualifiedName("log", fn)) }
512+
private class MatchFunction extends RegexpMatchFunction::Range, Function {
513+
MatchFunction() {
514+
exists(string fn | fn.matches("Match%") | this.hasQualifiedName("regexp", fn))
515+
}
516+
517+
override FunctionInput getRegexpArg() { result.isParameter(0) }
518+
519+
override FunctionInput getValue() { result.isParameter(1) }
520+
521+
override FunctionOutput getResult() { result.isResult(0) }
522+
}
523+
524+
private class MatchMethod extends RegexpMatchFunction::Range, Method {
525+
MatchMethod() {
526+
exists(string fn | fn.matches("Match%") | this.hasQualifiedName("regexp", "Regexp", fn))
527+
}
528+
529+
override FunctionInput getRegexpArg() { result.isReceiver() }
530+
531+
override FunctionInput getValue() { result.isParameter(0) }
532+
533+
override FunctionOutput getResult() { result.isResult() }
534+
}
535+
536+
private class ReplaceFunction extends RegexpReplaceFunction::Range, Method {
537+
ReplaceFunction() {
538+
exists(string fn | fn.matches("ReplaceAll%") | this.hasQualifiedName("regexp", "Regexp", fn))
539+
}
540+
541+
override FunctionInput getRegexpArg() { result.isReceiver() }
542+
543+
override FunctionInput getSource() { result.isParameter(0) }
507544

508-
override predicate mayReturnNormally() { none() }
545+
override FunctionOutput getResult() { result.isResult() }
509546
}
510547
}
511548

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `log` package.
3+
*/
4+
5+
import go
6+
7+
/** Provides models of commonly used functions in the `log` package. */
8+
module Log {
9+
private class LogCall extends LoggerCall::Range, DataFlow::CallNode {
10+
LogCall() {
11+
exists(string fn |
12+
fn.matches("Fatal%")
13+
or
14+
fn.matches("Panic%")
15+
or
16+
fn.matches("Print%")
17+
|
18+
this.getTarget().hasQualifiedName("log", fn)
19+
or
20+
this.getTarget().(Method).hasQualifiedName("log", "Logger", fn)
21+
)
22+
}
23+
24+
override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
25+
}
26+
27+
/** A fatal log function, which calls `os.Exit`. */
28+
private class FatalLogFunction extends Function {
29+
FatalLogFunction() { exists(string fn | fn.matches("Fatal%") | hasQualifiedName("log", fn)) }
30+
31+
override predicate mayReturnNormally() { none() }
32+
}
33+
34+
private class FunctionModels extends TaintTracking::FunctionModel {
35+
FunctionInput inp;
36+
FunctionOutput outp;
37+
38+
FunctionModels() {
39+
// signature: func New(out io.Writer, prefix string, flag int) *Logger
40+
hasQualifiedName("log", "New") and
41+
(inp.isResult() and outp.isParameter(0))
42+
}
43+
44+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
45+
input = inp and output = outp
46+
}
47+
}
48+
49+
private class MethodModels extends TaintTracking::FunctionModel, Method {
50+
FunctionInput inp;
51+
FunctionOutput outp;
52+
53+
MethodModels() {
54+
// signature: func (*Logger).Fatal(v ...interface{})
55+
this.hasQualifiedName("log", "Logger", "Fatal") and
56+
(inp.isParameter(_) and outp.isReceiver())
57+
or
58+
// signature: func (*Logger).Fatalf(format string, v ...interface{})
59+
this.hasQualifiedName("log", "Logger", "Fatalf") and
60+
(inp.isParameter(_) and outp.isReceiver())
61+
or
62+
// signature: func (*Logger).Fatalln(v ...interface{})
63+
this.hasQualifiedName("log", "Logger", "Fatalln") and
64+
(inp.isParameter(_) and outp.isReceiver())
65+
or
66+
// signature: func (*Logger).Panic(v ...interface{})
67+
this.hasQualifiedName("log", "Logger", "Panic") and
68+
(inp.isParameter(_) and outp.isReceiver())
69+
or
70+
// signature: func (*Logger).Panicf(format string, v ...interface{})
71+
this.hasQualifiedName("log", "Logger", "Panicf") and
72+
(inp.isParameter(_) and outp.isReceiver())
73+
or
74+
// signature: func (*Logger).Panicln(v ...interface{})
75+
this.hasQualifiedName("log", "Logger", "Panicln") and
76+
(inp.isParameter(_) and outp.isReceiver())
77+
or
78+
// signature: func (*Logger).Print(v ...interface{})
79+
this.hasQualifiedName("log", "Logger", "Print") and
80+
(inp.isParameter(_) and outp.isReceiver())
81+
or
82+
// signature: func (*Logger).Printf(format string, v ...interface{})
83+
this.hasQualifiedName("log", "Logger", "Printf") and
84+
(inp.isParameter(_) and outp.isReceiver())
85+
or
86+
// signature: func (*Logger).Println(v ...interface{})
87+
this.hasQualifiedName("log", "Logger", "Println") and
88+
(inp.isParameter(_) and outp.isReceiver())
89+
or
90+
// signature: func (*Logger).SetOutput(w io.Writer)
91+
this.hasQualifiedName("log", "Logger", "SetOutput") and
92+
(inp.isReceiver() and outp.isParameter(0))
93+
or
94+
// signature: func (*Logger).SetPrefix(prefix string)
95+
this.hasQualifiedName("log", "Logger", "SetPrefix") and
96+
(inp.isParameter(0) and outp.isReceiver())
97+
or
98+
// signature: func (*Logger).Writer() io.Writer
99+
this.hasQualifiedName("log", "Logger", "Writer") and
100+
(inp.isResult() and outp.isReceiver())
101+
}
102+
103+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
104+
input = inp and output = outp
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)