diff --git a/ql/src/semmle/go/frameworks/Stdlib.qll b/ql/src/semmle/go/frameworks/Stdlib.qll index 44dddef19d5..1d6c1e1330f 100644 --- a/ql/src/semmle/go/frameworks/Stdlib.qll +++ b/ql/src/semmle/go/frameworks/Stdlib.qll @@ -37,6 +37,7 @@ import semmle.go.frameworks.stdlib.NetHttp import semmle.go.frameworks.stdlib.NetHttpHttputil import semmle.go.frameworks.stdlib.NetMail import semmle.go.frameworks.stdlib.NetTextproto +import semmle.go.frameworks.stdlib.Log import semmle.go.frameworks.stdlib.Path import semmle.go.frameworks.stdlib.PathFilepath import semmle.go.frameworks.stdlib.Reflect @@ -481,31 +482,67 @@ module URL { } } -/** Provides models of commonly used functions in the `log` package. */ -module Log { - private class LogCall extends LoggerCall::Range, DataFlow::CallNode { - LogCall() { - exists(string fn | - fn.matches("Fatal%") - or - fn.matches("Panic%") - or - fn.matches("Print%") - | - this.getTarget().hasQualifiedName("log", fn) - or - this.getTarget().(Method).hasQualifiedName("log", "Logger", fn) +/** Provides models of commonly used APIs in the `regexp` package. */ +module Regexp { + private class Pattern extends RegexpPattern::Range, DataFlow::ArgumentNode { + string fnName; + + Pattern() { + exists(Function fn | fnName.matches("Match%") or fnName.matches("%Compile%") | + fn.hasQualifiedName("regexp", fnName) and + this = fn.getACall().getArgument(0) ) } - override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } + override DataFlow::Node getAParse() { result = this.getCall() } + + override string getPattern() { result = this.asExpr().getStringValue() } + + override DataFlow::Node getAUse() { + fnName.matches("MustCompile%") and + result = this.getCall().getASuccessor*() + or + fnName.matches("Compile%") and + result = this.getCall().getResult(0).getASuccessor*() + or + result = this + } } - /** A fatal log function, which calls `os.Exit`. */ - private class FatalLogFunction extends Function { - FatalLogFunction() { exists(string fn | fn.matches("Fatal%") | hasQualifiedName("log", fn)) } + private class MatchFunction extends RegexpMatchFunction::Range, Function { + MatchFunction() { + exists(string fn | fn.matches("Match%") | this.hasQualifiedName("regexp", fn)) + } - override predicate mayReturnNormally() { none() } + override FunctionInput getRegexpArg() { result.isParameter(0) } + + override FunctionInput getValue() { result.isParameter(1) } + + override FunctionOutput getResult() { result.isResult(0) } + } + + private class MatchMethod extends RegexpMatchFunction::Range, Method { + MatchMethod() { + exists(string fn | fn.matches("Match%") | this.hasQualifiedName("regexp", "Regexp", fn)) + } + + override FunctionInput getRegexpArg() { result.isReceiver() } + + override FunctionInput getValue() { result.isParameter(0) } + + override FunctionOutput getResult() { result.isResult() } + } + + private class ReplaceFunction extends RegexpReplaceFunction::Range, Method { + ReplaceFunction() { + exists(string fn | fn.matches("ReplaceAll%") | this.hasQualifiedName("regexp", "Regexp", fn)) + } + + override FunctionInput getRegexpArg() { result.isReceiver() } + + override FunctionInput getSource() { result.isParameter(0) } + + override FunctionOutput getResult() { result.isResult() } } } diff --git a/ql/src/semmle/go/frameworks/stdlib/Log.qll b/ql/src/semmle/go/frameworks/stdlib/Log.qll new file mode 100644 index 00000000000..cdbb84dcec8 --- /dev/null +++ b/ql/src/semmle/go/frameworks/stdlib/Log.qll @@ -0,0 +1,107 @@ +/** + * Provides classes modeling security-relevant aspects of the `log` package. + */ + +import go + +/** Provides models of commonly used functions in the `log` package. */ +module Log { + private class LogCall extends LoggerCall::Range, DataFlow::CallNode { + LogCall() { + exists(string fn | + fn.matches("Fatal%") + or + fn.matches("Panic%") + or + fn.matches("Print%") + | + this.getTarget().hasQualifiedName("log", fn) + or + this.getTarget().(Method).hasQualifiedName("log", "Logger", fn) + ) + } + + override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } + } + + /** A fatal log function, which calls `os.Exit`. */ + private class FatalLogFunction extends Function { + FatalLogFunction() { exists(string fn | fn.matches("Fatal%") | hasQualifiedName("log", fn)) } + + override predicate mayReturnNormally() { none() } + } + + private class FunctionModels extends TaintTracking::FunctionModel { + FunctionInput inp; + FunctionOutput outp; + + FunctionModels() { + // signature: func New(out io.Writer, prefix string, flag int) *Logger + hasQualifiedName("log", "New") and + (inp.isResult() and outp.isParameter(0)) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input = inp and output = outp + } + } + + private class MethodModels extends TaintTracking::FunctionModel, Method { + FunctionInput inp; + FunctionOutput outp; + + MethodModels() { + // signature: func (*Logger).Fatal(v ...interface{}) + this.hasQualifiedName("log", "Logger", "Fatal") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).Fatalf(format string, v ...interface{}) + this.hasQualifiedName("log", "Logger", "Fatalf") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).Fatalln(v ...interface{}) + this.hasQualifiedName("log", "Logger", "Fatalln") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).Panic(v ...interface{}) + this.hasQualifiedName("log", "Logger", "Panic") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).Panicf(format string, v ...interface{}) + this.hasQualifiedName("log", "Logger", "Panicf") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).Panicln(v ...interface{}) + this.hasQualifiedName("log", "Logger", "Panicln") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).Print(v ...interface{}) + this.hasQualifiedName("log", "Logger", "Print") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).Printf(format string, v ...interface{}) + this.hasQualifiedName("log", "Logger", "Printf") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).Println(v ...interface{}) + this.hasQualifiedName("log", "Logger", "Println") and + (inp.isParameter(_) and outp.isReceiver()) + or + // signature: func (*Logger).SetOutput(w io.Writer) + this.hasQualifiedName("log", "Logger", "SetOutput") and + (inp.isReceiver() and outp.isParameter(0)) + or + // signature: func (*Logger).SetPrefix(prefix string) + this.hasQualifiedName("log", "Logger", "SetPrefix") and + (inp.isParameter(0) and outp.isReceiver()) + or + // signature: func (*Logger).Writer() io.Writer + this.hasQualifiedName("log", "Logger", "Writer") and + (inp.isResult() and outp.isReceiver()) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input = inp and output = outp + } + } +} diff --git a/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Log.go b/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Log.go new file mode 100644 index 00000000000..ff83e8b06af --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/Log.go @@ -0,0 +1,205 @@ +// Code generated by https://github.com/gagliardetto/codebox. DO NOT EDIT. + +package main + +import ( + "io" + "log" +) + +func TaintStepTest_LogNew_B0I0O0(sourceCQL interface{}) interface{} { + fromLogger656 := sourceCQL.(*log.Logger) + var intoWriter414 io.Writer + intermediateCQL := log.New(intoWriter414, "", 0) + link(fromLogger656, intermediateCQL) + return intoWriter414 +} + +func TaintStepTest_LogLoggerFatal_B0I0O0(sourceCQL interface{}) interface{} { + fromInterface518 := sourceCQL.(interface{}) + var intoLogger650 log.Logger + intoLogger650.Fatal(fromInterface518) + return intoLogger650 +} + +func TaintStepTest_LogLoggerFatalf_B0I0O0(sourceCQL interface{}) interface{} { + fromString784 := sourceCQL.(string) + var intoLogger957 log.Logger + intoLogger957.Fatalf(fromString784, nil) + return intoLogger957 +} + +func TaintStepTest_LogLoggerFatalf_B0I1O0(sourceCQL interface{}) interface{} { + fromInterface520 := sourceCQL.(interface{}) + var intoLogger443 log.Logger + intoLogger443.Fatalf("", fromInterface520) + return intoLogger443 +} + +func TaintStepTest_LogLoggerFatalln_B0I0O0(sourceCQL interface{}) interface{} { + fromInterface127 := sourceCQL.(interface{}) + var intoLogger483 log.Logger + intoLogger483.Fatalln(fromInterface127) + return intoLogger483 +} + +func TaintStepTest_LogLoggerPanic_B0I0O0(sourceCQL interface{}) interface{} { + fromInterface989 := sourceCQL.(interface{}) + var intoLogger982 log.Logger + intoLogger982.Panic(fromInterface989) + return intoLogger982 +} + +func TaintStepTest_LogLoggerPanicf_B0I0O0(sourceCQL interface{}) interface{} { + fromString417 := sourceCQL.(string) + var intoLogger584 log.Logger + intoLogger584.Panicf(fromString417, nil) + return intoLogger584 +} + +func TaintStepTest_LogLoggerPanicf_B0I1O0(sourceCQL interface{}) interface{} { + fromInterface991 := sourceCQL.(interface{}) + var intoLogger881 log.Logger + intoLogger881.Panicf("", fromInterface991) + return intoLogger881 +} + +func TaintStepTest_LogLoggerPanicln_B0I0O0(sourceCQL interface{}) interface{} { + fromInterface186 := sourceCQL.(interface{}) + var intoLogger284 log.Logger + intoLogger284.Panicln(fromInterface186) + return intoLogger284 +} + +func TaintStepTest_LogLoggerPrint_B0I0O0(sourceCQL interface{}) interface{} { + fromInterface908 := sourceCQL.(interface{}) + var intoLogger137 log.Logger + intoLogger137.Print(fromInterface908) + return intoLogger137 +} + +func TaintStepTest_LogLoggerPrintf_B0I0O0(sourceCQL interface{}) interface{} { + fromString494 := sourceCQL.(string) + var intoLogger873 log.Logger + intoLogger873.Printf(fromString494, nil) + return intoLogger873 +} + +func TaintStepTest_LogLoggerPrintf_B0I1O0(sourceCQL interface{}) interface{} { + fromInterface599 := sourceCQL.(interface{}) + var intoLogger409 log.Logger + intoLogger409.Printf("", fromInterface599) + return intoLogger409 +} + +func TaintStepTest_LogLoggerPrintln_B0I0O0(sourceCQL interface{}) interface{} { + fromInterface246 := sourceCQL.(interface{}) + var intoLogger898 log.Logger + intoLogger898.Println(fromInterface246) + return intoLogger898 +} + +func TaintStepTest_LogLoggerSetOutput_B0I0O0(sourceCQL interface{}) interface{} { + fromLogger598 := sourceCQL.(log.Logger) + var intoWriter631 io.Writer + fromLogger598.SetOutput(intoWriter631) + return intoWriter631 +} + +func TaintStepTest_LogLoggerSetPrefix_B0I0O0(sourceCQL interface{}) interface{} { + fromString165 := sourceCQL.(string) + var intoLogger150 log.Logger + intoLogger150.SetPrefix(fromString165) + return intoLogger150 +} + +func TaintStepTest_LogLoggerWriter_B0I0O0(sourceCQL interface{}) interface{} { + fromWriter340 := sourceCQL.(io.Writer) + var intoLogger471 log.Logger + intermediateCQL := intoLogger471.Writer() + link(fromWriter340, intermediateCQL) + return intoLogger471 +} + +func RunAllTaints_Log() { + { + source := newSource(0) + out := TaintStepTest_LogNew_B0I0O0(source) + sink(0, out) + } + { + source := newSource(1) + out := TaintStepTest_LogLoggerFatal_B0I0O0(source) + sink(1, out) + } + { + source := newSource(2) + out := TaintStepTest_LogLoggerFatalf_B0I0O0(source) + sink(2, out) + } + { + source := newSource(3) + out := TaintStepTest_LogLoggerFatalf_B0I1O0(source) + sink(3, out) + } + { + source := newSource(4) + out := TaintStepTest_LogLoggerFatalln_B0I0O0(source) + sink(4, out) + } + { + source := newSource(5) + out := TaintStepTest_LogLoggerPanic_B0I0O0(source) + sink(5, out) + } + { + source := newSource(6) + out := TaintStepTest_LogLoggerPanicf_B0I0O0(source) + sink(6, out) + } + { + source := newSource(7) + out := TaintStepTest_LogLoggerPanicf_B0I1O0(source) + sink(7, out) + } + { + source := newSource(8) + out := TaintStepTest_LogLoggerPanicln_B0I0O0(source) + sink(8, out) + } + { + source := newSource(9) + out := TaintStepTest_LogLoggerPrint_B0I0O0(source) + sink(9, out) + } + { + source := newSource(10) + out := TaintStepTest_LogLoggerPrintf_B0I0O0(source) + sink(10, out) + } + { + source := newSource(11) + out := TaintStepTest_LogLoggerPrintf_B0I1O0(source) + sink(11, out) + } + { + source := newSource(12) + out := TaintStepTest_LogLoggerPrintln_B0I0O0(source) + sink(12, out) + } + { + source := newSource(13) + out := TaintStepTest_LogLoggerSetOutput_B0I0O0(source) + sink(13, out) + } + { + source := newSource(14) + out := TaintStepTest_LogLoggerSetPrefix_B0I0O0(source) + sink(14, out) + } + { + source := newSource(15) + out := TaintStepTest_LogLoggerWriter_B0I0O0(source) + sink(15, out) + } +}