diff --git a/go/ext/codeql-pack.lock.yml b/go/ext/codeql-pack.lock.yml new file mode 100644 index 00000000..53004274 --- /dev/null +++ b/go/ext/codeql-pack.lock.yml @@ -0,0 +1,4 @@ +--- +lockVersion: 1.0.0 +dependencies: {} +compiled: false diff --git a/go/ext/generated/.gitkeep b/go/ext/generated/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/go/ext/manual/.gitkeep b/go/ext/manual/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/go/ext/manual/flag.yml b/go/ext/manual/flag.yml new file mode 100644 index 00000000..8c49ed19 --- /dev/null +++ b/go/ext/manual/flag.yml @@ -0,0 +1,19 @@ +extensions: + # Make sure that the extensible model predicates are at least defined as empty. + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["flag", "", True, "String", "", "", "ReturnValue[0]", "remote", "manual"] + - ["flag", "", True, "StringVar", "", "", "ReturnValue[0]", "remote", "manual"] + # local variants + - ["flag", "", True, "String", "", "", "ReturnValue[0]", "local", "manual"] + - ["flag", "", True, "StringVar", "", "", "ReturnValue[0]", "local", "manual"] + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: [] + - addsTo: + pack: codeql/go-all + extensible: summaryModel + data: [] diff --git a/go/ext/manual/os.yml b/go/ext/manual/os.yml new file mode 100644 index 00000000..1c66e004 --- /dev/null +++ b/go/ext/manual/os.yml @@ -0,0 +1,27 @@ +extensions: + # Make sure that the extensible model predicates are at least defined as empty. + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["os", "Args", True, "", "", "", "ReturnValue[0]", "remote", "manual"] + - ["os", "", True, "Getenv", "", "", "ReturnValue[0]", "remote", "manual"] + - ["os", "", True, "LookupEnv", "", "", "ReturnValue[0]", "remote", "manual"] + - ["os", "", True, "ReadFile", "", "", "ReturnValue[0]", "remote", "manual"] + - ["os", "", True, "Readlink", "", "", "ReturnValue[0]", "remote", "manual"] + - ["os", "", True, "Environ", "", "", "ReturnValue[0]", "remote", "manual"] + # local variants + - ["os", "Args", True, "", "", "", "ReturnValue[0]", "local", "manual"] + - ["os", "", True, "Getenv", "", "", "ReturnValue[0]", "local", "manual"] + - ["os", "", True, "LookupEnv", "", "", "ReturnValue[0]", "local", "manual"] + - ["os", "", True, "ReadFile", "", "", "ReturnValue[0]", "local", "manual"] + - ["os", "", True, "Readlink", "", "", "ReturnValue[0]", "local", "manual"] + - ["os", "", True, "Environ", "", "", "ReturnValue[0]", "local", "manual"] + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: [] + - addsTo: + pack: codeql/go-all + extensible: summaryModel + data: [] diff --git a/go/ext/qlpack.yml b/go/ext/qlpack.yml new file mode 100644 index 00000000..b5278f1c --- /dev/null +++ b/go/ext/qlpack.yml @@ -0,0 +1,10 @@ +library: true +name: githubsecuritylab/codeql-go-extensions +version: 0.0.1 +extensionTargets: + codeql/go-all: '*' +dataExtensions: + - 'manual/*.yml' + - 'manual/**/*.yml' + - 'generated/*.yml' + - 'generated/**/*.yml' diff --git a/go/lib/ghsl/LocalSources.qll b/go/lib/ghsl/LocalSources.qll index 3c34cbdb..0c7fa025 100644 --- a/go/lib/ghsl/LocalSources.qll +++ b/go/lib/ghsl/LocalSources.qll @@ -3,82 +3,36 @@ private import go module LocalSources { private import semmle.go.dataflow.DataFlow private import semmle.go.dataflow.TaintTracking + private import semmle.go.dataflow.ExternalFlow as ExternalFlow private import semmle.go.Scopes - - abstract class Range extends DataFlow::Node { } - -// ========== Sources ========== - -abstract class Sources extends DataFlow::Node { } - -// ---------------------------------------------------- -// Used for finding Selections or Calls for Go imports -// ---------------------------------------------------- -//class UseOfGoImports extends Sources { - //UseOfGoImports () { - //exists ( ValueEntity read, - //DataFlow::Package pkg | - //read.getScope().getEntity(_) = pkg.getScope().getEntity(_) - //and ( this.toString().regexpMatch("selection of.*") - //or this.toString().regexpMatch("call to .*") ) - //) - //} -//} - -// ---------------------------------------------------- - -class OsCmd extends LocalSources::Range { - OsCmd() { - exists ( ValueEntity read, - DataFlow::Package pkg | - read.getScope().getEntity(_) = pkg.getScope().getEntity(_) - and this.toString() = "selection of Run" - ) - } -} + /** + * A source of data that is controlled by the local user. + */ + abstract class Range extends DataFlow::Node { } -class OsExec extends LocalSources::Range { - OsExec() { - exists ( ValueEntity read, - DataFlow::Package pkg | - read.getScope().getEntity(_) = pkg.getScope().getEntity(_) - and this.toString() = "selection of Command" - ) + /** + * Support for Local Sources + */ + class MaDLocalSource extends Range { + MaDLocalSource() { ExternalFlow::sourceNode(this, "local") } } -} -class OsArgs extends LocalSources::Range { - OsArgs() { - exists ( ValueEntity read, - DataFlow::Package pkg | - read.getScope().getEntity(_) = pkg.getScope().getEntity(_) - and this.toString() = "selection of Args" - ) + class OsCmd extends LocalSources::Range { + OsCmd() { + exists(ValueEntity read, DataFlow::Package pkg | + read.getScope().getEntity(_) = pkg.getScope().getEntity(_) and + this.toString() = "selection of Run" + ) + } } -} - -// Not currently working (need a test case) -//class OsGetenv extends Sources, DataFlow::CallNode { - //OsGetenv() { - //// https://pkg.go.dev/os#Getenv - //this.getTarget().hasQualifiedName(package("os", ""), "Getenv") - //or - //// https://pkg.go.dev/os#Environ - //this.getTarget().hasQualifiedName(package("os", ""), "Environ") - //} -//} - // https://pkg.go.dev/flag -class Flag extends LocalSources::Range { - Flag() { - exists ( ValueEntity read, - DataFlow::Package pkg | - read.getScope().getEntity(_) = pkg.getScope().getEntity(_) - and - ( this.toString() = "selection of String" - or this.toString() = "selection of Parse" ) + class OsExec extends LocalSources::Range { + OsExec() { + exists(ValueEntity read, DataFlow::Package pkg | + read.getScope().getEntity(_) = pkg.getScope().getEntity(_) and + this.toString() = "selection of Command" ) } + } } -} \ No newline at end of file diff --git a/go/test/lib/localsources/cmd/flag.go b/go/test/lib/localsources/cmd/flag.go new file mode 100644 index 00000000..4bf3eb0d --- /dev/null +++ b/go/test/lib/localsources/cmd/flag.go @@ -0,0 +1,25 @@ +package main + +import ( + "flag" + "fmt" +) + +func main() { + + wordPtr := flag.String("word", "foo", "a string") + + numbPtr := flag.Int("numb", 42, "an int") + forkPtr := flag.Bool("fork", false, "a bool") + + var svar string + flag.StringVar(&svar, "svar", "bar", "a string var") + + flag.Parse() + + fmt.Println("word:", *wordPtr) + fmt.Println("numb:", *numbPtr) + fmt.Println("fork:", *forkPtr) + fmt.Println("svar:", svar) + fmt.Println("tail:", flag.Args()) +} diff --git a/go/test/lib/localsources/cmd/go_os.go b/go/test/lib/localsources/cmd/go_os.go new file mode 100644 index 00000000..3f9c8966 --- /dev/null +++ b/go/test/lib/localsources/cmd/go_os.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + args := os.Args + fmt.Println(args[0], args[1]) + + // Environ + env := os.Environ() + fmt.Println(env[0], env[1]) + + // getenv + myenv := os.Getenv("HOME") + fmt.Println(myenv) + +} diff --git a/go/test/lib/localsources/go.mod b/go/test/lib/localsources/go.mod new file mode 100644 index 00000000..83cd87d0 --- /dev/null +++ b/go/test/lib/localsources/go.mod @@ -0,0 +1,3 @@ +module github.com/GitHubSecurityLab/CodeQLCommunityPacks + +go 1.10 diff --git a/go/test/lib/localsources/local.expected b/go/test/lib/localsources/local.expected new file mode 100644 index 00000000..3822c1b8 --- /dev/null +++ b/go/test/lib/localsources/local.expected @@ -0,0 +1,8 @@ +remoteSources +| cmd/flag.go:10:13:10:50 | call to String | +| cmd/go_os.go:13:9:13:20 | call to Environ | +| cmd/go_os.go:17:11:17:27 | call to Getenv | +localSources +| cmd/flag.go:10:13:10:50 | call to String | +| cmd/go_os.go:13:9:13:20 | call to Environ | +| cmd/go_os.go:17:11:17:27 | call to Getenv | diff --git a/go/test/lib/localsources/local.ql b/go/test/lib/localsources/local.ql new file mode 100644 index 00000000..e9da4d86 --- /dev/null +++ b/go/test/lib/localsources/local.ql @@ -0,0 +1,7 @@ +import go +import ghsl.Utils +import ghsl.LocalSources + +query predicate remoteSources(DataFlow::ExprNode node) { node instanceof RemoteFlowSource::Range } + +query predicate localSources(DataFlow::ExprNode node) { node instanceof LocalSources::Range } diff --git a/go/test/qlpack.yml b/go/test/qlpack.yml index d321edf7..b1b50607 100644 --- a/go/test/qlpack.yml +++ b/go/test/qlpack.yml @@ -5,5 +5,6 @@ dependencies: # codeql/go-queries: '*' githubsecuritylab/codeql-go-queries: '*' githubsecuritylab/codeql-go-libs: '*' + githubsecuritylab/codeql-go-extensions: '*' extractor: go tests: .