diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..d2aab98e7e3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,11 @@ +{ + "extensions": [ + "github.vscode-codeql", + "slevesque.vscode-zipexplorer" + ], + "settings": { + "codeQL.experimentalBqrsParsing": true, + "codeQL.experimentalFeatures": true, + "codeQL.runningQueries.debug": true + } +} \ No newline at end of file diff --git a/.github/workflows/codeqltest.yml b/.github/workflows/codeqltest.yml index 31ed1503154..5b869c800b8 100644 --- a/.github/workflows/codeqltest.yml +++ b/.github/workflows/codeqltest.yml @@ -20,7 +20,7 @@ jobs: echo "Done" cd $HOME echo "Downloading CodeQL CLI..." - curl https://github.com/github/codeql-cli-binaries/releases/download/v2.0.3/codeql.zip -L -o codeql.zip + curl https://github.com/github/codeql-cli-binaries/releases/download/v2.1.1/codeql.zip -L -o codeql.zip echo "Done" echo "Unpacking CodeQL CLI..." unzip -q codeql.zip @@ -53,7 +53,7 @@ jobs: echo "Done" cd $HOME echo "Downloading CodeQL CLI..." - curl https://github.com/github/codeql-cli-binaries/releases/download/v2.0.3/codeql.zip -L -o codeql.zip + curl https://github.com/github/codeql-cli-binaries/releases/download/v2.1.1/codeql.zip -L -o codeql.zip echo "Done" echo "Unpacking CodeQL CLI..." unzip -q codeql.zip @@ -86,10 +86,10 @@ jobs: echo "Done" cd "$HOME" echo "Downloading CodeQL CLI..." - Invoke-WebRequest -Uri https://github.com/github/codeql-cli-binaries/releases/download/v2.0.3/codeql.zip -OutFile codeql.zip + Invoke-WebRequest -Uri https://github.com/github/codeql-cli-binaries/releases/download/v2.1.1/codeql.zip -OutFile codeql.zip echo "Done" echo "Unpacking CodeQL CLI..." - unzip -q codeql.zip + Expand-Archive codeql.zip -DestinationPath $HOME rm -fo codeql.zip echo "Done" diff --git a/.gitignore b/.gitignore index e7067655aa3..b8e85a8e113 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ tools/linux64 tools/osx64 tools/win64 tools/tokenizer.jar +main diff --git a/Makefile b/Makefile index 626da6979bc..8003afe4d3e 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ CODEQL_TOOLS = $(addprefix codeql-tools/,autobuild.cmd autobuild.sh index.cmd in EXTRACTOR_PACK_OUT = build/codeql-extractor-go -BINARIES = go-extractor go-tokenizer go-autobuilder go-bootstrap +BINARIES = go-extractor go-tokenizer go-autobuilder go-bootstrap go-gen-dbscheme .PHONY: tools tools-codeql tools-codeql-full clean \ tools-linux64 tools-osx64 tools-win64 @@ -81,8 +81,8 @@ tools/net/sourceforge/pmd/cpd/GoLanguage.class: extractor/net/sourceforge/pmd/cp rm tools/net/sourceforge/pmd/cpd/TokenEntry.class rm tools/net/sourceforge/pmd/cpd/Tokenizer.class -ql/src/go.dbscheme: tools/$(CODEQL_PLATFORM)/go-extractor$(EXE) - env TRAP_FOLDER=/tmp $< --dbscheme $@ +ql/src/go.dbscheme: tools/$(CODEQL_PLATFORM)/go-gen-dbscheme$(EXE) + $< $@ build/stats/src.stamp: mkdir -p $(@D)/src @@ -109,3 +109,10 @@ build/testdb/go.dbscheme: upgrades/initial/go.dbscheme rm -rf build/testdb echo >build/empty.trap codeql dataset import -S upgrades/initial/go.dbscheme build/testdb build/empty.trap + +.PHONY: sync-dataflow-libraries +sync-dataflow-libraries: + for f in DataFlowImpl.qll DataFlowImplCommon.qll tainttracking1/TaintTrackingImpl.qll;\ + do\ + curl -s -o ./ql/src/semmle/go/dataflow/internal/$$f https://raw.githubusercontent.com/github/codeql/master/java/ql/src/semmle/code/java/dataflow/internal/$$f;\ + done diff --git a/README.md b/README.md index f326a65cad3..7130251c563 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,9 @@ It contains two major components: The goal of this project is to provide comprehensive static analysis support for Go in CodeQL. +For the queries and libraries that power CodeQL support for other languages, visit [the CodeQL +repository](https://github.com/github/codeql). + ## Installation Simply clone this repository. There are no external dependencies. @@ -21,9 +24,10 @@ Code workspace. ## Usage -To analyze a Go codebase, either use the CodeQL command-line interface to create a database -yourself, or download a pre-built database from LGTM.com. You can then run any of the queries -contained in this repository either on the command line or using the VS Code extension. +To analyze a Go codebase, either use the [CodeQL command-line +interface](https://help.semmle.com/codeql/codeql-cli.html) to create a database yourself, or +download a pre-built database from [LGTM.com](https://lgtm.com/). You can then run any of the +queries contained in this repository either on the command line or using the VS Code extension. Note that the [lgtm.com](https://github.com/github/codeql-go/tree/lgtm.com) branch of this repository corresponds to the version of the queries that is currently deployed on LGTM.com. diff --git a/change-notes/2020-05-01-bad-redirect-check.md b/change-notes/2020-05-01-bad-redirect-check.md new file mode 100644 index 00000000000..6ee5d0fb235 --- /dev/null +++ b/change-notes/2020-05-01-bad-redirect-check.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Bad redirect check" (`go/bad-redirect-check`) now requires that the checked variable is actually used in a redirect as opposed to relying on a name-based heuristic. This eliminates some false positive results, and adds more true positive results. diff --git a/change-notes/2020-05-01-macaron-model.md b/change-notes/2020-05-01-macaron-model.md new file mode 100644 index 00000000000..056e7d093cc --- /dev/null +++ b/change-notes/2020-05-01-macaron-model.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Basic support for the [Macaron](https://go-macaron.com/) HTTP library has been added, which may lead to more results from the security queries. \ No newline at end of file diff --git a/change-notes/2020-05-05-clear-text-logging.md b/change-notes/2020-05-05-clear-text-logging.md new file mode 100644 index 00000000000..ad9e974557f --- /dev/null +++ b/change-notes/2020-05-05-clear-text-logging.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Clear-text logging of sensitive information" has been improved to recognize more logging APIs, which may lead to more alerts. \ No newline at end of file diff --git a/change-notes/2020-05-05-mux-model.md b/change-notes/2020-05-05-mux-model.md new file mode 100644 index 00000000000..4ca9d688dd7 --- /dev/null +++ b/change-notes/2020-05-05-mux-model.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Basic support for the [Mux](https://github.com/gorilla/mux/) HTTP library has been added, which + may lead to more results from the security queries. diff --git a/change-notes/2020-05-07-update-data-flow.md b/change-notes/2020-05-07-update-data-flow.md new file mode 100644 index 00000000000..6d67c59b041 --- /dev/null +++ b/change-notes/2020-05-07-update-data-flow.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* The data-flow library has been improved, which affects and improves most security queries. In particular, + flow through functions involving nested field reads and writes is now modeled more fully. diff --git a/change-notes/2020-05-11-reflected-xss.md b/change-notes/2020-05-11-reflected-xss.md new file mode 100644 index 00000000000..0d1f703c7e4 --- /dev/null +++ b/change-notes/2020-05-11-reflected-xss.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* The query "Reflected cross-site scripting" has been improved to recognize more cases where the + value should be considered to be safe, which should lead to fewer false positive results. diff --git a/change-notes/2020-05-12-tainted-path.md b/change-notes/2020-05-12-tainted-path.md new file mode 100644 index 00000000000..ccd78ed7d1f --- /dev/null +++ b/change-notes/2020-05-12-tainted-path.md @@ -0,0 +1,4 @@ +lgtm,codescanning +* The queries "Uncontrolled data used in path expression" and "Arbitrary file write during zip + extraction ("zip slip")" have been improved to recognize more file APIs, which may lead to more + alerts. diff --git a/change-notes/2020-05-13-io-model.md b/change-notes/2020-05-13-io-model.md new file mode 100644 index 00000000000..0e3531efedc --- /dev/null +++ b/change-notes/2020-05-13-io-model.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Modeling of the standard `io` library has been improved, which may lead to more results from the + security queries. diff --git a/change-notes/2020-05-18-redundant-recover.md b/change-notes/2020-05-18-redundant-recover.md new file mode 100644 index 00000000000..cca5e8fe490 --- /dev/null +++ b/change-notes/2020-05-18-redundant-recover.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* A new query "Redundant call to recover" (`go/redundant-recover`) has been added. The query detects calls to `recover` that have no effect. diff --git a/change-notes/2020-05-20-mongodb-model.md b/change-notes/2020-05-20-mongodb-model.md new file mode 100644 index 00000000000..ff0a09d37de --- /dev/null +++ b/change-notes/2020-05-20-mongodb-model.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Modeling of the `go.mongodb.org/mongo-driver/mongo` package has been added, which may lead to more + results from the security queries. diff --git a/change-notes/2020-05-22-websocket-model.md b/change-notes/2020-05-22-websocket-model.md new file mode 100644 index 00000000000..5ce24528ee2 --- /dev/null +++ b/change-notes/2020-05-22-websocket-model.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Modeling of several WebSocket libraries has been added, which may lead to more results from the + security queries. diff --git a/change-notes/2020-05-29-open-redirect.md b/change-notes/2020-05-29-open-redirect.md new file mode 100644 index 00000000000..182a001ae09 --- /dev/null +++ b/change-notes/2020-05-29-open-redirect.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Open URL redirect" (`go/unvalidated-url-redirection`) now recognizes values returned by method `http.Request.FormValue` as possibly user controlled, allowing it to flag more true positive results. diff --git a/extractor/cli/go-extractor/go-extractor.go b/extractor/cli/go-extractor/go-extractor.go index 483f734b8dc..8ab34255e77 100644 --- a/extractor/cli/go-extractor/go-extractor.go +++ b/extractor/cli/go-extractor/go-extractor.go @@ -8,8 +8,6 @@ import ( "runtime/pprof" "strings" - "github.com/github/codeql-go/extractor/dbscheme" - "github.com/github/codeql-go/extractor" ) @@ -20,12 +18,10 @@ func usage() { fmt.Fprintf(os.Stderr, "Usage:\n\n %s [...] [...] [--] ...\n\n", os.Args[0]) fmt.Fprintf(os.Stderr, "Flags:\n\n") fmt.Fprintf(os.Stderr, "--help Print this help.\n") - fmt.Fprintf(os.Stderr, "--dbscheme string Write dbscheme to this file.\n") } -func parseFlags(args []string) ([]string, []string, string) { +func parseFlags(args []string) ([]string, []string) { i := 0 - var dumpDbscheme string buildFlags := []string{} for i < len(args) && strings.HasPrefix(args[i], "-") { if args[i] == "--" { @@ -33,12 +29,7 @@ func parseFlags(args []string) ([]string, []string, string) { break } - if strings.HasPrefix(args[i], "--dbscheme=") { - dumpDbscheme = strings.TrimPrefix(args[i], "--dbscheme=") - } else if args[i] == "--dbscheme" { - i++ - dumpDbscheme = args[i] - } else if args[i] == "--help" { + if args[i] == "--help" { usage() os.Exit(0) } else { @@ -51,21 +42,11 @@ func parseFlags(args []string) ([]string, []string, string) { cpuprofile = os.Getenv("CODEQL_EXTRACTOR_GO_CPU_PROFILE") memprofile = os.Getenv("CODEQL_EXTRACTOR_GO_MEM_PROFILE") - return buildFlags, args[i:], dumpDbscheme + return buildFlags, args[i:] } func main() { - buildFlags, patterns, dumpDbscheme := parseFlags(os.Args[1:]) - - if dumpDbscheme != "" { - f, err := os.Create(dumpDbscheme) - if err != nil { - log.Fatalf("Unable to open file %s for writing.", dumpDbscheme) - } - dbscheme.PrintDbScheme(f) - f.Close() - log.Printf("Dbscheme written to file %s.", dumpDbscheme) - } + buildFlags, patterns := parseFlags(os.Args[1:]) if cpuprofile != "" { f, err := os.Create(cpuprofile) diff --git a/extractor/cli/go-gen-dbscheme/go-gen-dbscheme.go b/extractor/cli/go-gen-dbscheme/go-gen-dbscheme.go new file mode 100644 index 00000000000..2a757b47f5b --- /dev/null +++ b/extractor/cli/go-gen-dbscheme/go-gen-dbscheme.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "os" + + "github.com/github/codeql-go/extractor/dbscheme" +) + +func usage() { + fmt.Fprintf(os.Stderr, "%s is a program for generating the dbscheme for CodeQL Go databases.\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage:\n\n %s \n\n", os.Args[0]) +} + +func main() { + if len(os.Args) != 2 { + usage() + os.Exit(2) + } + + out := os.Args[1] + + f, err := os.Create(out) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to open file %s for writing.", out) + os.Exit(1) + } + dbscheme.PrintDbScheme(f) + f.Close() + fmt.Printf("Dbscheme written to file %s.", out) +} diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index c82fa0459b1..df146c8e4e5 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -4,6 +4,7 @@ import ( "go/ast" "go/token" gotypes "go/types" + "golang.org/x/tools/go/packages" ) var defaultSnippet = AddDefaultSnippet(` @@ -642,6 +643,20 @@ var ModLParenType = ModExprKind.NewBranch("@modlparen") // ModRParenType is the type of go.mod line block end AST nodes var ModRParenType = ModExprKind.NewBranch("@modrparen") +// ErrorType is the type of frontend errors +var ErrorType = NewPrimaryKeyType("@error") + +// ErrorKind is a case type for distinguishing different kinds of frontend errors +var ErrorKind = NewCaseType(ErrorType, "kind") + +// ErrorTypes is a map from error kinds to the corresponding type +var ErrorTypes = map[packages.ErrorKind]*BranchType{ + packages.UnknownError: ErrorKind.NewBranch("@unknownerror"), + packages.ListError: ErrorKind.NewBranch("@listerror"), + packages.ParseError: ErrorKind.NewBranch("@parseerror"), + packages.TypeError: ErrorKind.NewBranch("@typeerror"), +} + // LocationsDefaultTable is the table defining location objects var LocationsDefaultTable = NewTable("locations_default", EntityColumn(LocationDefaultType, "id").Key(), @@ -915,3 +930,16 @@ var ModTokensTable = NewTable("modtokens", EntityColumn(ModExprType, "parent"), IntColumn("idx"), ).KeySet("parent", "idx") + +// ErrorsTable is the table describing frontend errors +var ErrorsTable = NewTable("errors", + EntityColumn(ErrorType, "id").Key(), + IntColumn("kind"), + StringColumn("msg"), + StringColumn("rawpos"), + StringColumn("file"), + IntColumn("line"), + IntColumn("col"), + EntityColumn(PackageType, "package"), + IntColumn("idx"), +).KeySet("package", "idx") diff --git a/extractor/extractor.go b/extractor/extractor.go index f59ee9c228c..65c5501d6d5 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -11,6 +11,7 @@ import ( "log" "os" "path/filepath" + "regexp" "runtime" "strconv" "strings" @@ -57,13 +58,6 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { packages.Visit(pkgs, func(pkg *packages.Package) bool { return true }, func(pkg *packages.Package) { - if len(pkg.Errors) != 0 { - log.Printf("Warning: encountered errors extracting package `%s`:", pkg.PkgPath) - for _, err := range pkg.Errors { - log.Printf(" %s", err.Error()) - } - } - tw, err := trap.NewWriter(pkg.PkgPath, pkg) if err != nil { log.Fatal(err) @@ -74,6 +68,14 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { tw.ForEachObject(extractObjectType) lbl := tw.Labeler.GlobalID(pkg.PkgPath + ";pkg") dbscheme.PackagesTable.Emit(tw, lbl, pkg.Name, pkg.PkgPath, scope) + + if len(pkg.Errors) != 0 { + log.Printf("Warning: encountered errors extracting package `%s`:", pkg.PkgPath) + for i, err := range pkg.Errors { + log.Printf(" %s", err.Error()) + extractError(tw, err, lbl, i) + } + } }) // this sets the number of threads that the Go runtime will spawn; this is separate @@ -253,6 +255,50 @@ func extractObjectType(tw *trap.Writer, obj types.Object, lbl trap.Label) { } } +var ( + // file:line:col + threePartPos = regexp.MustCompile(`^(.+):(\d+):(\d+)$`) + // file:line + twoPartPos = regexp.MustCompile(`^(.+):(\d+)$`) +) + +// extractError extracts the message and location of a frontend error +func extractError(tw *trap.Writer, err packages.Error, pkglbl trap.Label, idx int) { + var ( + lbl = tw.Labeler.FreshID() + kind = dbscheme.ErrorTypes[err.Kind].Index() + pos = err.Pos + file = "" + line = 0 + col = 0 + e error + ) + + if parts := threePartPos.FindStringSubmatch(pos); parts != nil { + // "file:line:col" + col, e = strconv.Atoi(parts[3]) + if e != nil { + log.Printf("Warning: malformed column number `%s`: %v", parts[3], e) + } + line, e = strconv.Atoi(parts[2]) + if e != nil { + log.Printf("Warning: malformed line number `%s`: %v", parts[2], e) + } + file = parts[1] + } else if parts := twoPartPos.FindStringSubmatch(pos); parts != nil { + // "file:line" + line, e = strconv.Atoi(parts[2]) + if e != nil { + log.Printf("Warning: malformed line number `%s`: %v", parts[2], e) + } + file = parts[1] + } else if pos != "" && pos != "-" { + log.Printf("Warning: malformed error position `%s`", pos) + } + file = filepath.ToSlash(srcarchive.TransformPath(file)) + dbscheme.ErrorsTable.Emit(tw, lbl, kind, err.Msg, pos, file, line, col, pkglbl, idx) +} + // extractPackage extracts AST information for all files in the given package func extractPackage(pkg *packages.Package, wg *sync.WaitGroup, goroutineSem *semaphore, fdSem *semaphore) { diff --git a/ql/src/RedundantCode/RedundantRecover.qhelp b/ql/src/RedundantCode/RedundantRecover.qhelp new file mode 100644 index 00000000000..7470b63c898 --- /dev/null +++ b/ql/src/RedundantCode/RedundantRecover.qhelp @@ -0,0 +1,54 @@ + + + + +

+The built-in recover function is only useful inside deferred +functions. Calling it in a function that is never deferred means that it will +always return nil and it will never regain control of a panicking +goroutine. The same is true of calling recover directly in a defer +statement. +

+
+ + +

+Carefully inspect the code to determine whether it is a mistake that should be +fixed. +

+
+ + +

+In the example below, the function fun1 is intended to recover +from the panic. However, the function that is deferred calls another function, +which then calls recover: +

+ +

+This problem can be fixed by deferring the call to the function which calls +recover: +

+ + +

+In the following example, recover is called directly in a defer +statement, which has no effect, so the panic is not caught. +

+ +

+We can fix this by instead deferring an anonymous function which calls +recover. +

+ +
+ + +
  • + Defer, Panic, and Recover - The Go Blog. +
  • +
    + +
    diff --git a/ql/src/RedundantCode/RedundantRecover.ql b/ql/src/RedundantCode/RedundantRecover.ql new file mode 100644 index 00000000000..7e509bc4862 --- /dev/null +++ b/ql/src/RedundantCode/RedundantRecover.ql @@ -0,0 +1,33 @@ +/** + * @name Redundant call to recover + * @description Calling 'recover' in a function which isn't called using a defer + * statement has no effect. Also, putting 'recover' directly in a + * defer statement has no effect. + * @kind problem + * @problem.severity warning + * @id go/redundant-recover + * @tags maintainability + * correctness + * @precision high + */ + +import go + +predicate isDeferred(DataFlow::CallNode call) { + exists(DeferStmt defer | defer.getCall() = call.asExpr()) +} + +from DataFlow::CallNode recoverCall, FuncDef f, string msg +where + recoverCall.getTarget() = Builtin::recover() and + f = recoverCall.getEnclosingCallable() and + ( + isDeferred(recoverCall) and + msg = "Deferred calls to 'recover' have no effect." + or + not isDeferred(recoverCall) and + exists(f.getACall()) and + not isDeferred(f.getACall()) and + msg = "This call to 'recover' has no effect because $@ is never called using a defer statement." + ) +select recoverCall, msg, f, "the enclosing function" diff --git a/ql/src/RedundantCode/RedundantRecover1.go b/ql/src/RedundantCode/RedundantRecover1.go new file mode 100644 index 00000000000..d058dd0dfde --- /dev/null +++ b/ql/src/RedundantCode/RedundantRecover1.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func callRecover1() { + if recover() != nil { + fmt.Printf("recovered") + } +} + +func fun1() { + defer func() { + callRecover1() + }() + panic("1") +} diff --git a/ql/src/RedundantCode/RedundantRecover1Good.go b/ql/src/RedundantCode/RedundantRecover1Good.go new file mode 100644 index 00000000000..b017e050dc4 --- /dev/null +++ b/ql/src/RedundantCode/RedundantRecover1Good.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +func callRecover1Good() { + if recover() != nil { + fmt.Printf("recovered") + } +} + +func fun1Good() { + defer callRecover1Good() + panic("1") +} diff --git a/ql/src/RedundantCode/RedundantRecover2.go b/ql/src/RedundantCode/RedundantRecover2.go new file mode 100644 index 00000000000..4365cb7c9fe --- /dev/null +++ b/ql/src/RedundantCode/RedundantRecover2.go @@ -0,0 +1,6 @@ +package main + +func fun2() { + defer recover() + panic("2") +} diff --git a/ql/src/RedundantCode/RedundantRecover2Good.go b/ql/src/RedundantCode/RedundantRecover2Good.go new file mode 100644 index 00000000000..d34e5c82b63 --- /dev/null +++ b/ql/src/RedundantCode/RedundantRecover2Good.go @@ -0,0 +1,6 @@ +package main + +func fun2Good() { + defer func() { recover() }() + panic("2") +} diff --git a/ql/src/Security/CWE-601/BadRedirectCheck.ql b/ql/src/Security/CWE-601/BadRedirectCheck.ql index d42605bb86b..168326cb30e 100644 --- a/ql/src/Security/CWE-601/BadRedirectCheck.ql +++ b/ql/src/Security/CWE-601/BadRedirectCheck.ql @@ -3,8 +3,8 @@ * @description A redirect check that checks for a leading slash but not two * leading slashes or a leading slash followed by a backslash is * incomplete. - * @kind problem - * @problem.severity warning + * @kind path-problem + * @problem.severity error * @id go/bad-redirect-check * @tags security * external/cwe/cwe-601 @@ -12,6 +12,8 @@ */ import go +import semmle.go.security.OpenUrlRedirectCustomizations +import DataFlow::PathGraph StringOps::HasPrefix checkForLeadingSlash(SsaWithFields v) { exists(DataFlow::Node substr | @@ -21,43 +23,154 @@ StringOps::HasPrefix checkForLeadingSlash(SsaWithFields v) { ) } -DataFlow::Node checkForSecondSlash(SsaWithFields v) { - exists(StringOps::HasPrefix hp | result = hp and hp.getBaseString() = v.getAUse() | +predicate isCheckedForSecondSlash(SsaWithFields v) { + exists(StringOps::HasPrefix hp | hp.getBaseString() = v.getAUse() | hp.getSubstring().getStringValue() = "//" ) or exists(DataFlow::EqualityTestNode eq, DataFlow::Node slash, DataFlow::ElementReadNode er | - result = eq - | slash.getStringValue() = "/" and er.getBase() = v.getAUse() and er.getIndex().getIntValue() = 1 and eq.eq(_, er, slash) ) + or + // a call to path.Clean will strip away multiple leading slashes + isCleaned(v.getAUse()) } -DataFlow::Node checkForSecondBackslash(SsaWithFields v) { - exists(StringOps::HasPrefix hp | result = hp and hp.getBaseString() = v.getAUse() | +/** + * Holds if `nd` is the result of a call to `path.Clean`, or flows into the first argument + * of such a call, possibly inter-procedurally. + */ +predicate isCleaned(DataFlow::Node nd) { + exists(Function clean | clean.hasQualifiedName("path", "Clean") | + nd = clean.getACall() + or + nd = clean.getACall().getArgument(0) + ) + or + isCleaned(nd.getAPredecessor()) + or + exists(FuncDef f, FunctionInput inp | nd = inp.getExitNode(f) | + forex(DataFlow::CallNode call | call.getACallee() = f | isCleaned(inp.getEntryNode(call))) + ) +} + +predicate isCheckedForSecondBackslash(SsaWithFields v) { + exists(StringOps::HasPrefix hp | hp.getBaseString() = v.getAUse() | hp.getSubstring().getStringValue() = "/\\" ) or exists(DataFlow::EqualityTestNode eq, DataFlow::Node slash, DataFlow::ElementReadNode er | - result = eq - | slash.getStringValue() = "\\" and er.getBase() = v.getAUse() and er.getIndex().getIntValue() = 1 and eq.eq(_, er, slash) ) + or + // if this variable comes from or is a net/url.URL.Path, backslashes are most likely sanitized, + // as the parse functions turn them into "%5C" + urlPath(v.getAUse()) } -from DataFlow::Node node, SsaWithFields v +/** + * Holds if `nd` derives its value from the field `url.URL.Path`, possibly inter-procedurally. + */ +predicate urlPath(DataFlow::Node nd) { + exists(Field f | + f.hasQualifiedName("net/url", "URL", "Path") and + nd = f.getARead() + ) + or + urlPath(nd.getAPredecessor()) + or + exists(FuncDef f, FunctionInput inp | nd = inp.getExitNode(f) | + forex(DataFlow::CallNode call | call.getACallee() = f | urlPath(inp.getEntryNode(call))) + ) +} + +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "BadRedirectCheck" } + + override predicate isSource(DataFlow::Node source) { this.isSource(source, _) } + + /** + * Holds if `source` is the first node that flows into a use of a variable that is checked by a + * bad redirect check `check`.. + */ + predicate isSource(DataFlow::Node source, DataFlow::Node check) { + exists(SsaWithFields v | + DataFlow::localFlow(source, v.getAUse()) and + not exists(source.getAPredecessor()) and + isBadRedirectCheckOrWrapper(check, v) + ) + } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + // this is very over-approximate, because most filtering is done by the isSource predicate + exists(Write w | w.writesField(succ, _, pred)) + } + + override predicate isSanitizerOut(DataFlow::Node node) { + // assume this value is safe if something is prepended to it. + exists(StringOps::Concatenation conc, int i, int j | i < j | + node = conc.getOperand(j) and + exists(conc.getOperand(i)) + ) + or + exists(DataFlow::CallNode call, int i | call.getTarget().hasQualifiedName("path", "Join") | + i > 0 and node = call.getArgument(i) + ) + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof OpenUrlRedirect::Sink } +} + +/** + * Holds there is a check `check` that is a bad redirect check, and `v` is either + * checked directly by `check` or checked by a function that contains `check`. + */ +predicate isBadRedirectCheckOrWrapper(DataFlow::Node check, SsaWithFields v) { + isBadRedirectCheck(check, v) + or + exists(DataFlow::CallNode call, FuncDef f, FunctionInput input | + call = f.getACall() and + input.getEntryNode(call) = v.getAUse() and + isBadRedirectCheckWrapper(check, f, input) + ) +} + +/** + * Holds if `check` checks that `v` has a leading slash, but not whether it has another slash or a + * backslash in its second position. + */ +predicate isBadRedirectCheck(DataFlow::Node check, SsaWithFields v) { + // a check for a leading slash + check = checkForLeadingSlash(v) and + // where there does not exist a check for both a second slash and a second backslash + // (we allow those checks to be on variables that are most likely equivalent to `v` + // to rule out false positives due to minor variations in data flow) + not ( + isCheckedForSecondSlash(v.similar()) and + isCheckedForSecondBackslash(v.similar()) + ) +} + +/** + * Holds if `f` contains a bad redirect check `check`, that checks the parameter `input`. + */ +predicate isBadRedirectCheckWrapper(DataFlow::Node check, FuncDef f, FunctionInput input) { + exists(SsaWithFields v | + v.getAUse().getAPredecessor*() = input.getExitNode(f) and + isBadRedirectCheck(check, v) + ) +} + +from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node check where - // there is a check for a leading slash - node = checkForLeadingSlash(v) and - // but not a check for both a second slash and a second backslash - not (exists(checkForSecondSlash(v)) and exists(checkForSecondBackslash(v))) and - v.getQualifiedName().regexpMatch("(?i).*url.*|.*redir.*|.*target.*") -select node, - "This expression checks '$@' for a leading slash but checks do not exist for both '/' and '\\' in the second position.", - v, v.getQualifiedName() + cfg.isSource(source.getNode(), check) and + cfg.hasFlowPath(source, sink) +select check, source, sink, + "This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position.", + source.getNode(), "this value", sink.getNode(), "redirect" diff --git a/ql/src/codeql-suites/go-lgtm-full.qls b/ql/src/codeql-suites/go-lgtm-full.qls index ee466f62619..6950fccec02 100644 --- a/ql/src/codeql-suites/go-lgtm-full.qls +++ b/ql/src/codeql-suites/go-lgtm-full.qls @@ -2,3 +2,8 @@ - qlpack: codeql-go - apply: lgtm-selectors.yml from: codeql-suite-helpers +# These are only for IDE use. +- exclude: + tags contain: + - ide-contextual-queries/local-definitions + - ide-contextual-queries/local-references diff --git a/ql/src/codeql-suites/go-security-and-quality.qls b/ql/src/codeql-suites/go-security-and-quality.qls new file mode 100644 index 00000000000..102526ec6fc --- /dev/null +++ b/ql/src/codeql-suites/go-security-and-quality.qls @@ -0,0 +1,4 @@ +- description: Security-and-quality queries for Go +- qlpack: codeql-go +- apply: security-and-quality-selectors.yml + from: codeql-suite-helpers diff --git a/ql/src/codeql-suites/go-security-extended.qls b/ql/src/codeql-suites/go-security-extended.qls new file mode 100644 index 00000000000..1a4c82d8bf5 --- /dev/null +++ b/ql/src/codeql-suites/go-security-extended.qls @@ -0,0 +1,4 @@ +- description: Security-extended queries for Go +- qlpack: codeql-go +- apply: security-extended-selectors.yml + from: codeql-suite-helpers diff --git a/ql/src/experimental/CWE-640/EmailBad.go b/ql/src/experimental/CWE-640/EmailBad.go new file mode 100644 index 00000000000..aab8467b340 --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailBad.go @@ -0,0 +1,13 @@ +package main + +import ( + "net/http" + "net/smtp" +) + +func mail(w http.ResponseWriter, r *http.Request) { + host := r.Header.Get("Host") + token := backend.getUserSecretResetToken(email) + body := "Click to reset password: " + host + "/" + token + smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body)) +} diff --git a/ql/src/experimental/CWE-640/EmailGood.go b/ql/src/experimental/CWE-640/EmailGood.go new file mode 100644 index 00000000000..d0cfc569b88 --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailGood.go @@ -0,0 +1,13 @@ +package main + +import ( + "net/http" + "net/smtp" +) + +func mailGood(w http.ResponseWriter, r *http.Request) { + host := config.Get("Host") + token := backend.getUserSecretResetToken(email) + body := "Click to reset password: " + host + "/" + token + smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body)) +} diff --git a/ql/src/experimental/CWE-640/EmailInjection.qhelp b/ql/src/experimental/CWE-640/EmailInjection.qhelp new file mode 100644 index 00000000000..f3ac1dc4bbe --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailInjection.qhelp @@ -0,0 +1,46 @@ + + + +

    + Using untrusted input to construct an email can cause multiple security + vulnerabilities. For instance, inclusion of an untrusted input in an email body + may allow an attacker to conduct Cross Site Scripting (XSS) attacks, while + inclusion of an HTTP header may allow a full account compromise as shown in the + example below. +

    +
    + +

    + Any data which is passed to an email subject or body must be sanitized before use. +

    +
    + +

    + In the following example snippet, the host field is user controlled. +

    +

    + A malicious user can send an HTTP request to the targeted web site, + but with a Host header that refers to their own web site. This means the + emails will be sent out to potential victims, originating from a server + they trust, but with links leading to a malicious web site. +

    +

    + If the email contains a password reset link, and should the victim click + the link, the secret reset token will be leaked to the attacker. Using the + leaked token, the attacker can then construct the real reset link and use it to + change the victim's password. +

    + +

    + One way to prevent this is to load the host name from a trusted configuration file instead. +

    + +
    + +
  • + OWASP + Content Spoofing + . +
  • +
    +
    diff --git a/ql/src/experimental/CWE-640/EmailInjection.ql b/ql/src/experimental/CWE-640/EmailInjection.ql new file mode 100644 index 00000000000..7cf11fa1a3e --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailInjection.ql @@ -0,0 +1,19 @@ +/** + * @name Email content injection + * @description Incorporating untrusted input directly into an email message can enable + * content spoofing, which in turn may lead to information leaks and other + * security issues. + * @id go/email-injection + * @kind path-problem + * @problem.severity error + * @tags security + * external/cwe/cwe-640 + */ + +import go +import DataFlow::PathGraph +import EmailInjection::EmailInjection + +from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config +where config.hasFlowPath(source, sink) +select sink, source, sink, "Email content may contain $@.", source.getNode(), "untrusted input" diff --git a/ql/src/experimental/CWE-640/EmailInjection.qll b/ql/src/experimental/CWE-640/EmailInjection.qll new file mode 100644 index 00000000000..4cf8b382c98 --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailInjection.qll @@ -0,0 +1,29 @@ +/** + * Provides a taint-tracking configuration for reasoning about + * server-side email-injection vulnerabilities. + * + * Note, for performance reasons: only import this file if + * `EmailInjection::Configuration` is needed, otherwise + * `EmailInjectionCustomizations` should be imported instead. + */ + +import go + +/** + * Provides a taint-tracking configuration for reasoning about + * email-injection vulnerabilities. + */ +module EmailInjection { + import EmailInjectionCustomizations::EmailInjection + + /** + * A taint-tracking configuration for reasoning about email-injection vulnerabilities. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "Email Injection" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + } +} diff --git a/ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll b/ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll new file mode 100644 index 00000000000..77e3ad97a3b --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll @@ -0,0 +1,30 @@ +/** Provides classes for reasoning about email-injection vulnerabilities. */ + +import go + +/** + * Provides a library for reasoning about email-injection vulnerabilities. + */ +module EmailInjection { + /** + * A data-flow node that should be considered a source of untrusted data for email-injection vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data-flow node that should be considered a sink for email-injection vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** A source of untrusted data, considered as a taint source for email injection. */ + class UntrustedFlowSourceAsSource extends Source { + UntrustedFlowSourceAsSource() { this instanceof UntrustedFlowSource } + } + + /** + * A data-flow node that becomes part of an email considered as a taint sink for email injection. + */ + class MailDataAsSink extends Sink { + MailDataAsSink() { this instanceof EmailData } + } +} diff --git a/ql/src/experimental/CWE-681/IncorrectNumericConversion.go b/ql/src/experimental/CWE-681/IncorrectNumericConversion.go new file mode 100644 index 00000000000..518ca38cee0 --- /dev/null +++ b/ql/src/experimental/CWE-681/IncorrectNumericConversion.go @@ -0,0 +1,20 @@ +package main + +import ( + "strconv" +) + +func parseAllocateBad1(wanted string) int32 { + parsed, err := strconv.Atoi(wanted) + if err != nil { + panic(err) + } + return int32(parsed) +} +func parseAllocateBad2(wanted string) int32 { + parsed, err := strconv.ParseInt(wanted, 10, 64) + if err != nil { + panic(err) + } + return int32(parsed) +} diff --git a/ql/src/experimental/CWE-681/IncorrectNumericConversion.qhelp b/ql/src/experimental/CWE-681/IncorrectNumericConversion.qhelp new file mode 100644 index 00000000000..f978858ed89 --- /dev/null +++ b/ql/src/experimental/CWE-681/IncorrectNumericConversion.qhelp @@ -0,0 +1,65 @@ + + + +

    + If a numeric value string is parsed using strconv.Atoi into an int, and subsequently that int + is converted into another type of a smaller size, the result can produce unexpected values. +

    +

    + This also applies to the results of strconv.ParseFloat, strconv.ParseInt, + and strconv.ParseUint when the specified size is larger than the size of the + type that number is converted to. +

    +
    + +

    + If you need to parse numeric values with specific bit sizes, avoid strconv.Atoi, and instead + use the functions specific to each type (strconv.ParseFloat, strconv.ParseInt, + strconv.ParseUint) that also allow to specify the wanted bit size. +

    +

    + When using those functions, be careful to not convert the result to another type with a smaller bit size than + the bit size you specified when parsing the number. +

    +

    + If this is not possible, then add upper (and lower) bound checks specific to each type and + bit size (you can find the minimum and maximum value for each type in the `math` package). +

    +
    + +

    + In the first example, assume that an input string is passed to parseAllocateBad1 function, + parsed by strconv.Atoi, and then converted into an int32 type: +

    + +

    + The bounds are not checked, so this means that if the provided number is greater than the maximum value of type int32, + the resulting value from the conversion will be different from the actual provided value. +

    +

    + To avoid unexpected values, you should either use the other functions provided by the strconv + package to parse the specific types and bit sizes as shown in the + parseAllocateGood2 function; or check bounds as in the parseAllocateGood1 + function. +

    + +
    + +

    + In the second example, assume that an input string is passed to parseAllocateBad2 function, + parsed by strconv.ParseInt with a bit size set to 64, and then converted into an int32 type: +

    + +

    + If the provided number is greater than the maximum value of type int32, the resulting value from the conversion will be + different from the actual provided value. +

    +

    + To avoid unexpected values, you should specify the correct bit size as in parseAllocateGood3; + or check bounds before making the conversion as in parseAllocateGood4. +

    + +
    +
    diff --git a/ql/src/experimental/CWE-681/IncorrectNumericConversion.ql b/ql/src/experimental/CWE-681/IncorrectNumericConversion.ql new file mode 100644 index 00000000000..def77b696e9 --- /dev/null +++ b/ql/src/experimental/CWE-681/IncorrectNumericConversion.ql @@ -0,0 +1,266 @@ +/** + * @name Incorrect conversion between numeric types + * @description Converting the result of strconv.Atoi (and other parsers from strconv package) + * to numeric types of smaller bit size can produce unexpected values. + * @kind path-problem + * @problem.severity warning + * @id go/incorrect-numeric-conversion + * @tags security + * external/cwe/cwe-190 + * external/cwe/cwe-681 + */ + +import go +import DataFlow::PathGraph + +/** A function that parses integers. */ +class Atoi extends Function { + Atoi() { this.hasQualifiedName("strconv", "Atoi") } +} + +/** A function that parses floating-point numbers. */ +class ParseFloat extends Function { + ParseFloat() { this.hasQualifiedName("strconv", "ParseFloat") } +} + +/** A function that parses integers with a specifiable bitSize. */ +class ParseInt extends Function { + ParseInt() { this.hasQualifiedName("strconv", "ParseInt") } +} + +/** A function that parses unsigned integers with a specifiable bitSize. */ +class ParseUint extends Function { + ParseUint() { this.hasQualifiedName("strconv", "ParseUint") } +} + +/** Provides a class for modeling calls to number-parsing functions. */ +module ParserCall { + /** + * A data-flow call node that parses a number. + */ + abstract class Range extends DataFlow::CallNode { + /** Gets the bit size of the result number. */ + abstract int getTargetBitSize(); + + /** Gets the name of the parser function. */ + abstract string getParserName(); + } +} + +class ParserCall extends DataFlow::CallNode { + ParserCall::Range self; + + ParserCall() { this = self } + + int getTargetBitSize() { result = self.getTargetBitSize() } + + string getParserName() { result = self.getParserName() } +} + +class AtoiCall extends DataFlow::CallNode, ParserCall::Range { + AtoiCall() { exists(Atoi atoi | this = atoi.getACall()) } + + override int getTargetBitSize() { result = 0 } + + override string getParserName() { result = "strconv.Atoi" } +} + +class ParseIntCall extends DataFlow::CallNode, ParserCall::Range { + ParseIntCall() { exists(ParseInt parseInt | this = parseInt.getACall()) } + + override int getTargetBitSize() { result = this.getArgument(2).getIntValue() } + + override string getParserName() { result = "strconv.ParseInt" } +} + +class ParseUintCall extends DataFlow::CallNode, ParserCall::Range { + ParseUintCall() { exists(ParseUint parseUint | this = parseUint.getACall()) } + + override int getTargetBitSize() { result = this.getArgument(2).getIntValue() } + + override string getParserName() { result = "strconv.ParseUint" } +} + +class ParseFloatCall extends DataFlow::CallNode, ParserCall::Range { + ParseFloatCall() { exists(ParseFloat parseFloat | this = parseFloat.getACall()) } + + override int getTargetBitSize() { result = this.getArgument(1).getIntValue() } + + override string getParserName() { result = "strconv.ParseFloat" } +} + +class NumericConversionExpr extends ConversionExpr { + string fullTypeName; + int bitSize; + + NumericConversionExpr() { + exists(NumericType conv | + conv = getTypeExpr().getType().getUnderlyingType() and + fullTypeName = conv.getName() and + bitSize = conv.getSize() + ) + } + + string getFullTypeName() { result = fullTypeName } + + int getBitSize() { result = bitSize } +} + +/** + * An `if` statement with the condition being either a relational comparison, + * or one or more `&&`. + */ +class IfRelationalComparison extends IfStmt { + IfRelationalComparison() { + this.getCond() instanceof RelationalComparisonExpr or this.getCond() instanceof LandExpr + } + + RelationalComparisonExpr getComparison() { result = this.getCond().(RelationalComparisonExpr) } + + LandExpr getLandExpr() { result = this.getCond().(LandExpr) } +} + +/** + * Flow of result of parsing a 64 bit number, to conversion to lower bit numbers. + */ +class Lt64BitFlowConfig extends TaintTracking::Configuration, DataFlow::Configuration { + Lt64BitFlowConfig() { this = "Lt64BitFlowConfig" } + + override predicate isSource(DataFlow::Node source) { + exists(ParserCall call | call.getTargetBitSize() = [0, 64] | source = call) + } + + override predicate isSink(DataFlow::Node sink) { + exists(NumericConversionExpr conv | conv.getBitSize() = [32, 16, 8] | sink.asExpr() = conv) + } + + override predicate isSanitizerIn(DataFlow::Node node) { isSanitizedInsideAnIfBoundCheck(node) } +} + +/** + * Flow of result of parsing a 32 bit number, to conversion to lower bit numbers. + */ +class Lt32BitFlowConfig extends TaintTracking::Configuration, DataFlow::Configuration { + Lt32BitFlowConfig() { this = "Lt32BitFlowConfig" } + + override predicate isSource(DataFlow::Node source) { + // NOTE: target bit size 0 is already addressed in Lt64BitFlowConfig. + exists(ParserCall call | call.getTargetBitSize() = [/*0,*/ 32] | source = call) + } + + override predicate isSink(DataFlow::Node sink) { + exists(NumericConversionExpr conv | conv.getBitSize() = [16, 8] | sink.asExpr() = conv) + } + + override predicate isSanitizerIn(DataFlow::Node node) { isSanitizedInsideAnIfBoundCheck(node) } +} + +/** + * Flow of result of parsing a 16 bit number, to conversion to lower bit numbers. + */ +class Lt16BitFlowConfig extends TaintTracking::Configuration, DataFlow::Configuration { + Lt16BitFlowConfig() { this = "Lt16BitFlowConfig" } + + override predicate isSource(DataFlow::Node source) { + exists(ParserCall call | call.getTargetBitSize() = 16 | source = call) + } + + override predicate isSink(DataFlow::Node sink) { + exists(NumericConversionExpr conv | conv.getBitSize() = 8 | sink.asExpr() = conv) + } + + override predicate isSanitizerIn(DataFlow::Node node) { isSanitizedInsideAnIfBoundCheck(node) } +} + +/** + * Check if the node is a numeric conversion inside an `if` body, where + * the `if` condition contains an upper bound check on the conversion operand. + */ +predicate isSanitizedInsideAnIfBoundCheck(DataFlow::Node node) { + exists(IfRelationalComparison comp, NumericConversionExpr conv | + conv = node.asExpr().(NumericConversionExpr) and + conv.getBitSize() = [8, 16, 32] and + comp.getThen().getAChild*() = conv and + ( + // If the conversion is inside an `if` block that compares the source as + // `source > 0` or `source >= 0`, then that sanitizes conversion of int to int32; + conv.getFullTypeName() = "int32" and + comp.getComparison().getLesserOperand().getNumericValue() = 0 and + comp.getComparison().getGreaterOperand().getGlobalValueNumber() = + conv.getOperand().getGlobalValueNumber() + or + comparisonGreaterOperandValueIsEqual("int8", comp, conv, getMaxInt8()) + or + comparisonGreaterOperandValueIsEqual("int16", comp, conv, getMaxInt16()) + or + comparisonGreaterOperandValueIsEqual("int32", comp, conv, getMaxInt32()) + or + comparisonGreaterOperandValueIsEqual("uint8", comp, conv, getMaxUint8()) + or + comparisonGreaterOperandValueIsEqual("uint16", comp, conv, getMaxUint16()) + ) + ) +} + +int getMaxInt8() { result = 2.pow(7) - 1 } + +int getMaxInt16() { result = 2.pow(15) - 1 } + +int getMaxInt32() { result = 2.pow(31) - 1 } + +int getMaxUint8() { result = 2.pow(8) - 1 } + +int getMaxUint16() { result = 2.pow(16) - 1 } + +/** + * The `if` relational comparison (which can also be inside a `LandExpr`) stating that + * the greater operand is equal to `value`, and the lesses operand is the conversion operand. + */ +predicate comparisonGreaterOperandValueIsEqual( + string typeName, IfRelationalComparison ifExpr, NumericConversionExpr conv, int value +) { + conv.getFullTypeName() = typeName and + ( + // exclude cases like: if parsed < math.MaxInt8 {return int8(parsed)} + exists(RelationalComparisonExpr comp | comp = ifExpr.getComparison() | + // greater operand is equal to value: + comp.getGreaterOperand().getNumericValue() = value and + // and lesser is the conversion operand: + comp.getLesserOperand().getGlobalValueNumber() = conv.getOperand().getGlobalValueNumber() + ) + or + // exclude cases like: if err == nil && parsed < math.MaxInt8 {return int8(parsed)} + exists(RelationalComparisonExpr andExpr | + andExpr = ifExpr.getLandExpr().getAnOperand().(RelationalComparisonExpr) + | + // greater operand is equal to value: + andExpr.getGreaterOperand().getNumericValue() = value and + // and lesser is the conversion operand: + andExpr.getLesserOperand().getGlobalValueNumber() = conv.getOperand().getGlobalValueNumber() + ) + ) +} + +string formatBitSize(ParserCall call) { + call.getTargetBitSize() = 0 and result = "(arch-dependent)" + or + call.getTargetBitSize() > 0 and result = call.getTargetBitSize().toString() +} + +from DataFlow::PathNode source, DataFlow::PathNode sink +where + ( + exists(Lt64BitFlowConfig cfg | cfg.hasFlowPath(source, sink)) + or + exists(Lt32BitFlowConfig cfg | cfg.hasFlowPath(source, sink)) + or + exists(Lt16BitFlowConfig cfg | cfg.hasFlowPath(source, sink)) + ) and + // Exclude results in test files: + exists(File fl | fl = sink.getNode().asExpr().(NumericConversionExpr).getFile() | + not fl instanceof TestFile + ) +select source.getNode(), source, sink, + "Incorrect conversion of a " + formatBitSize(source.getNode().(ParserCall)) + "-bit number from " + + source.getNode().(ParserCall).getParserName() + " result to a lower bit size type " + + sink.getNode().asExpr().(NumericConversionExpr).getFullTypeName() diff --git a/ql/src/experimental/CWE-681/IncorrectNumericConversionGood.go b/ql/src/experimental/CWE-681/IncorrectNumericConversionGood.go new file mode 100644 index 00000000000..29c111cf54e --- /dev/null +++ b/ql/src/experimental/CWE-681/IncorrectNumericConversionGood.go @@ -0,0 +1,51 @@ +package main + +import ( + "math" + "strconv" +) + +func main() { + +} + +const DefaultAllocate int32 = 256 + +func parseAllocateGood1(desired string) int32 { + parsed, err := strconv.Atoi(desired) + if err != nil { + return DefaultAllocate + } + // GOOD: check for lower and uppper bounds + if parsed > 0 && parsed <= math.MaxInt32 { + return int32(parsed) + } + return DefaultAllocate +} +func parseAllocateGood2(desired string) int32 { + // GOOD: parse specifying the bit size + parsed, err := strconv.ParseInt(desired, 10, 32) + if err != nil { + return DefaultAllocate + } + return int32(parsed) +} + +func parseAllocateGood3(wanted string) int32 { + parsed, err := strconv.ParseInt(wanted, 10, 32) + if err != nil { + panic(err) + } + return int32(parsed) +} +func parseAllocateGood4(wanted string) int32 { + parsed, err := strconv.ParseInt(wanted, 10, 64) + if err != nil { + panic(err) + } + // GOOD: check for lower and uppper bounds + if parsed > 0 && parsed <= math.MaxInt32 { + return int32(parsed) + } + return DefaultAllocate +} diff --git a/ql/src/experimental/CWE-807/SensitiveConditionBypass.qhelp b/ql/src/experimental/CWE-807/SensitiveConditionBypass.qhelp new file mode 100644 index 00000000000..ee844ade258 --- /dev/null +++ b/ql/src/experimental/CWE-807/SensitiveConditionBypass.qhelp @@ -0,0 +1,31 @@ + + + +

    +Testing untrusted user input against a fixed constant results in +a bypass of the conditional check as the attacker may alter the input to match the constant. +When an incorrect check of this type is used to guard a potentially sensitive block, +it results an attacker gaining access to the sensitive block. +

    +
    + +

    + Never decide whether to authenticate a user based on data that may be controlled by that user. + If necessary, ensure that the data is validated extensively when it is input before any + authentication checks are performed. +

    +

    +It is still possible to have a system that "remembers" users, thus not requiring +the user to login on every interaction. For example, personalization settings can be applied +without authentication because this is not sensitive information. However, users +should be allowed to take sensitive actions only when they have been fully authenticated. +

    +
    + +

    +The following example shows a comparison where an user controlled +expression is used to guard a sensitive method. This should be avoided.: +

    + +
    +
    diff --git a/ql/src/experimental/CWE-807/SensitiveConditionBypass.ql b/ql/src/experimental/CWE-807/SensitiveConditionBypass.ql new file mode 100644 index 00000000000..35ba33bd056 --- /dev/null +++ b/ql/src/experimental/CWE-807/SensitiveConditionBypass.ql @@ -0,0 +1,31 @@ +/** + * @name User-controlled bypassing of sensitive action + * @description This query tests for user-controlled bypassing + * of sensitive actions. + * @id go/sensitive-condition-bypass + * @kind problem + * @problem.severity warning + * @tags external/cwe/cwe-807 + * external/cwe/cwe-247 + * external/cwe/cwe-350 + */ + +import go +import SensitiveConditionBypass + +from + ControlFlow::ConditionGuardNode guard, DataFlow::Node sensitiveSink, + SensitiveExpr::Classification classification, Configuration config, DataFlow::PathNode source, + DataFlow::PathNode operand, ComparisonExpr comp +where + // there should be a flow between source and the operand sink + config.hasFlowPath(source, operand) and + // both the operand should belong to the same comparision expression + operand.getNode().asExpr() = comp.getAnOperand() and + // get the ConditionGuardNode corresponding to the comparision expr. + guard.getCondition() = comp and + // the sink `sensitiveSink` should be sensitive, + isSensitive(sensitiveSink, classification) and + // the guard should control the sink + guard.dominates(sensitiveSink.getBasicBlock()) +select comp, "This sensitive comparision check can potentially be bypassed." diff --git a/ql/src/experimental/CWE-807/SensitiveConditionBypass.qll b/ql/src/experimental/CWE-807/SensitiveConditionBypass.qll new file mode 100644 index 00000000000..065282b0531 --- /dev/null +++ b/ql/src/experimental/CWE-807/SensitiveConditionBypass.qll @@ -0,0 +1,68 @@ +import go +import semmle.go.security.SensitiveActions + +/** + * Holds if `sink` is used in a context that suggests it may hold sensitive data of + * the given `type`. + */ +predicate isSensitive(DataFlow::Node sink, SensitiveExpr::Classification type) { + exists(Write write, string name | + write.getRhs() = sink and + name = write.getLhs().getName() and + // whitelist obvious test password variables + not name.regexpMatch(HeuristicNames::notSensitive()) + | + name.regexpMatch(HeuristicNames::maybeSensitive(type)) + ) + or + exists(SensitiveCall a | sink.asExpr() = a and a.getClassification() = type) + or + exists(SensitiveExpr a | sink.asExpr() = a and a.getClassification() = type) + or + exists(SensitiveAction a | a = sink and type = SensitiveExpr::secret()) +} + +private class ConstComparisonExpr extends ComparisonExpr { + string constString; + + ConstComparisonExpr() { + exists(DataFlow::Node n | + n.getASuccessor*() = DataFlow::exprNode(this.getAnOperand()) and + constString = n.getStringValue() + ) + } + + predicate isPotentialFalsePositive() { + // if its an empty string + constString.length() = 0 or + // // if it is uri path + constString.matches("/%") or + constString.matches("%/") or + constString.matches("%/%") + } +} + +/** + * A data-flow configuration for reasoning about + * user-controlled bypassing of sensitive actions. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "Condtional Expression Check Bypass" } + + override predicate isSource(DataFlow::Node source) { + source instanceof UntrustedFlowSource + or + exists(DataFlow::FieldReadNode f | + f.getField().hasQualifiedName("net/http", "Request", "Host") + | + source = f + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(ConstComparisonExpr c | + c.getAnOperand() = sink.asExpr() and + not c.isPotentialFalsePositive() + ) + } +} diff --git a/ql/src/experimental/CWE-807/SensitiveConditionBypassBad.go b/ql/src/experimental/CWE-807/SensitiveConditionBypassBad.go new file mode 100644 index 00000000000..bf8e70f88b7 --- /dev/null +++ b/ql/src/experimental/CWE-807/SensitiveConditionBypassBad.go @@ -0,0 +1,10 @@ +package main + +import "net/http" + +func example(w http.ResponseWriter, r *http.Request) { + test2 := "test" + if r.Header.Get("X-Password") != test2 { + login() + } +} diff --git a/ql/src/experimental/CWE-840/ConditionalBypass.qhelp b/ql/src/experimental/CWE-840/ConditionalBypass.qhelp new file mode 100644 index 00000000000..3a1e5503de2 --- /dev/null +++ b/ql/src/experimental/CWE-840/ConditionalBypass.qhelp @@ -0,0 +1,26 @@ + + + +

    +Conditional checks that compare two values that are both controlled by an untrusted user against +each other are easy to bypass and should not be used in security-critical contexts. +

    +
    + +

    +To guard against bypass, it is advisable to avoid framing a comparison where both sides are +untrusted user inputs. Instead, use a configuration to store and access the values required. +

    +
    + +

    +The following example shows a comparison where both the sides are from attacker-controlled request +headers. This should be avoided: +

    + +

    +One way to remedy the problem is to test against a value stored in a configuration: +

    + +
    +
    \ No newline at end of file diff --git a/ql/src/experimental/CWE-840/ConditionalBypass.ql b/ql/src/experimental/CWE-840/ConditionalBypass.ql new file mode 100644 index 00000000000..09d8aefe3f9 --- /dev/null +++ b/ql/src/experimental/CWE-840/ConditionalBypass.ql @@ -0,0 +1,40 @@ +/** + * @name User-controlled bypass of condition + * @description A check that compares two user-controlled inputs with each other can be bypassed + * by a malicious user. + * @id go/user-controlled-bypass + * @kind problem + * @problem.severity warning + * @tags external/cwe/cwe-840 + */ + +import go + +/** + * A taint-tracking configuration for reasoning about conditional bypass. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "ConditionalBypass" } + + override predicate isSource(DataFlow::Node source) { + source instanceof UntrustedFlowSource + or + source = any(Field f | f.hasQualifiedName("net/http", "Request", "Host")).getARead() + } + + override predicate isSink(DataFlow::Node sink) { + exists(ComparisonExpr c | c.getAnOperand() = sink.asExpr()) + } +} + +from + Configuration config, DataFlow::PathNode lhsSource, DataFlow::PathNode lhs, + DataFlow::PathNode rhsSource, DataFlow::PathNode rhs, ComparisonExpr c +where + config.hasFlowPath(rhsSource, rhs) and + rhs.getNode().asExpr() = c.getRightOperand() and + config.hasFlowPath(lhsSource, lhs) and + lhs.getNode().asExpr() = c.getLeftOperand() +select c, + "This comparison compares user-controlled values from $@ and $@, and hence can be bypassed.", + lhsSource, "here", rhsSource, "here" diff --git a/ql/src/experimental/CWE-840/ConditionalBypassBad.go b/ql/src/experimental/CWE-840/ConditionalBypassBad.go new file mode 100644 index 00000000000..b788dee2009 --- /dev/null +++ b/ql/src/experimental/CWE-840/ConditionalBypassBad.go @@ -0,0 +1,12 @@ +package main + +import ( + "net/http" +) + +func exampleHandlerBad(w http.ResponseWriter, r *http.Request) { + // BAD: the Origin and Host headers are user controlled + if r.Header.Get("Origin") != "http://"+r.Host { + //do something + } +} diff --git a/ql/src/experimental/CWE-840/ConditionalBypassGood.go b/ql/src/experimental/CWE-840/ConditionalBypassGood.go new file mode 100644 index 00000000000..635d16d1f8f --- /dev/null +++ b/ql/src/experimental/CWE-840/ConditionalBypassGood.go @@ -0,0 +1,12 @@ +package main + +import ( + "net/http" +) + +func exampleHandlerGood(w http.ResponseWriter, r *http.Request) { + // GOOD: the configuration is not user controlled + if r.Header.Get("Origin") != config.get("Host") { + //do something + } +} diff --git a/ql/src/experimental/frameworks/Gin.qll b/ql/src/experimental/frameworks/Gin.qll new file mode 100644 index 00000000000..a39243c60e2 --- /dev/null +++ b/ql/src/experimental/frameworks/Gin.qll @@ -0,0 +1,169 @@ +/** + * Provides classes for working with untrusted flow sources from the `github.com/gin-gonic/gin` package. + */ + +import go + +private module Gin { + /** + * Data from a `Context` struct, considered as a source of untrusted flow. + */ + private class GithubComGinGonicGinContextSource extends UntrustedFlowSource::Range { + GithubComGinGonicGinContextSource() { + exists(string packagePath, string typeName | + packagePath = "github.com/gin-gonic/gin" and + typeName = "Context" + | + // Method calls: + exists(DataFlow::MethodCallNode call, string methodName | + call.getTarget().hasQualifiedName(packagePath, typeName, methodName) and + ( + methodName = "FullPath" + or + methodName = "GetHeader" + or + methodName = "QueryArray" + or + methodName = "Query" + or + methodName = "PostFormArray" + or + methodName = "PostForm" + or + methodName = "Param" + or + methodName = "GetStringSlice" + or + methodName = "GetString" + or + methodName = "GetRawData" + or + methodName = "ClientIP" + or + methodName = "ContentType" + or + methodName = "Cookie" + or + methodName = "GetQueryArray" + or + methodName = "GetQuery" + or + methodName = "GetPostFormArray" + or + methodName = "GetPostForm" + or + methodName = "DefaultPostForm" + or + methodName = "DefaultQuery" + or + methodName = "GetPostFormMap" + or + methodName = "GetQueryMap" + or + methodName = "GetStringMap" + or + methodName = "GetStringMapString" + or + methodName = "GetStringMapStringSlice" + or + methodName = "PostFormMap" + or + methodName = "QueryMap" + ) + | + this = call.getResult(0) + ) + or + // Field reads: + exists(DataFlow::Field fld | + fld.hasQualifiedName(packagePath, typeName, "Accepted") and + this = fld.getARead() + ) + ) + } + } + + /** + * Data from a `Params` slice, considered as a source of untrusted flow. + */ + private class GithubComGinGonicGinParamsSource extends UntrustedFlowSource::Range { + GithubComGinGonicGinParamsSource() { + exists(string packagePath, string typeName | + packagePath = "github.com/gin-gonic/gin" and + typeName = "Params" + | + // Any read of a variable of this type: + exists(DataFlow::ReadNode read | read.getType().hasQualifiedName(packagePath, typeName) | + this = read + ) + or + // Method calls: + exists(DataFlow::MethodCallNode call | + call.getTarget().hasQualifiedName(packagePath, typeName, ["ByName", "Get"]) + | + this = call.getResult(0) + ) + ) + } + } + + /** + * Data from a `Param` struct, considered as a source of untrusted flow. + */ + private class GithubComGinGonicGinParamSource extends UntrustedFlowSource::Range { + GithubComGinGonicGinParamSource() { + exists(string packagePath, string typeName | + packagePath = "github.com/gin-gonic/gin" and + typeName = "Param" + | + // Any read of a variable of this type: + exists(DataFlow::ReadNode read | read.getType().hasQualifiedName(packagePath, typeName) | + this = read + ) + or + // Field reads: + exists(DataFlow::Field fld | fld.hasQualifiedName(packagePath, typeName, ["Key", "Value"]) | + this = fld.getARead() + ) + ) + } + } + + /** + * A call to a method on `Context` struct that unmarshals data into a target. + */ + private class GithubComGinGonicGinContextBindSource extends UntrustedFlowSource::Range { + GithubComGinGonicGinContextBindSource() { + exists(string packagePath, string typeName | + packagePath = "github.com/gin-gonic/gin" and + typeName = "Context" + | + exists(DataFlow::MethodCallNode call, string methodName | + call.getTarget().hasQualifiedName(packagePath, typeName, methodName) and + ( + methodName = "BindJSON" or + methodName = "BindYAML" or + methodName = "BindXML" or + methodName = "BindUri" or + methodName = "BindQuery" or + methodName = "BindWith" or + methodName = "BindHeader" or + methodName = "MustBindWith" or + methodName = "Bind" or + methodName = "ShouldBind" or + methodName = "ShouldBindBodyWith" or + methodName = "ShouldBindJSON" or + methodName = "ShouldBindQuery" or + methodName = "ShouldBindUri" or + methodName = "ShouldBindHeader" or + methodName = "ShouldBindWith" or + methodName = "ShouldBindXML" or + methodName = "ShouldBindYAML" + ) + | + this = call.getArgument(0) + ) + ) + } + } +} diff --git a/ql/src/go.dbscheme b/ql/src/go.dbscheme index f7fb4ff6229..ee5c327face 100644 --- a/ql/src/go.dbscheme +++ b/ql/src/go.dbscheme @@ -124,6 +124,10 @@ modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent #keyset[parent, idx] modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + @container = @file | @folder; @locatable = @node | @localscope; @@ -418,3 +422,9 @@ case @modexpr.kind of | 3 = @modlparen | 4 = @modrparen; +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/ql/src/go.dbscheme.stats b/ql/src/go.dbscheme.stats index 2245ca9508d..c1b9237ef18 100644 --- a/ql/src/go.dbscheme.stats +++ b/ql/src/go.dbscheme.stats @@ -1,41 +1,33 @@ - - @similarity - 0 - - - @file - 523 - - - @folder - 223 - @duplication 0 - - @externalDataElement - 0 - @comment_group 12133 + + @folder + 223 + + + @file + 523 + + + @externalDataElement + 0 + + + @similarity + 0 + @field 19974 - - @slashslashcomment - 24891 - - - @slashstarcomment - 846 - @importdecl 479 @@ -60,6 +52,14 @@ @baddecl 0 + + @slashslashcomment + 24891 + + + @slashstarcomment + 846 + @importspec 3468 @@ -84,6 +84,42 @@ @localscope 36428 + + @pkgobject + 3468 + + + @decltypeobject + 3499 + + + @builtintypeobject + 20 + + + @declconstobject + 8488 + + + @builtinconstobject + 4 + + + @declvarobject + 50364 + + + @declfunctionobject + 17254 + + + @builtinfunctionobject + 18 + + + @labelobject + 49 + @invalidtype 1 @@ -236,46 +272,6 @@ @complexliteraltype 0 - - @pkgobject - 3468 - - - @decltypeobject - 3499 - - - @builtintypeobject - 20 - - - @declconstobject - 8488 - - - @builtinconstobject - 4 - - - @package - 340 - - - @declvarobject - 50364 - - - @declfunctionobject - 17254 - - - @builtinfunctionobject - 18 - - - @labelobject - 49 - @declstmt 1454 @@ -424,6 +420,10 @@ @andassignstmt 0 + + @package + 340 + @modcommentblock 3 @@ -444,6 +444,22 @@ @modrparen 2 + + @unknownerror + 0 + + + @listerror + 0 + + + @parseerror + 0 + + + @typeerror + 0 + @ident 237316 @@ -9789,5 +9805,817 @@ + + errors + 0 + + + id + 0 + + + kind + 0 + + + msg + 0 + + + rawpos + 0 + + + file + 0 + + + line + 0 + + + col + 0 + + + package + 0 + + + idx + 0 + + + + + id + kind + + + 12 + + + 1 + 2 + 1 + + + + + + + id + msg + + + 12 + + + 1 + 2 + 1 + + + + + + + id + rawpos + + + 12 + + + 1 + 2 + 1 + + + + + + + id + file + + + 12 + + + 1 + 2 + 1 + + + + + + + id + line + + + 12 + + + 1 + 2 + 1 + + + + + + + id + col + + + 12 + + + 1 + 2 + 1 + + + + + + + id + package + + + 12 + + + 1 + 2 + 1 + + + + + + + id + idx + + + 12 + + + 1 + 2 + 1 + + + + + + + kind + id + + + 12 + + + + + + kind + msg + + + 12 + + + + + + kind + rawpos + + + 12 + + + + + + kind + file + + + 12 + + + + + + kind + line + + + 12 + + + + + + kind + col + + + 12 + + + + + + kind + package + + + 12 + + + + + + kind + idx + + + 12 + + + + + + msg + id + + + 12 + + + + + + msg + kind + + + 12 + + + + + + msg + rawpos + + + 12 + + + + + + msg + file + + + 12 + + + + + + msg + line + + + 12 + + + + + + msg + col + + + 12 + + + + + + msg + package + + + 12 + + + + + + msg + idx + + + 12 + + + + + + rawpos + id + + + 12 + + + + + + rawpos + kind + + + 12 + + + + + + rawpos + msg + + + 12 + + + + + + rawpos + file + + + 12 + + + + + + rawpos + line + + + 12 + + + + + + rawpos + col + + + 12 + + + + + + rawpos + package + + + 12 + + + + + + rawpos + idx + + + 12 + + + + + + file + id + + + 12 + + + + + + file + kind + + + 12 + + + + + + file + msg + + + 12 + + + + + + file + rawpos + + + 12 + + + + + + file + line + + + 12 + + + + + + file + col + + + 12 + + + + + + file + package + + + 12 + + + + + + file + idx + + + 12 + + + + + + line + id + + + 12 + + + + + + line + kind + + + 12 + + + + + + line + msg + + + 12 + + + + + + line + rawpos + + + 12 + + + + + + line + file + + + 12 + + + + + + line + col + + + 12 + + + + + + line + package + + + 12 + + + + + + line + idx + + + 12 + + + + + + col + id + + + 12 + + + + + + col + kind + + + 12 + + + + + + col + msg + + + 12 + + + + + + col + rawpos + + + 12 + + + + + + col + file + + + 12 + + + + + + col + line + + + 12 + + + + + + col + package + + + 12 + + + + + + col + idx + + + 12 + + + + + + package + id + + + 12 + + + + + + package + kind + + + 12 + + + + + + package + msg + + + 12 + + + + + + package + rawpos + + + 12 + + + + + + package + file + + + 12 + + + + + + package + line + + + 12 + + + + + + package + col + + + 12 + + + + + + package + idx + + + 12 + + + + + + idx + id + + + 12 + + + + + + idx + kind + + + 12 + + + + + + idx + msg + + + 12 + + + + + + idx + rawpos + + + 12 + + + + + + idx + file + + + 12 + + + + + + idx + line + + + 12 + + + + + + idx + col + + + 12 + + + + + + idx + package + + + 12 + + + + + + diff --git a/ql/src/go.qll b/ql/src/go.qll index 0bce4c7d6b7..4438f9be89b 100644 --- a/ql/src/go.qll +++ b/ql/src/go.qll @@ -7,6 +7,7 @@ import semmle.go.AST import semmle.go.Comments import semmle.go.Concepts import semmle.go.Decls +import semmle.go.Errors import semmle.go.Expr import semmle.go.Files import semmle.go.GoMod @@ -16,18 +17,23 @@ import semmle.go.Scopes import semmle.go.Stmt import semmle.go.StringOps import semmle.go.Types +import semmle.go.Util import semmle.go.controlflow.BasicBlocks import semmle.go.controlflow.ControlFlowGraph import semmle.go.controlflow.IR import semmle.go.dataflow.DataFlow import semmle.go.dataflow.GlobalValueNumbering -import semmle.go.dataflow.TaintTracking import semmle.go.dataflow.SSA +import semmle.go.dataflow.TaintTracking +import semmle.go.frameworks.Email import semmle.go.frameworks.HTTP -import semmle.go.frameworks.SystemCommandExecutors +import semmle.go.frameworks.Macaron +import semmle.go.frameworks.Mux +import semmle.go.frameworks.NoSQL import semmle.go.frameworks.SQL -import semmle.go.frameworks.XPath import semmle.go.frameworks.Stdlib +import semmle.go.frameworks.SystemCommandExecutors import semmle.go.frameworks.Testing +import semmle.go.frameworks.WebSocket +import semmle.go.frameworks.XPath import semmle.go.security.FlowSources -import semmle.go.Util diff --git a/ql/src/localDefinitions.ql b/ql/src/localDefinitions.ql new file mode 100644 index 00000000000..9618d8f8637 --- /dev/null +++ b/ql/src/localDefinitions.ql @@ -0,0 +1,19 @@ +/** + * @name Jump-to-definition links + * @description Generates use-definition pairs that provide the data + * for jump-to-definition in the code viewer. + * @kind definitions + * @id go/ide-jump-to-definition + * @tags ide-contextual-queries/local-definitions + */ + +import go + +external string selectedSourceFile(); + +cached +File getEncodedFile(string name) { result.getAbsolutePath().replaceAll(":", "_") = name } + +from Ident def, Ident use, Entity e +where use.uses(e) and def.declares(e) and use.getFile() = getEncodedFile(selectedSourceFile()) +select use, def, "V" diff --git a/ql/src/localReferences.ql b/ql/src/localReferences.ql new file mode 100644 index 00000000000..ddd556fc8b3 --- /dev/null +++ b/ql/src/localReferences.ql @@ -0,0 +1,19 @@ +/** + * @name Find-references links + * @description Generates use-definition pairs that provide the data + * for find-references in the code viewer. + * @kind definitions + * @id go/ide-find-references + * @tags ide-contextual-queries/local-references + */ + +import go + +external string selectedSourceFile(); + +cached +File getEncodedFile(string name) { result.getAbsolutePath().replaceAll(":", "_") = name } + +from Ident def, Ident use, Entity e +where use.uses(e) and def.declares(e) and def.getFile() = getEncodedFile(selectedSourceFile()) +select use, def, "V" diff --git a/ql/src/semmle/go/Comments.qll b/ql/src/semmle/go/Comments.qll index d4cdfefc362..428b62e142d 100644 --- a/ql/src/semmle/go/Comments.qll +++ b/ql/src/semmle/go/Comments.qll @@ -6,6 +6,14 @@ import go /** * A code comment. + * + * Examples: + * + *
    + * // a line comment
    + * /* a block
    + *   comment */
    + * 
    */ class Comment extends @comment, AstNode { /** @@ -24,6 +32,21 @@ class Comment extends @comment, AstNode { /** * A comment group, that is, a sequence of comments without any intervening tokens or * empty lines. + * + * Examples: + * + *
    + * // a line comment
    + * // another line comment
    + *
    + * // a line comment
    + * /* a block
    + *   comment */
    +* 
    + * /* a block
    + * comment */
    + * /* another block comment */
    + * 
    */ class CommentGroup extends @comment_group, AstNode { /** Gets the `i`th comment in this group (0-based indexing). */ @@ -39,7 +62,23 @@ class CommentGroup extends @comment_group, AstNode { } /** - * A program element to which a documentation comment group may be attached. + * A program element to which a documentation comment group may be attached: + * a file, a field, a specifier, a generic declaration, a function declaration + * or a go.mod expression. + * + * Examples: + * + * ```go + * // function documentation + * func double(x int) int { return 2 * x } + * + * // generic declaration documentation + * const ( + * // specifier documentation + * size int64 = 1024 + * eof = -1 // not specifier documentation + * ) + * ``` */ class Documentable extends AstNode, @documentable { /** Gets the documentation comment group attached to this element, if any. */ @@ -48,6 +87,20 @@ class Documentable extends AstNode, @documentable { /** * A comment group that is attached to a program element as documentation. + * + * Examples: + * + * ```go + * // function documentation + * func double(x int) int { return 2 * x } + * + * // generic declaration documentation + * const ( + * // specifier documentation + * size int64 = 1024 + * eof = -1 // not specifier documentation + * ) + * ``` */ class DocComment extends CommentGroup { Documentable node; @@ -60,21 +113,47 @@ class DocComment extends CommentGroup { /** * A single-line comment starting with `//`. + * + * Examples: + * + * ```go + * // Single line comment + * ``` */ class SlashSlashComment extends @slashslashcomment, Comment { } /** * A block comment starting with `/*` and ending with */. + * + * Examples: + * + *
    + * /* a block
    + *   comment */
    + * 
    */ class SlashStarComment extends @slashstarcomment, Comment { } /** * A single-line comment starting with `//`. + * + * Examples: + * + * ```go + * // Single line comment + * ``` */ class LineComment = SlashSlashComment; /** * A block comment starting with `/*` and ending with */. + * + * Examples: + * + *
    + * /* a block
    + *   comment */
    + * 
    */ class BlockComment = SlashStarComment; @@ -96,6 +175,13 @@ private Comment getInitialComment(File f, int i) { /** * A build constraint comment of the form `// +build ...`. + * + * Examples: + * + * ```go + * // +build darwin freebsd netbsd openbsd + * // +build !linux + * ``` */ class BuildConstraintComment extends LineComment { BuildConstraintComment() { diff --git a/ql/src/semmle/go/Concepts.qll b/ql/src/semmle/go/Concepts.qll index b43d75cf8cb..81cd1b1ff72 100644 --- a/ql/src/semmle/go/Concepts.qll +++ b/ql/src/semmle/go/Concepts.qll @@ -11,7 +11,7 @@ import semmle.go.dataflow.FunctionInputsAndOutputs * A data-flow node that executes an operating system command, * for instance by spawning a new process. * - * Extends this class to refine existing API models. If you want to model new APIs, + * Extend this class to refine existing API models. If you want to model new APIs, * extend `SystemCommandExecution::Range` instead. */ class SystemCommandExecution extends DataFlow::Node { @@ -366,8 +366,8 @@ module HTTP { * extend `HTTP::ResponseWriter` instead. */ abstract class Range extends Variable { - /** Gets a data-flow node that represents this response writer. */ - DataFlow::Node getANode() { result = this.getARead().getASuccessor*() } + /** Gets a data-flow node that is a use of this response writer. */ + abstract DataFlow::Node getANode(); } } @@ -391,7 +391,7 @@ module HTTP { /** Gets a redirect that is sent in this HTTP response. */ Redirect getARedirect() { result.getResponseWriter() = this } - /** Gets a data-flow node that represents this response writer. */ + /** Gets a data-flow node that is a use of this response writer. */ DataFlow::Node getANode() { result = self.getANode() } } diff --git a/ql/src/semmle/go/Errors.qll b/ql/src/semmle/go/Errors.qll new file mode 100644 index 00000000000..3c8a8b9adea --- /dev/null +++ b/ql/src/semmle/go/Errors.qll @@ -0,0 +1,53 @@ +/** Provides classes for working with Go frontend errors recorded during extraction. */ + +import go + +/** + * An error reported by the Go frontend during extraction. + */ +class Error extends @error { + /** Gets the message associated with this error. */ + string getMessage() { errors(this, _, result, _, _, _, _, _, _) } + + /** Gets the raw position reported by the frontend for this error. */ + string getRawPosition() { errors(this, _, _, result, _, _, _, _, _) } + + /** Gets the package in which this error was reported. */ + Package getPackage() { errors(this, _, _, _, _, _, _, result, _) } + + /** Gets the index of this error among all errors reported for the same package. */ + int getIndex() { errors(this, _, _, _, _, _, _, _, result) } + + /** Gets the file in which this error was reported, if it can be determined. */ + File getFile() { hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [LGTM locations](https://lgtm.com/help/ql/locations). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + errors(this, _, _, _, filepath, startline, startcolumn, _, _) and + endline = startline and + endcolumn = startcolumn + } + + /** Gets a textual representation of this error. */ + string toString() { result = getMessage() } +} + +/** An error reported by an unknown part of the Go frontend. */ +class UnknownError extends Error, @unknownerror { } + +/** An error reported by the Go frontend driver. */ +class ListError extends Error, @listerror { } + +/** An error reported by the Go parser. */ +class ParseError extends Error, @parseerror { } + +/** An error reported by the Go type checker. */ +class TypeError extends Error, @typeerror { } diff --git a/ql/src/semmle/go/Expr.qll b/ql/src/semmle/go/Expr.qll index 1c6ccb32eee..b4928892a59 100644 --- a/ql/src/semmle/go/Expr.qll +++ b/ql/src/semmle/go/Expr.qll @@ -6,6 +6,13 @@ import go /** * An expression. + * + * Examples: + * + * ```go + * x + 1 + * y < 0 + * ``` */ class Expr extends @expr, ExprParent { /** @@ -138,6 +145,13 @@ class Expr extends @expr, ExprParent { /** * A bad expression, that is, an expression that could not be parsed. + * + * Examples: + * + * ```go + * x + + * y < + * ``` */ class BadExpr extends @badexpr, Expr { override string toString() { result = "bad expression" } @@ -145,6 +159,12 @@ class BadExpr extends @badexpr, Expr { /** * An identifier. + * + * Examples: + * + * ```go + * x + * ``` */ class Ident extends @ident, Expr { /** Gets the name of this identifier. */ @@ -164,6 +184,12 @@ class Ident extends @ident, Expr { /** * The blank identifier `_`. + * + * Examples: + * + * ```go + * _ + * ``` */ class BlankIdent extends Ident { BlankIdent() { getName() = "_" } @@ -172,6 +198,12 @@ class BlankIdent extends Ident { /** * An ellipsis expression, representing either the `...` type in a parameter list or * the `...` length in an array type. + * + * Examples: + * + * ```go + * ... + * ``` */ class Ellipsis extends @ellipsis, Expr { /** Gets the operand of this ellipsis expression. */ @@ -182,6 +214,13 @@ class Ellipsis extends @ellipsis, Expr { /** * A literal expression of basic type. + * + * Examples: + * + * ```go + * 1 + * "hello" + * ``` */ class BasicLit extends @basiclit, Expr { /** Gets the value of this literal expressed as a string. */ @@ -204,21 +243,53 @@ class BasicLit extends @basiclit, Expr { /** * An integer literal. + * + * Examples: + * + * ```go + * 256 + * ``` */ class IntLit extends @intlit, BasicLit { } /** * A floating-point literal. + * + * Examples: + * + * ```go + * 2.71828 + * ``` */ class FloatLit extends @floatlit, BasicLit { } /** * An imaginary literal. + * + * Examples: + * + * ```go + * 2i + * 2.7i + * ``` */ class ImagLit extends @imaglit, BasicLit { } /** * A rune literal. + * + * Examples: + * + * ```go + * 'a' + * 'ä' + * '本' + * '\377' + * '\xff' + * '\u12e4' + * '\U00101234' + * '\n' + * ``` */ class CharLit extends @charlit, BasicLit { // use the constant value of the literal as the string value, as the value we get from the @@ -230,11 +301,23 @@ class RuneLit = CharLit; /** * A string literal. + * + * Examples: + * + * ```go + * "hello world" + * ``` */ class StringLit extends @stringlit, BasicLit { } /** * A function literal. + * + * Examples: + * + * ```go + * func(x, y int) int { return x + y } + * ``` */ class FuncLit extends @funclit, Expr, StmtParent, FuncDef { override FuncTypeExpr getTypeExpr() { result = getChildExpr(0) } @@ -251,6 +334,13 @@ class FuncLit extends @funclit, Expr, StmtParent, FuncDef { /** * A composite literal + * + * Examples: + * + * ```go + * Point3D{0.5, -0.5, 0.5} + * map[string]int{"A": 1, "B": 2} + * ``` */ class CompositeLit extends @compositelit, Expr { /** Gets the expression representing the type of this composite literal. */ @@ -291,6 +381,12 @@ class CompositeLit extends @compositelit, Expr { /** * A map literal. + * + * Examples: + * + * ```go + * map[string]int{"A": 1, "B": 2} + * ``` */ class MapLit extends CompositeLit { MapType mt; @@ -306,6 +402,14 @@ class MapLit extends CompositeLit { /** * A struct literal. + * + * Examples: + * + * ```go + * Point3D{0.5, -0.5, 0.5} + * Point3D{y: 1} + * Point3D{} + * ``` */ class StructLit extends CompositeLit { StructType st; @@ -318,6 +422,12 @@ class StructLit extends CompositeLit { /** * A parenthesized expression. + * + * Examples: + * + * ```go + * (x + y) + * ``` */ class ParenExpr extends @parenexpr, Expr { /** Gets the expression between parentheses. */ @@ -332,6 +442,12 @@ class ParenExpr extends @parenexpr, Expr { /** * A selector expression, that is, a base expression followed by a selector. + * + * Examples: + * + * ```go + * x.f + * ``` */ class SelectorExpr extends @selectorexpr, Expr { /** Gets the base of this selector expression. */ @@ -356,6 +472,12 @@ class SelectorExpr extends @selectorexpr, Expr { /** * An index expression, that is, a base expression followed by an index. + * + * Examples: + * + * ```go + * a[i] + * ``` */ class IndexExpr extends @indexexpr, Expr { /** Gets the base of this index expression. */ @@ -371,6 +493,13 @@ class IndexExpr extends @indexexpr, Expr { /** * A slice expression, that is, a base expression followed by slice indices. + * + * Examples: + * + * ```go + * a[1:3] + * a[1:3:5] + * ``` */ class SliceExpr extends @sliceexpr, Expr { /** Gets the base of this slice expression. */ @@ -390,6 +519,12 @@ class SliceExpr extends @sliceexpr, Expr { /** * A type assertion expression. + * + * Examples: + * + * ```go + * x.(T) + * ``` */ class TypeAssertExpr extends @typeassertexpr, Expr { /** Gets the base expression whose type is being asserted. */ @@ -411,11 +546,25 @@ class TypeAssertExpr extends @typeassertexpr, Expr { * * In most cases, the subclasses `CallExpr` and `ConversionExpr` should be used * instead. + * + * Examples: + * + * ```go + * f(x) + * g(a, b...) + * []byte("x") + * ``` */ class CallOrConversionExpr extends @callorconversionexpr, Expr { } /** * A type conversion expression. + * + * Examples: + * + * ```go + * []byte("x") + * ``` */ class ConversionExpr extends CallOrConversionExpr { ConversionExpr() { isTypeExprBottomUp(getChildExpr(0)) } @@ -438,6 +587,13 @@ class ConversionExpr extends CallOrConversionExpr { * * On snapshots with incomplete type information, type conversions may be misclassified * as function call expressions. + * + * Examples: + * + * ```go + * f(x) + * g(a, b...) + * ``` */ class CallExpr extends CallOrConversionExpr { CallExpr() { exists(Expr callee | callee = getChildExpr(0) | not isTypeExprBottomUp(callee)) } @@ -484,6 +640,12 @@ class CallExpr extends CallOrConversionExpr { /** * A star expression. + * + * Examples: + * + * ```go + * *x + * ``` */ class StarExpr extends @starexpr, Expr { /** Gets the base expression of this star expression. */ @@ -496,6 +658,12 @@ class StarExpr extends @starexpr, Expr { /** * A key-value pair in a composite literal. + * + * Examples: + * + * ```go + * "A": 1 + * ``` */ class KeyValueExpr extends @keyvalueexpr, Expr { /** Gets the key expression of this key-value pair. */ @@ -512,6 +680,12 @@ class KeyValueExpr extends @keyvalueexpr, Expr { /** * An expression representing an array type. + * + * Examples: + * + * ```go + * [5]int + * ``` */ class ArrayTypeExpr extends @arraytypeexpr, Expr { /** Gets the length expression of this array type. */ @@ -525,6 +699,12 @@ class ArrayTypeExpr extends @arraytypeexpr, Expr { /** * An expression representing a struct type. + * + * Examples: + * + * ```go + * struct {x, y int; z float32} + * ``` */ class StructTypeExpr extends @structtypeexpr, Expr { /** Gets the `i`th field declared in this struct type expression (0-based). */ @@ -541,6 +721,12 @@ class StructTypeExpr extends @structtypeexpr, Expr { /** * An expression representing a function type. + * + * Examples: + * + * ```go + * func(a, b int, c float32) (float32, bool) + * ``` */ class FuncTypeExpr extends @functypeexpr, Expr, ScopeNode { /** Gets the `i`th parameter of this function type (0-based). */ @@ -575,6 +761,12 @@ class FuncTypeExpr extends @functypeexpr, Expr, ScopeNode { /** * An expression representing an interface type. + * + * Examples: + * + * ```go + * interface { Read(p []byte) (n int, err error); Close() error} + * ``` */ class InterfaceTypeExpr extends @interfacetypeexpr, Expr { /** Gets the `i`th method specification of this interface type. */ @@ -594,6 +786,12 @@ class InterfaceTypeExpr extends @interfacetypeexpr, Expr { /** * An expression representing a map type. + * + * Examples: + * + * ```go + * map[string]int + * ``` */ class MapTypeExpr extends @maptypeexpr, Expr { /** Gets the expression representing the key type of this map type. */ @@ -613,6 +811,13 @@ class MapTypeExpr extends @maptypeexpr, Expr { /** * An expression with a (unary or binary) operator. + * + * Examples: + * + * ```go + * a * b + * -c + * ``` */ class OperatorExpr extends @operatorexpr, Expr { /** Gets the operator of this expression. */ @@ -624,21 +829,50 @@ class OperatorExpr extends @operatorexpr, Expr { /** * An expression with an arithmetic operator like `-` or `/`. + * + * Examples: + * + * ```go + * x - y + * u / v + * ``` */ class ArithmeticExpr extends @arithmeticexpr, OperatorExpr { } /** * An expression with a logical operator like `!` or `&&`. + * + * Examples: + * + * ```go + * !a + * b && c + * ``` */ class LogicalExpr extends @logicalexpr, OperatorExpr { } /** * An expression with a bitwise operator such as `^` or `|`. + * + * Examples: + * + * ```go + * x ^ y + * a | b + * ``` */ class BitwiseExpr extends @bitwiseexpr, OperatorExpr { } /** * An expression with a unary operator. + * + * Examples: + * + * ```go + * +7 + * -2.5i + * !x + * ``` */ class UnaryExpr extends @unaryexpr, OperatorExpr { /** Gets the operand of this unary expression. */ @@ -655,21 +889,46 @@ class UnaryExpr extends @unaryexpr, OperatorExpr { /** * An expression with a unary arithmetic operator, that is, unary `-` or `+`. + * + * Examples: + * + * ```go + * +7 + * -2.5i + * ``` */ class ArithmeticUnaryExpr extends @arithmeticunaryexpr, ArithmeticExpr, UnaryExpr { } /** * An expression with a unary logical operator, that is, `!`. + * + * Examples: + * + * ```go + * !x + * ``` */ class LogicalUnaryExpr extends @logicalunaryexpr, LogicalExpr, UnaryExpr { } /** * An expression with a unary bitwise operator, that is, `^`. + * + * Examples: + * + * ```go + * ^x + * ``` */ class BitwiseUnaryExpr extends @bitwiseunaryexpr, BitwiseExpr, UnaryExpr { } /** * A unary plus expression using `+`. + * + * Examples: + * + * ```go + * +7 + * ``` */ class PlusExpr extends @plusexpr, ArithmeticUnaryExpr { override string getOperator() { result = "+" } @@ -677,6 +936,12 @@ class PlusExpr extends @plusexpr, ArithmeticUnaryExpr { /** * A unary minus expression using `-`. + * + * Examples: + * + * ```go + * -2.5i + * ``` */ class MinusExpr extends @minusexpr, ArithmeticUnaryExpr { override string getOperator() { result = "-" } @@ -684,6 +949,12 @@ class MinusExpr extends @minusexpr, ArithmeticUnaryExpr { /** * A unary "not" expression using `!`. + * + * Examples: + * + * ```go + * !x + * ``` */ class NotExpr extends @notexpr, LogicalUnaryExpr { override string getOperator() { result = "!" } @@ -691,6 +962,12 @@ class NotExpr extends @notexpr, LogicalUnaryExpr { /** * A unary complement expression using `^`. + * + * Examples: + * + * ```go + * ^x + * ``` */ class ComplementExpr extends @complementexpr, BitwiseUnaryExpr { override string getOperator() { result = "^" } @@ -709,6 +986,12 @@ class DerefExpr extends @derefexpr, UnaryExpr { /** * A unary address-of expression using `&`. + * + * Examples: + * + * ```go + * &x + * ``` */ class AddressExpr extends @addressexpr, UnaryExpr { override predicate mayHaveOwnSideEffects() { any() } @@ -718,6 +1001,12 @@ class AddressExpr extends @addressexpr, UnaryExpr { /** * A unary receive expression using `<-`. + * + * Examples: + * + * ```go + * <-chan + * ``` */ class RecvExpr extends @arrowexpr, UnaryExpr { override predicate mayHaveOwnSideEffects() { any() } @@ -727,6 +1016,14 @@ class RecvExpr extends @arrowexpr, UnaryExpr { /** * A binary expression. + * + * Examples: + * + * ```go + * a * b + * a || b + * b != c + * ``` */ class BinaryExpr extends @binaryexpr, OperatorExpr { /** Gets the left operand of this binary expression. */ @@ -754,31 +1051,69 @@ class BinaryExpr extends @binaryexpr, OperatorExpr { /** * A binary arithmetic expression, that is, `+`, `-`, `*`, `/` or `%`. + * + * Examples: + * + * ```go + * a * b + * ``` */ class ArithmeticBinaryExpr extends @arithmeticbinaryexpr, ArithmeticExpr, BinaryExpr { } /** * A binary logical expression, that is, `&&` or `||`. + * + * Examples: + * + * ```go + * a || b + * ``` */ class LogicalBinaryExpr extends @logicalbinaryexpr, LogicalExpr, BinaryExpr { } /** * A binary bitwise expression, that is, `<<`, `>>`, `|`, `^`, `&` or `&^`. + * + * Examples: + * + * ```go + * a << i + * b ^ c + * ``` */ class BitwiseBinaryExpr extends @bitwisebinaryexpr, BitwiseExpr, BinaryExpr { } /** * A shift expression, that is, `<<` or `>>`. + * + * Examples: + * + * ```go + * a << i + * ``` */ class ShiftExpr extends @shiftexpr, BitwiseBinaryExpr { } /** * A comparison expression, that is, `==`, `!=`, `<`, `<=`, `>=` or `>`. + * + * Examples: + * + * ```go + * a != b + * c > d + * ``` */ class ComparisonExpr extends @comparison, BinaryExpr { } /** * An equality test, that is, `==` or `!=`. + * + * Examples: + * + * ```go + * a != b + * ``` */ class EqualityTestExpr extends @equalitytest, ComparisonExpr { /** Gets the polarity of this equality test, that is, `true` for `==` and `false` for `!=`. */ @@ -787,6 +1122,12 @@ class EqualityTestExpr extends @equalitytest, ComparisonExpr { /** * A relational comparison, that is, `<`, `<=`, `>=` or `>`. + * + * Examples: + * + * ```go + * c > d + * ``` */ class RelationalComparisonExpr extends @relationalcomparison, ComparisonExpr { /** Holds if this comparison is strict, that is, it implies inequality. */ @@ -807,6 +1148,12 @@ class RelationalComparisonExpr extends @relationalcomparison, ComparisonExpr { /** * A logical-or expression using `||`. + * + * Examples: + * + * ```go + * a || b + * ``` */ class LorExpr extends @lorexpr, LogicalBinaryExpr { override string getOperator() { result = "||" } @@ -816,6 +1163,12 @@ class LogOrExpr = LorExpr; /** * A logical-and expression using `&&`. + * + * Examples: + * + * ```go + * a && b + * ``` */ class LandExpr extends @landexpr, LogicalBinaryExpr { override string getOperator() { result = "&&" } @@ -825,6 +1178,12 @@ class LogAndExpr = LandExpr; /** * An equality test using `==`. + * + * Examples: + * + * ```go + * a == b + * ``` */ class EqlExpr extends @eqlexpr, EqualityTestExpr { override string getOperator() { result = "==" } @@ -836,6 +1195,12 @@ class EqExpr = EqlExpr; /** * An inequality test using `!=`. + * + * Examples: + * + * ```go + * a != b + * ``` */ class NeqExpr extends @neqexpr, EqualityTestExpr { override string getOperator() { result = "!=" } @@ -845,6 +1210,12 @@ class NeqExpr extends @neqexpr, EqualityTestExpr { /** * A less-than test using `<`. + * + * Examples: + * + * ```go + * a < b + * ``` */ class LssExpr extends @lssexpr, RelationalComparisonExpr { override string getOperator() { result = "<" } @@ -860,6 +1231,12 @@ class LTExpr = LssExpr; /** * A less-than-or-equal test using `<=`. + * + * Examples: + * + * ```go + * a <= b + * ``` */ class LeqExpr extends @leqexpr, RelationalComparisonExpr { override string getOperator() { result = "<=" } @@ -873,6 +1250,12 @@ class LEExpr = LeqExpr; /** * A greater-than test using `>`. + * + * Examples: + * + * ```go + * a > b + * ``` */ class GtrExpr extends @gtrexpr, RelationalComparisonExpr { override string getOperator() { result = ">" } @@ -888,6 +1271,12 @@ class GTExpr = GtrExpr; /** * A greater-than-or-equal test using `>=`. + * + * Examples: + * + * ```go + * a >= b + * ``` */ class GeqExpr extends @geqexpr, RelationalComparisonExpr { override string getOperator() { result = ">=" } @@ -901,6 +1290,12 @@ class GEExpr = GeqExpr; /** * An addition expression using `+`. + * + * Examples: + * + * ```go + * a + b + * ``` */ class AddExpr extends @addexpr, ArithmeticBinaryExpr { override string getOperator() { result = "+" } @@ -908,6 +1303,12 @@ class AddExpr extends @addexpr, ArithmeticBinaryExpr { /** * A subtraction expression using `-`. + * + * Examples: + * + * ```go + * a - b + * ``` */ class SubExpr extends @subexpr, ArithmeticBinaryExpr { override string getOperator() { result = "-" } @@ -915,6 +1316,12 @@ class SubExpr extends @subexpr, ArithmeticBinaryExpr { /** * A bitwise or expression using `|`. + * + * Examples: + * + * ```go + * a | b + * ``` */ class OrExpr extends @orexpr, BitwiseBinaryExpr { override string getOperator() { result = "|" } @@ -924,6 +1331,12 @@ class BitOrExpr = OrExpr; /** * An exclusive-or expression using `^`. + * + * Examples: + * + * ```go + * a ^ b + * ``` */ class XorExpr extends @xorexpr, BitwiseBinaryExpr { override string getOperator() { result = "^" } @@ -931,6 +1344,12 @@ class XorExpr extends @xorexpr, BitwiseBinaryExpr { /** * A multiplication expression using `*`. + * + * Examples: + * + * ```go + * a * b + * ``` */ class MulExpr extends @mulexpr, ArithmeticBinaryExpr { override string getOperator() { result = "*" } @@ -938,6 +1357,12 @@ class MulExpr extends @mulexpr, ArithmeticBinaryExpr { /** * A divison or quotient expression using `/`. + * + * Examples: + * + * ```go + * a / b + * ``` */ class QuoExpr extends @quoexpr, ArithmeticBinaryExpr { override predicate mayHaveOwnSideEffects() { any() } @@ -949,6 +1374,12 @@ class DivExpr = QuoExpr; /** * A remainder or modulo expression using `%`. + * + * Examples: + * + * ```go + * a % b + * ``` */ class RemExpr extends @remexpr, ArithmeticBinaryExpr { override string getOperator() { result = "%" } @@ -958,6 +1389,12 @@ class ModExpr = RemExpr; /** * A left-shift expression using `<<`. + * + * Examples: + * + * ```go + * a << i + * ``` */ class ShlExpr extends @shlexpr, ShiftExpr { override string getOperator() { result = "<<" } @@ -967,6 +1404,12 @@ class LShiftExpr = ShlExpr; /** * A right-shift expression using `>>`. + * + * Examples: + * + * ```go + * a >> i + * ``` */ class ShrExpr extends @shrexpr, ShiftExpr { override string getOperator() { result = ">>" } @@ -976,6 +1419,12 @@ class RShiftExpr = ShrExpr; /** * A bitwise and-expression using `&`. + * + * Examples: + * + * ```go + * a & b + * ``` */ class AndExpr extends @andexpr, BitwiseBinaryExpr { override string getOperator() { result = "&" } @@ -985,6 +1434,12 @@ class BitAndExpr = AndExpr; /** * A bitwise and-not expression using `&^`. + * + * Examples: + * + * ```go + * a &^ b + * ``` */ class AndNotExpr extends @andnotexpr, BitwiseBinaryExpr { override string getOperator() { result = "&^" } @@ -992,6 +1447,14 @@ class AndNotExpr extends @andnotexpr, BitwiseBinaryExpr { /** * An expression representing a channel type. + * + * Examples: + * + * ```go + * chan float64 + * chan<- bool + * <-chan int + * ``` */ class ChanTypeExpr extends @chantypeexpr, Expr { /** @@ -1010,6 +1473,12 @@ class ChanTypeExpr extends @chantypeexpr, Expr { /** * An expression representing a send-only channel type. + * + * Examples: + * + * ```go + * chan<- bool + * ``` */ class SendChanTypeExpr extends @sendchantypeexpr, ChanTypeExpr { override predicate canSend() { any() } @@ -1017,6 +1486,12 @@ class SendChanTypeExpr extends @sendchantypeexpr, ChanTypeExpr { /** * An expression representing a receive-only channel type. + * + * Examples: + * + * ```go + * <-chan int + * ``` */ class RecvChanTypeExpr extends @recvchantypeexpr, ChanTypeExpr { override predicate canReceive() { any() } @@ -1024,6 +1499,12 @@ class RecvChanTypeExpr extends @recvchantypeexpr, ChanTypeExpr { /** * An expression representing a duplex channel type that can both send and receive data. + * + * Examples: + * + * ```go + * chan float64 + * ``` */ class SendRecvChanTypeExpr extends @sendrcvchantypeexpr, ChanTypeExpr { override predicate canSend() { any() } @@ -1033,6 +1514,18 @@ class SendRecvChanTypeExpr extends @sendrcvchantypeexpr, ChanTypeExpr { /** * A (possibly qualified) name referring to a package, type, constant, variable, function or label. + * + * Examples: + * + * ```go + * Println + * fmt.Println + * fmt + * int + * T + * x + * Outerloop + * ``` */ class Name extends Expr { Entity target; @@ -1043,13 +1536,37 @@ class Name extends Expr { Entity getTarget() { result = target } } -/** A simple (that is, unqualified) name. */ +/** + * A simple (that is, unqualified) name. + * + * Examples: + * + * ```go + * Println + * ``` + */ class SimpleName extends Name, Ident { } -/** A qualified name. */ +/** + * A qualified name. + * + * Examples: + * + * ```go + * fmt.Println + * ``` + */ class QualifiedName extends Name, SelectorExpr { } -/** A name referring to an imported package. */ +/** + * A name referring to an imported package. + * + * Examples: + * + * ```go + * fmt + * ``` + */ class PackageName extends Name { override PackageEntity target; @@ -1057,7 +1574,16 @@ class PackageName extends Name { override PackageEntity getTarget() { result = target } } -/** A name referring to a type. */ +/** + * A name referring to a type. + * + * Examples: + * + * ```go + * int + * T + * ``` + */ class TypeName extends Name { override TypeEntity target; @@ -1065,7 +1591,17 @@ class TypeName extends Name { override TypeEntity getTarget() { result = target } } -/** A name referring to a value, that is, a constant, variable or function. */ +/** + * A name referring to a value, that is, a constant, variable or function. + * + * Examples: + * + * ```go + * c + * f + * x + * ``` + */ class ValueName extends Name { override ValueEntity target; @@ -1073,7 +1609,15 @@ class ValueName extends Name { override ValueEntity getTarget() { result = target } } -/** A name referring to a constant. */ +/** + * A name referring to a constant. + * + * Examples: + * + * ```go + * c + * ``` + */ class ConstantName extends ValueName { override Constant target; @@ -1094,7 +1638,15 @@ class ConstantName extends ValueName { } } -/** A name referring to a variable. */ +/** + * A name referring to a variable. + * + * Examples: + * + * ```go + * x + * ``` + */ class VariableName extends ValueName { override Variable target; @@ -1102,7 +1654,15 @@ class VariableName extends ValueName { override Variable getTarget() { result = target } } -/** A name referring to a function. */ +/** + * A name referring to a function. + * + * Examples: + * + * ```go + * f + * ``` + */ class FunctionName extends ValueName { override Function target; @@ -1110,7 +1670,15 @@ class FunctionName extends ValueName { override Function getTarget() { result = target } } -/** A name referring to a statement label. */ +/** + * A name referring to a statement label. + * + * Examples: + * + * ```go + * Outerloop + * ``` + */ class LabelName extends Name { override Label target; @@ -1190,7 +1758,16 @@ private predicate isTypeExprTopDown(Expr e) { e = any(Ellipsis ell | isTypeExprTopDown(ell)).getOperand() } -/** An expression referring to a type. */ +/** + * An expression referring to a type. + * + * Examples: + * + * ```go + * int + * func + * ``` + */ class TypeExpr extends Expr { TypeExpr() { isTypeExprBottomUp(this) or @@ -1198,7 +1775,16 @@ class TypeExpr extends Expr { } } -/** An expression referring to a memory location. */ +/** + * An expression referring to a memory location. + * + * Examples: + * + * ```go + * a[i] + * *p + * ``` + */ class ReferenceExpr extends Expr { ReferenceExpr() { (this instanceof Ident or this instanceof SelectorExpr) and @@ -1250,7 +1836,16 @@ class ReferenceExpr extends Expr { } } -/** An expression that refers to a value (as opposed to a package, a type or a statement label). */ +/** + * An expression that refers to a value (as opposed to a package, a type or a statement label). + * + * Examples: + * + * ```go + * x + y + * f(x) + * ``` + */ class ValueExpr extends Expr { ValueExpr() { this.(ReferenceExpr).isRvalue() or diff --git a/ql/src/semmle/go/Packages.qll b/ql/src/semmle/go/Packages.qll index 6b24a3cc6ab..a88cc2a7368 100644 --- a/ql/src/semmle/go/Packages.qll +++ b/ql/src/semmle/go/Packages.qll @@ -24,3 +24,15 @@ class Package extends @package { /** Gets a textual representation of this element. */ string toString() { result = "package " + getPath() } } + +/** + * Gets an import path that identifies a package in module `mod` with the given path, + * possibly modulo [semantic import versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning). + * + * For example, `package("github.com/go-pg/pg", "types")` gets an import path that can + * refer to `"github.com/go-pg/pg/types"`, but also to `"github.com/go-pg/pg/v10/types"`. + */ +bindingset[result, mod, path] +string package(string mod, string path) { + result.regexpMatch("\\Q" + mod + "\\E([/.]v[^/]+)?($|/)\\Q" + path + "\\E") +} diff --git a/ql/src/semmle/go/Scopes.qll b/ql/src/semmle/go/Scopes.qll index 74af0d196e1..c9bf60e3ac4 100644 --- a/ql/src/semmle/go/Scopes.qll +++ b/ql/src/semmle/go/Scopes.qll @@ -406,6 +406,9 @@ class Method extends Function { result = this.getReceiverType().getPackage() } + /** Holds if this method is declared in an interface. */ + predicate isInterfaceMethod() { getReceiverType().getUnderlyingType() instanceof InterfaceType } + /** Gets the receiver variable of this method. */ Variable getReceiver() { result = receiver } @@ -464,8 +467,14 @@ class Method extends Function { * Holds if this method implements the method `m`, that is, if `m` is a method * on an interface, and this is a method with the same name on a type that * implements that interface. + * + * Note that all methods implement themselves, and interface methods _only_ + * implement themselves. */ predicate implements(Method m) { + this = m + or + not isInterfaceMethod() and exists(Type t | this = t.getMethod(m.getName()) and t.implements(m.getReceiverType().getUnderlyingType()) diff --git a/ql/src/semmle/go/Stmt.qll b/ql/src/semmle/go/Stmt.qll index 8a99c39f8ce..822046733f2 100644 --- a/ql/src/semmle/go/Stmt.qll +++ b/ql/src/semmle/go/Stmt.qll @@ -6,6 +6,18 @@ import go /** * A statement. + * + * Examples: + * + * ```go + * a = 0 + * + * if x := f(); x < y { + * return y - x + * } else { + * return x - y + * } + * ``` */ class Stmt extends @stmt, ExprParent, StmtParent { /** @@ -30,6 +42,13 @@ class Stmt extends @stmt, ExprParent, StmtParent { /** * A bad statement, that is, a statement that could not be parsed. + * + * Examples: + * + * ```go + * go fmt.Println + * defer int + * ``` */ class BadStmt extends @badstmt, Stmt { override string toString() { result = "bad statement" } @@ -37,6 +56,14 @@ class BadStmt extends @badstmt, Stmt { /** * A declaration statement. + * + * Examples: + * + * ```go + * var i int + * const pi = 3.14159 + * type Printer interface{ Print() } + * ``` */ class DeclStmt extends @declstmt, Stmt, DeclParent { /** Gets the declaration in this statement. */ @@ -49,6 +76,12 @@ class DeclStmt extends @declstmt, Stmt, DeclParent { /** * An empty statement. + * + * Examples: + * + * ```go + * ; + * ``` */ class EmptyStmt extends @emptystmt, Stmt { override string toString() { result = "empty statement" } @@ -56,6 +89,12 @@ class EmptyStmt extends @emptystmt, Stmt { /** * A labeled statement. + * + * Examples: + * + * ```go + * Error: log.Panic("error encountered") + * ``` */ class LabeledStmt extends @labeledstmt, Stmt { /** Gets the identifier representing the label. */ @@ -74,6 +113,15 @@ class LabeledStmt extends @labeledstmt, Stmt { /** * An expression statement. + * + * Examples: + * + * ```go + * h(x+y) + * f.Close() + * <-ch + * (<-ch) + * ``` */ class ExprStmt extends @exprstmt, Stmt { /** Gets the expression. */ @@ -86,6 +134,12 @@ class ExprStmt extends @exprstmt, Stmt { /** * A send statement. + * + * Examples: + * + * ```go + * ch <- 3 + * ``` */ class SendStmt extends @sendstmt, Stmt { /** Gets the expression representing the channel. */ @@ -101,6 +155,13 @@ class SendStmt extends @sendstmt, Stmt { /** * An increment or decrement statement. + * + * Examples: + * + * ```go + * a++ + * b-- + * ``` */ class IncDecStmt extends @incdecstmt, Stmt { /** Gets the expression being incremented or decremented. */ @@ -114,6 +175,12 @@ class IncDecStmt extends @incdecstmt, Stmt { /** * An increment statement. + * + * Examples: + * + * ```go + * a++ + * ``` */ class IncStmt extends @incstmt, IncDecStmt { override string getOperator() { result = "++" } @@ -123,6 +190,12 @@ class IncStmt extends @incstmt, IncDecStmt { /** * A decrement statement. + * + * Examples: + * + * ```go + * b-- + * ``` */ class DecStmt extends @decstmt, IncDecStmt { override string getOperator() { result = "--" } @@ -132,6 +205,16 @@ class DecStmt extends @decstmt, IncDecStmt { /** * A (simple or compound) assignment statement. + * + * Examples: + * + * ```go + * x := 1 + * *p = f() + * a[i] = 23 + * (k) = <-ch // same as: k = <-ch + * a += 2 + * ``` */ class Assignment extends @assignment, Stmt { /** Gets the `i`th left-hand side of this assignment (0-based). */ @@ -177,11 +260,28 @@ class Assignment extends @assignment, Stmt { /** * A simple assignment statement, that is, an assignment without a compound operator. + * + * Examples: + * + * ```go + * x := 1 + * *p = f() + * a[i] = 23 + * (k) = <-ch // same as: k = <-ch + * ``` */ class SimpleAssignStmt extends @simpleassignstmt, Assignment { } /** * A plain assignment statement. + * + * Examples: + * + * ```go + * *p = f() + * a[i] = 23 + * (k) = <-ch // same as: k = <-ch + * ``` */ class AssignStmt extends @assignstmt, SimpleAssignStmt { override string getOperator() { result = "=" } @@ -189,6 +289,12 @@ class AssignStmt extends @assignstmt, SimpleAssignStmt { /** * A define statement. + * + * Examples: + * + * ```go + * x := 1 + * ``` */ class DefineStmt extends @definestmt, SimpleAssignStmt { override string getOperator() { result = ":=" } @@ -196,11 +302,24 @@ class DefineStmt extends @definestmt, SimpleAssignStmt { /** * A compound assignment statement. + * + * Examples: + * + * ```go + * a += 2 + * a /= 2 + * ``` */ class CompoundAssignStmt extends @compoundassignstmt, Assignment { } /** * An add-assign statement using `+=`. + * + * Examples: + * + * ```go + * a += 2 + * ``` */ class AddAssignStmt extends @addassignstmt, CompoundAssignStmt { override string getOperator() { result = "+=" } @@ -208,6 +327,12 @@ class AddAssignStmt extends @addassignstmt, CompoundAssignStmt { /** * A subtract-assign statement using `-=`. + * + * Examples: + * + * ```go + * a -= 2 + * ``` */ class SubAssignStmt extends @subassignstmt, CompoundAssignStmt { override string getOperator() { result = "-=" } @@ -215,6 +340,12 @@ class SubAssignStmt extends @subassignstmt, CompoundAssignStmt { /** * A multiply-assign statement using `*=`. + * + * Examples: + * + * ```go + * a *= 2 + * ``` */ class MulAssignStmt extends @mulassignstmt, CompoundAssignStmt { override string getOperator() { result = "*=" } @@ -222,6 +353,12 @@ class MulAssignStmt extends @mulassignstmt, CompoundAssignStmt { /** * A divide-assign statement using `/=`. + * + * Examples: + * + * ```go + * a /= 2 + * ``` */ class QuoAssignStmt extends @quoassignstmt, CompoundAssignStmt { override string getOperator() { result = "/=" } @@ -231,6 +368,12 @@ class DivAssignStmt = QuoAssignStmt; /** * A modulo-assign statement using `%=`. + * + * Examples: + * + * ```go + * a %= 2 + * ``` */ class RemAssignStmt extends @remassignstmt, CompoundAssignStmt { override string getOperator() { result = "%=" } @@ -240,6 +383,12 @@ class ModAssignStmt = RemAssignStmt; /** * An and-assign statement using `&=`. + * + * Examples: + * + * ```go + * a &= 2 + * ``` */ class AndAssignStmt extends @andassignstmt, CompoundAssignStmt { override string getOperator() { result = "&=" } @@ -247,6 +396,12 @@ class AndAssignStmt extends @andassignstmt, CompoundAssignStmt { /** * An or-assign statement using `|=`. + * + * Examples: + * + * ```go + * a |= 2 + * ``` */ class OrAssignStmt extends @orassignstmt, CompoundAssignStmt { override string getOperator() { result = "|=" } @@ -254,6 +409,12 @@ class OrAssignStmt extends @orassignstmt, CompoundAssignStmt { /** * An xor-assign statement using `^=`. + * + * Examples: + * + * ```go + * a ^= 2 + * ``` */ class XorAssignStmt extends @xorassignstmt, CompoundAssignStmt { override string getOperator() { result = "^=" } @@ -261,6 +422,12 @@ class XorAssignStmt extends @xorassignstmt, CompoundAssignStmt { /** * A left-shift-assign statement using `<<=`. + * + * Examples: + * + * ```go + * a <<= 2 + * ``` */ class ShlAssignStmt extends @shlassignstmt, CompoundAssignStmt { override string getOperator() { result = "<<=" } @@ -270,6 +437,12 @@ class LShiftAssignStmt = ShlAssignStmt; /** * A right-shift-assign statement using `>>=`. + * + * Examples: + * + * ```go + * a >>= 2 + * ``` */ class ShrAssignStmt extends @shrassignstmt, CompoundAssignStmt { override string getOperator() { result = ">>=" } @@ -279,6 +452,12 @@ class RShiftAssignStmt = ShrAssignStmt; /** * An and-not-assign statement using `&^=`. + * + * Examples: + * + * ```go + * a &^= 2 + * ``` */ class AndNotAssignStmt extends @andnotassignstmt, CompoundAssignStmt { override string getOperator() { result = "&^=" } @@ -286,6 +465,12 @@ class AndNotAssignStmt extends @andnotassignstmt, CompoundAssignStmt { /** * A `go` statement. + * + * Examples: + * + * ```go + * go fillPixels(row) + * ``` */ class GoStmt extends @gostmt, Stmt { /** Gets the call. */ @@ -298,6 +483,12 @@ class GoStmt extends @gostmt, Stmt { /** * A `defer` statement. + * + * Examples: + * + * ```go + * defer mutex.Unlock() + * ``` */ class DeferStmt extends @deferstmt, Stmt { /** Gets the call being deferred. */ @@ -310,6 +501,12 @@ class DeferStmt extends @deferstmt, Stmt { /** * A `return` statement. + * + * Examples: + * + * ```go + * return x + * ``` */ class ReturnStmt extends @returnstmt, Stmt { /** Gets the `i`th returned expression (0-based) */ @@ -331,6 +528,17 @@ class ReturnStmt extends @returnstmt, Stmt { /** * A branch statement, for example a `break` or `goto`. + * + * Examples: + * + * ```go + * break + * break OuterLoop + * continue + * continue RowLoop + * goto Error + * fallthrough + * ``` */ class BranchStmt extends @branchstmt, Stmt { /** Gets the expression denoting the target label of the branch, if any. */ @@ -340,27 +548,72 @@ class BranchStmt extends @branchstmt, Stmt { string getLabel() { result = getLabelExpr().getName() } } -/** A `break` statement. */ +/** + * A `break` statement. + * + * Examples: + * + * ```go + * break + * break OuterLoop + * ``` + */ class BreakStmt extends @breakstmt, BranchStmt { override string toString() { result = "break statement" } } -/** A `continue` statement. */ +/** + * A `continue` statement. + * + * Examples: + * + * ```go + * continue + * continue RowLoop + * ``` + */ class ContinueStmt extends @continuestmt, BranchStmt { override string toString() { result = "continue statement" } } -/** A `goto` statement. */ +/** + * A `goto` statement. + * + * Examples: + * + * ```go + * goto Error + * ``` + */ class GotoStmt extends @gotostmt, BranchStmt { override string toString() { result = "goto statement" } } -/** A `fallthrough` statement. */ +/** + * A `fallthrough` statement. + * + * Examples: + * + * ```go + * fallthrough + * ``` + */ class FallthroughStmt extends @fallthroughstmt, BranchStmt { override string toString() { result = "fallthrough statement" } } -/** A block statement. */ +/** + * A block statement. + * + * Examples: + * + * ```go + * { + * fmt.Printf("iteration %d\n", i) + * f(i) + * } + * ``` + */ class BlockStmt extends @blockstmt, Stmt, ScopeNode { /** Gets the `i`th statement in this block (0-based). */ Stmt getStmt(int i) { result = getChildStmt(i) } @@ -376,7 +629,19 @@ class BlockStmt extends @blockstmt, Stmt, ScopeNode { override string toString() { result = "block statement" } } -/** An `if` statement. */ +/** + * An `if` statement. + * + * Examples: + * + * ```go + * if x := f(); x < y { + * return y - x + * } else { + * return x - y + * } + * ``` + */ class IfStmt extends @ifstmt, Stmt, ScopeNode { /** Gets the init statement of this `if` statement, if any. */ Stmt getInit() { result = getChildStmt(0) } @@ -400,7 +665,23 @@ class IfStmt extends @ifstmt, Stmt, ScopeNode { override string toString() { result = "if statement" } } -/** A `case` or `default` clause in a `switch` statement. */ +/** + * A `case` or `default` clause in a `switch` statement. + * + * Examples: + * + * ```go + * case 0, 1: + * a = 1 + * fallthrough + * + * default: + * b = 2 + * + * case func(int) float64: + * printFunction(i) + * ``` + */ class CaseClause extends @caseclause, Stmt, ScopeNode { /** Gets the `i`th expression of this `case` clause (0-based). */ Expr getExpr(int i) { result = getChildExpr(-(i + 1)) } @@ -430,6 +711,29 @@ class CaseClause extends @caseclause, Stmt, ScopeNode { /** * A `switch` statement, that is, either an expression switch or a type switch. + * + * Examples: + * + * ```go + * switch x := f(); x { + * case 0, 1: + * a = 1 + * fallthrough + * default: + * b = 2 + * } + * + * switch i := x.(type) { + * default: + * printString("don't know the type") + * case nil: + * printString("x is nil") + * case int: + * printInt(i) + * case func(int) float64: + * printFunction(i) + * } + * ``` */ class SwitchStmt extends @switchstmt, Stmt, ScopeNode { /** Gets the init statement of this `switch` statement, if any. */ @@ -465,6 +769,18 @@ class SwitchStmt extends @switchstmt, Stmt, ScopeNode { /** * An expression-switch statement. + * + * Examples: + * + * ```go + * switch x := f(); x { + * case 0, 1: + * a = 1 + * fallthrough + * default: + * b = 2 + * } + * ``` */ class ExpressionSwitchStmt extends @exprswitchstmt, SwitchStmt { /** Gets the switch expression of this `switch` statement. */ @@ -480,6 +796,21 @@ class ExpressionSwitchStmt extends @exprswitchstmt, SwitchStmt { /** * A type-switch statement. + * + * Examples: + * + * ```go + * switch i := x.(type) { + * default: + * printString("don't know the type") // type of i is type of x (interface{}) + * case nil: + * printString("x is nil") // type of i is type of x (interface{}) + * case int: + * printInt(i) // type of i is int + * case func(int) float64: + * printFunction(i) // type of i is func(int) float64 + * } + * ``` */ class TypeSwitchStmt extends @typeswitchstmt, SwitchStmt { /** Gets the assign statement of this type-switch statement. */ @@ -495,6 +826,26 @@ class TypeSwitchStmt extends @typeswitchstmt, SwitchStmt { /** * A comm clause, that is, a `case` or `default` clause in a `select` statement. + * + * Examples: + * + * ```go + * case i1 = <-c1: + * print("received ", i1, " from c1\n") + * + * case c2 <- i2: + * print("sent ", i2, " to c2\n") + * + * case i3, ok := (<-c3): // same as: i3, ok := <-c3 + * if ok { + * print("received ", i3, " from c3\n") + * } else { + * print("c3 is closed\n") + * } + * + * default: + * print("no communication\n") + * ``` */ class CommClause extends @commclause, Stmt, ScopeNode { /** Gets the comm statement of this clause, if any. */ @@ -516,6 +867,14 @@ class CommClause extends @commclause, Stmt, ScopeNode { /** * A receive statement in a comm clause. + * + * Examples: + * + * ```go + * i1 = <-c1 + * i3, ok := <-c3 + * i3, ok := (<-c3) + * ``` */ class RecvStmt extends Stmt { RecvStmt() { this = any(CommClause cc).getComm() and not this instanceof SendStmt } @@ -535,6 +894,25 @@ class RecvStmt extends Stmt { /** * A `select` statement. + * + * Examples: + * + * ```go + * select { + * case i1 = <-c1: + * print("received ", i1, " from c1\n") + * case c2 <- i2: + * print("sent ", i2, " to c2\n") + * case i3, ok := (<-c3): // same as: i3, ok := <-c3 + * if ok { + * print("received ", i3, " from c3\n") + * } else { + * print("c3 is closed\n") + * } + * default: + * print("no communication\n") + * } + * ``` */ class SelectStmt extends @selectstmt, Stmt { /** Gets the body of this `select` statement. */ @@ -576,6 +954,22 @@ class SelectStmt extends @selectstmt, Stmt { /** * A loop, that is, either a `for` statement or a `range` statement. + * + * Examples: + * + * ```go + * for a < b { + * a *= 2 + * } + * + * for i := 0; i < 10; i++ { + * f(i) + * } + * + * for key, value := range mymap { + * fmt.Printf("mymap[%s] = %d\n", key, value) + * } + * ``` */ class LoopStmt extends @loopstmt, Stmt, ScopeNode { /** Gets the body of this loop. */ @@ -584,6 +978,18 @@ class LoopStmt extends @loopstmt, Stmt, ScopeNode { /** * A `for` statement. + * + * Examples: + * + * ```go + * for a < b { + * a *= 2 + * } + * + * for i := 0; i < 10; i++ { + * f(i) + * } + * ``` */ class ForStmt extends @forstmt, LoopStmt { /** Gets the init statement of this `for` statement, if any. */ @@ -609,6 +1015,26 @@ class ForStmt extends @forstmt, LoopStmt { /** * A `range` statement. + * + * Examples: + * + * ```go + * for key, value := range mymap { + * fmt.Printf("mymap[%s] = %d\n", key, value) + * } + * + * for _, value = range array { + * fmt.Printf("array contains: %d\n", value) + * } + * + * for index, _ := range str { + * fmt.Printf("str[%d] = ?\n", index) + * } + * + * for value = range ch { + * fmt.Printf("value from channel: %d\n", value) + * } + * ``` */ class RangeStmt extends @rangestmt, LoopStmt { /** Gets the expression denoting the key of this `range` statement. */ diff --git a/ql/src/semmle/go/StringOps.qll b/ql/src/semmle/go/StringOps.qll index 8fdc4223716..dfc8ceb3c9f 100644 --- a/ql/src/semmle/go/StringOps.qll +++ b/ql/src/semmle/go/StringOps.qll @@ -9,7 +9,7 @@ module StringOps { /** * An expression that is equivalent to `strings.HasPrefix(A, B)` or `!strings.HasPrefix(A, B)`. * - * Extends this class to refine existing API models. If you want to model new APIs, + * Extend this class to refine existing API models. If you want to model new APIs, * extend `StringOps::HasPrefix::Range` instead. */ class HasPrefix extends DataFlow::Node { @@ -43,7 +43,7 @@ module StringOps { /** * An expression that is equivalent to `strings.HasPrefix(A, B)` or `!strings.HasPrefix(A, B)`. * - * Extends this class to model new APIs. If you want to refine existing API models, extend + * Extend this class to model new APIs. If you want to refine existing API models, extend * `StringOps::HasPrefix` instead. */ abstract class Range extends DataFlow::Node { diff --git a/ql/src/semmle/go/Types.qll b/ql/src/semmle/go/Types.qll index cee40d31410..1b246e3e904 100644 --- a/ql/src/semmle/go/Types.qll +++ b/ql/src/semmle/go/Types.qll @@ -25,11 +25,17 @@ class Type extends @type { /** * Gets the qualified name of this type, if any. + * + * Only (defined) named types like `io.Writer` have a qualified name. Basic types like `int`, + * pointer types like `*io.Writer`, and other composite types do not have a qualified name. */ string getQualifiedName() { result = getEntity().getQualifiedName() } /** * Holds if this type is declared in a package with path `pkg` and has name `name`. + * + * Only (defined) named types like `io.Writer` have a qualified name. Basic types like `int`, + * pointer types like `*io.Writer`, and other composite types do not have a qualified name. */ predicate hasQualifiedName(string pkg, string name) { getEntity().hasQualifiedName(pkg, name) } diff --git a/ql/src/semmle/go/dataflow/BarrierGuardUtil.qll b/ql/src/semmle/go/dataflow/BarrierGuardUtil.qll new file mode 100644 index 00000000000..1f2977d33d3 --- /dev/null +++ b/ql/src/semmle/go/dataflow/BarrierGuardUtil.qll @@ -0,0 +1,68 @@ +/** + * Provides implementations of some commonly used barrier guards for sanitizing untrusted URLs. + */ + +import go + +/** + * A call to a function called `isLocalUrl`, `isValidRedirect`, or similar, which is + * considered a barrier guard for sanitizing untrusted URLs. + */ +class RedirectCheckBarrierGuard extends DataFlow::BarrierGuard, DataFlow::CallNode { + RedirectCheckBarrierGuard() { + this.getCalleeName().regexpMatch("(?i)(is_?)?(local_?url|valid_?redir(ect)?)") + } + + override predicate checks(Expr e, boolean outcome) { + // `isLocalUrl(e)` is a barrier for `e` if it evaluates to `true` + getAnArgument().asExpr() = e and + outcome = true + } +} + +/** + * An equality check comparing a data-flow node against a constant string, considered as + * a barrier guard for sanitizing untrusted URLs. + * + * Additionally, a check comparing `url.Hostname()` against a constant string is also + * considered a barrier guard for `url`. + */ +class UrlCheck extends DataFlow::BarrierGuard, DataFlow::EqualityTestNode { + DataFlow::Node url; + + UrlCheck() { + exists(this.getAnOperand().getStringValue()) and + ( + url = this.getAnOperand() + or + exists(DataFlow::MethodCallNode mc | mc = this.getAnOperand() | + mc.getTarget().getName() = "Hostname" and + url = mc.getReceiver() + ) + ) + } + + override predicate checks(Expr e, boolean outcome) { + e = url.asExpr() and outcome = this.getPolarity() + } +} + +/** + * A call to a regexp match function, considered as a barrier guard for sanitizing untrusted URLs. + * + * This is overapproximate: we do not attempt to reason about the correctness of the regexp. + */ +class RegexpCheck extends DataFlow::BarrierGuard { + RegexpMatchFunction matchfn; + DataFlow::CallNode call; + + RegexpCheck() { + matchfn.getACall() = call and + this = matchfn.getResult().getNode(call).getASuccessor*() + } + + override predicate checks(Expr e, boolean branch) { + e = matchfn.getValue().getNode(call).asExpr() and + (branch = false or branch = true) + } +} diff --git a/ql/src/semmle/go/dataflow/FunctionInputsAndOutputs.qll b/ql/src/semmle/go/dataflow/FunctionInputsAndOutputs.qll index 95a4c180b97..2fde6b5114b 100644 --- a/ql/src/semmle/go/dataflow/FunctionInputsAndOutputs.qll +++ b/ql/src/semmle/go/dataflow/FunctionInputsAndOutputs.qll @@ -4,6 +4,7 @@ */ import go +private import semmle.go.dataflow.internal.DataFlowPrivate /** * An abstract representation of an input to a function, which is either a parameter @@ -11,7 +12,14 @@ import go */ private newtype TFunctionInput = TInParameter(int i) { exists(SignatureType s | exists(s.getParameterType(i))) } or - TInReceiver() + TInReceiver() or + TInResult(int index) { + // the one and only result + index = -1 + or + // one among several results + exists(SignatureType s | exists(s.getResultType(index))) + } /** * An abstract representation of an input to a function, which is either a parameter @@ -24,6 +32,12 @@ class FunctionInput extends TFunctionInput { /** Holds if this represents the receiver of a function. */ predicate isReceiver() { none() } + /** Holds if this represents the result of a function. */ + predicate isResult() { none() } + + /** Holds if this represents the `i`th result of a function. */ + predicate isResult(int i) { none() } + /** Gets the data-flow node corresponding to this input for the call `c`. */ final DataFlow::Node getNode(DataFlow::CallNode c) { result = getEntryNode(c) } @@ -69,6 +83,51 @@ private class ReceiverInput extends FunctionInput, TInReceiver { override string toString() { result = "receiver" } } +/** + * A result position of a function, viewed as an input. + * + * Results are usually outputs rather than inputs, but for taint tracking it can be useful to + * think of taint propagating backwards from a result of a function to its arguments. For instance, + * the function `bufio.NewWriter` returns a writer `bw` that buffers write operations to an + * underlying writer `w`. If tainted data is written to `bw`, then it makes sense to propagate + * that taint back to the underlying writer `w`, which can be modeled by saying that + * `bufio.NewWriter` propagates taint from its result to its first argument. + */ +private class ResultInput extends FunctionInput, TInResult { + int index; + + ResultInput() { this = TInResult(index) } + + override predicate isResult() { index = -1 } + + override predicate isResult(int i) { + i = 0 and isResult() + or + i = index and i >= 0 + } + + override DataFlow::Node getEntryNode(DataFlow::CallNode c) { + exists(DataFlow::PostUpdateNode pun, DataFlow::Node init | + pun = result and + init = pun.(DataFlow::SsaNode).getInit() + | + index = -1 and + init = c.getResult() + or + index >= 0 and + init = c.getResult(index) + ) + } + + override DataFlow::Node getExitNode(FuncDef f) { none() } + + override string toString() { + index = -1 and result = "result" + or + index >= 0 and result = "result " + index + } +} + /** * An abstract representation of an output of a function, which is one of its results. */ @@ -126,7 +185,11 @@ private class OutResult extends FunctionOutput, TOutResult { override predicate isResult() { index = -1 } - override predicate isResult(int i) { i = index and i >= 0 } + override predicate isResult(int i) { + i = 0 and isResult() + or + i = index and i >= 0 + } override DataFlow::Node getEntryNode(FuncDef f) { // return expressions @@ -181,8 +244,8 @@ private class OutParameter extends FunctionOutput, TOutParameter { } override DataFlow::Node getExitNode(DataFlow::CallNode c) { - exists(DataFlow::ArgumentNode arg | - arg.argumentOf(c.asExpr(), index) and + exists(DataFlow::Node arg | + arg = getArgument(c, index) and result.(DataFlow::PostUpdateNode).getPreUpdateNode() = arg ) } diff --git a/ql/src/semmle/go/dataflow/SSA.qll b/ql/src/semmle/go/dataflow/SSA.qll index 6d92b1c926d..4f31c197ef2 100644 --- a/ql/src/semmle/go/dataflow/SSA.qll +++ b/ql/src/semmle/go/dataflow/SSA.qll @@ -338,6 +338,13 @@ class SsaWithFields extends TSsaWithFields { /** Gets a use that refers to this SSA variable with fields. */ DataFlow::Node getAUse() { this = accessPath(result.asInstruction()) } + /** Gets the type of this SSA variable with fields. */ + Type getType() { + exists(SsaVariable var | this = TRoot(var) | result = var.getType()) + or + exists(Field f | this = TStep(_, f) | result = f.getType()) + } + /** Gets a textual representation of this element. */ string toString() { exists(SsaVariable var | this = TRoot(var) | result = "(" + var + ")") @@ -345,6 +352,15 @@ class SsaWithFields extends TSsaWithFields { exists(SsaWithFields base, Field f | this = TStep(base, f) | result = base + "." + f.getName()) } + /** + * Gets an SSA-with-fields variable that is similar to this SSA-with-fields variable in the + * sense that it has the same root variable and the same sequence of field accesses. + */ + SsaWithFields similar() { + result.getBaseVariable().getSourceVariable() = this.getBaseVariable().getSourceVariable() and + result.getQualifiedName() = this.getQualifiedName() + } + /** * Gets the qualified name of the source variable or variable and fields that this represents. * diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowImpl.qll b/ql/src/semmle/go/dataflow/internal/DataFlowImpl.qll index 5eb98aefe4e..f876c04d6c6 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowImpl.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowImpl.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } @@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi */ private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } -pragma[noinline] -private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { - viableCallable(call) = result.getCallable() and - kind = result.getKind() -} - /** - * Holds if `node` is reachable from a source in the given configuration - * taking simple call contexts into consideration. + * Holds if `node` is reachable from a source in the configuration `config`. + * + * The Boolean `fromArg` records whether the node is reached through an + * argument in a call. */ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) { not fullBarrier(node, config) and @@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - storeDirect(mid, _, node) and + store(mid, _, node) and not outBarrier(mid, config) ) or // read exists(Content f | nodeCandFwd1Read(f, node, fromArg, config) and - storeCandFwd1(f, config) and + nodeCandFwd1IsStored(f, config) and not inBarrier(node, config) ) or @@ -317,13 +310,34 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) fromArg = false or nodeCandFwd1OutFromArg(call, node, config) and - flowOutCandFwd1(call, fromArg, config) + nodeCandFwd1IsEntered(call, fromArg, config) ) ) } private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } +pragma[nomagic] +private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { + exists(Node mid | + nodeCandFwd1(mid, fromArg, config) and + read(mid, f, node) + ) +} + +/** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + */ +pragma[nomagic] +private predicate nodeCandFwd1IsStored(Content f, Configuration config) { + exists(Node mid, Node node | + not fullBarrier(node, config) and + useFieldFlow(config) and + nodeCandFwd1(mid, config) and + store(mid, f, node) + ) +} + pragma[nomagic] private predicate nodeCandFwd1ReturnPosition( ReturnPosition pos, boolean fromArg, Configuration config @@ -335,43 +349,10 @@ private predicate nodeCandFwd1ReturnPosition( } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { - exists(Node mid | - nodeCandFwd1(mid, fromArg, config) and - readDirect(mid, f, node) - ) -} - -/** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. - */ -pragma[nomagic] -private predicate storeCandFwd1(Content f, Configuration config) { - exists(Node mid, Node node | - not fullBarrier(node, config) and - useFieldFlow(config) and - nodeCandFwd1(mid, config) and - storeDirect(mid, f, node) - ) -} - -pragma[nomagic] -private predicate nodeCandFwd1ReturnKind( - DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config -) { +private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) { exists(ReturnPosition pos | nodeCandFwd1ReturnPosition(pos, fromArg, config) and - pos = viableReturnPos(call, kind) - ) -} - -pragma[nomagic] -private predicate nodeCandFwd1Out( - DataFlowCall call, Node node, boolean fromArg, Configuration config -) { - exists(ReturnKindExt kind | - nodeCandFwd1ReturnKind(call, kind, fromArg, config) and - node = kind.getAnOutNode(call) + viableReturnPosOut(call, pos, out) ) } @@ -384,7 +365,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) { exists(ArgumentNode arg | nodeCandFwd1(arg, fromArg, config) and viableParamArg(call, _, arg) @@ -395,8 +376,11 @@ bindingset[result, b] private boolean unbindBool(boolean b) { result != b.booleanNot() } /** - * Holds if `node` is part of a path from a source to a sink in the given - * configuration taking simple call contexts into consideration. + * Holds if `node` is part of a path from a source to a sink in the + * configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. */ pragma[nomagic] private predicate nodeCand1(Node node, boolean toReturn, Configuration config) { @@ -435,55 +419,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) // store exists(Content f | nodeCand1Store(f, node, toReturn, config) and - readCand1(f, config) + nodeCand1IsRead(f, config) ) or // read exists(Node mid, Content f | - readDirect(node, f, mid) and - storeCandFwd1(f, unbind(config)) and + read(node, f, mid) and + nodeCandFwd1IsStored(f, unbind(config)) and nodeCand1(mid, toReturn, config) ) or // flow into a callable exists(DataFlowCall call | - nodeCand1Arg(call, node, false, config) and + nodeCand1In(call, node, false, config) and toReturn = false or - nodeCand1ArgToReturn(call, node, config) and - flowInCand1(call, toReturn, config) + nodeCand1InToReturn(call, node, config) and + nodeCand1IsReturned(call, toReturn, config) ) or // flow out of a callable exists(ReturnPosition pos | - nodeCand1ReturnPosition(pos, config) and + nodeCand1Out(pos, config) and getReturnPosition(node) = pos and toReturn = true ) } -pragma[nomagic] -private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) } - -pragma[nomagic] -private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) { - exists(DataFlowCall call, ReturnKindExt kind, Node out | - nodeCand1(out, _, config) and - pos = viableReturnPos(call, kind) and - out = kind.getAnOutNode(call) - ) -} - /** * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate readCand1(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content f, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - readDirect(node, f, mid) and - storeCandFwd1(f, unbind(config)) and + read(node, f, mid) and + nodeCandFwd1IsStored(f, unbind(config)) and nodeCand1(mid, _, config) ) } @@ -492,8 +464,8 @@ pragma[nomagic] private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { exists(Node mid | nodeCand1(mid, toReturn, config) and - storeCandFwd1(f, unbind(config)) and - storeDirect(node, f, mid) + nodeCandFwd1IsStored(f, unbind(config)) and + store(node, f, mid) ) } @@ -501,13 +473,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu * Holds if `f` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate readStoreCand1(Content f, Configuration conf) { - readCand1(f, conf) and +private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { + nodeCand1IsRead(f, conf) and nodeCand1Store(f, _, _, conf) } pragma[nomagic] -private predicate viableParamArgCandFwd1( +private predicate viableReturnPosOutNodeCandFwd1( + DataFlowCall call, ReturnPosition pos, Node out, Configuration config +) { + nodeCandFwd1ReturnPosition(pos, _, config) and + viableReturnPosOut(call, pos, out) +} + +pragma[nomagic] +private predicate nodeCand1Out(ReturnPosition pos, Configuration config) { + exists(DataFlowCall call, Node out | + nodeCand1(out, _, config) and + viableReturnPosOutNodeCandFwd1(call, pos, out, config) + ) +} + +pragma[nomagic] +private predicate viableParamArgNodeCandFwd1( DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config ) { viableParamArg(call, p, arg) and @@ -515,32 +503,35 @@ private predicate viableParamArgCandFwd1( } pragma[nomagic] -private predicate nodeCand1Arg( +private predicate nodeCand1In( DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config ) { exists(ParameterNode p | nodeCand1(p, toReturn, config) and - viableParamArgCandFwd1(call, p, arg, config) + viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] -private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { - nodeCand1Arg(call, arg, true, config) +private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + nodeCand1In(call, arg, true, config) } /** * Holds if an output from `call` is reached in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) { +private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) { exists(Node out | nodeCand1(out, toReturn, config) and nodeCandFwd1OutFromArg(call, out, config) ) } -private predicate throughFlowNodeCand(Node node, Configuration config) { +pragma[nomagic] +private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) } + +private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and @@ -548,11 +539,12 @@ private predicate throughFlowNodeCand(Node node, Configuration config) { } /** Holds if flow may return from `callable`. */ -private predicate returnFlowCallableCand( +pragma[nomagic] +private predicate returnFlowCallableNodeCand1( DataFlowCallable callable, ReturnKindExt kind, Configuration config ) { exists(ReturnNodeExt ret | - throughFlowNodeCand(ret, config) and + throughFlowNodeCand1(ret, config) and callable = ret.getEnclosingCallable() and kind = ret.getKind() ) @@ -562,10 +554,10 @@ private predicate returnFlowCallableCand( * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ -private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) { +private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) { exists(ReturnKindExt kind | - throughFlowNodeCand(p, config) and - returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and + throughFlowNodeCand1(p, config) and + returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and // we don't expect a parameter to return stored in itself not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos) @@ -573,329 +565,75 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config ) } -private predicate store(Node n1, Content f, Node n2) { - storeDirect(n1, f, n2) or - argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2) -} - -private predicate read(Node n1, Content f, Node n2) { - readDirect(n1, f, n2) or - argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2) -} - -/** - * Holds if `p` can flow to `node` in the same callable with `summary` - * representing the flow path. The type of the tracked object is `t2`, and if - * the summary includes a store step, `t1` is the tracked type just prior to the - * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`. - */ pragma[nomagic] -private predicate parameterFlow( - ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary, - Configuration config -) { - parameterThroughFlowCand(p, config) and - p = node and - t1 = getErasedNodeTypeBound(node) and - t1 = t2 and - summary = TSummaryVal() - or - throughFlowNodeCand(node, unbind(config)) and - ( - exists(Node mid | - parameterFlow(p, mid, t1, t2, summary, config) and - localFlowStep(mid, node, config) and - compatibleTypes(t2, getErasedNodeTypeBound(node)) - ) - or - exists(Node mid, Summary midsum | - parameterFlow(p, mid, _, _, midsum, config) and - additionalLocalFlowStep(mid, node, config) and - t1 = getErasedNodeTypeBound(node) and - t1 = t2 and - summary = midsum.additionalStep() - ) - or - // read step - exists(Node mid, Content f, Summary midsum | - parameterFlow(p, mid, _, _, midsum, config) and - read(mid, f, node) and - readStoreCand1(f, unbind(config)) and - summary = midsum.readStep(f) and - t1 = f.getType() and - t1 = t2 - ) - or - // store step - exists(Node mid, Content f, Summary midsum | - parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and - store(mid, f, node) and - readStoreCand1(f, unbind(config)) and - summary = midsum.storeStep(f) and - compatibleTypes(t1, f.getType()) and - t2 = f.getContainerType() - ) - or - // value flow through a callable - exists(Node arg | - parameterFlow(p, arg, t1, t2, summary, config) and - argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and - compatibleTypes(t2, getErasedNodeTypeBound(node)) - ) - or - // flow through a callable - exists(Node arg, Summary s1, Summary s2 | - parameterFlow(p, arg, _, _, s1, config) and - argumentFlowsThrough(arg, node, t1, t2, s2, config) and - summary = s1.compose(s2) - ) - ) -} - -private predicate viableParamArgCand( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config -) { - viableParamArg(call, p, arg) and - nodeCand1(arg, unbind(config)) and - nodeCand1(p, config) and - not outBarrier(arg, config) and - not inBarrier(p, config) -} - -private predicate parameterFlowReturn( - ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2, - Summary summary, Configuration config -) { - parameterFlow(p, ret, t1, t2, summary, config) and - kind = ret.getKind() and - not summary.isPartial() and - not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)) +private predicate store(Node n1, Content f, Node n2, Configuration config) { + nodeCand1IsReadAndStored(f, config) and + nodeCand1(n2, unbind(config)) and + store(n1, f, n2) } pragma[nomagic] -private predicate argumentFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2, - Summary summary, Configuration config -) { - exists(ParameterNode p | - viableParamArgCand(call, p, arg, config) and - parameterFlowReturn(p, _, kind, t1, t2, summary, config) - ) -} - -/** - * Holds if data can flow from `arg` to `out` through a call with `summary` - * representing the flow path. The type of the tracked object is `t2`, and if - * the summary includes a store step, `t1` is the tracked type just prior to the - * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`. - */ -private predicate argumentFlowsThrough( - ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary, - Configuration config -) { - nodeCand1(out, unbind(config)) and - not inBarrier(out, config) and - compatibleTypes(t2, getErasedNodeTypeBound(out)) and - exists(DataFlowCall call, ReturnKindExt kind | - argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and - out = kind.getAnOutNode(call) - ) -} - -private newtype TNodeExt = - TNormalNode(Node node) { nodeCand1(node, _) } or - TReadStoreNode(DataFlowCall call, Node node1, Node node2, Content f1, Content f2) { - exists(Configuration config | - nodeCand1(node1, config) and - argumentValueFlowsThrough(call, node1, TContentSome(f1), TContentSome(f2), node2) and - nodeCand1(node2, unbind(config)) and - readStoreCand1(f1, unbind(config)) and - readStoreCand1(f2, unbind(config)) - ) - } - -/** - * An extended data flow node. Either a normal node, or an intermediate node - * used to split up a read+store step through a call into first a read step - * followed by a store step. - * - * This is purely an internal implementation detail. - */ -abstract private class NodeExt extends TNodeExt { - /** Gets the underlying (normal) node, if any. */ - abstract Node getNode(); - - abstract DataFlowType getErasedNodeTypeBound(); - - abstract DataFlowCallable getEnclosingCallable(); - - abstract predicate isCand1(Configuration config); - - abstract string toString(); - - abstract predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); -} - -/** A `Node` at which a cast can occur such that the type should be checked. */ -abstract private class CastingNodeExt extends NodeExt { } - -private class NormalNodeExt extends NodeExt, TNormalNode { - override Node getNode() { this = TNormalNode(result) } - - override DataFlowType getErasedNodeTypeBound() { - result = getErasedRepr(this.getNode().getTypeBound()) - } - - override DataFlowCallable getEnclosingCallable() { - result = this.getNode().getEnclosingCallable() - } - - override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) } - - override string toString() { result = this.getNode().toString() } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt { - NormalCastingNodeExt() { this.getNode() instanceof CastingNode } -} - -private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode { - private DataFlowCall call; - private Node node1; - private Node node2; - private Content f1; - private Content f2; - - ReadStoreNodeExt() { this = TReadStoreNode(call, node1, node2, f1, f2) } - - override Node getNode() { none() } - - override DataFlowType getErasedNodeTypeBound() { result = f1.getType() } - - override DataFlowCallable getEnclosingCallable() { result = node1.getEnclosingCallable() } - - override predicate isCand1(Configuration config) { - nodeCand1(node1, config) and nodeCand1(node2, config) - } - - override string toString() { - result = "(inside) " + call.toString() + " [" + f1 + " -> " + f2 + "]" - } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - call.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } -} - -pragma[nomagic] -private predicate readExt(NodeExt node1, Content f, NodeExt node2) { - read(node1.getNode(), f, node2.getNode()) - or - node2 = TReadStoreNode(_, node1.getNode(), _, f, _) -} - -pragma[nomagic] -private predicate storeExt(NodeExt node1, Content f, NodeExt node2) { - store(node1.getNode(), f, node2.getNode()) - or - node1 = TReadStoreNode(_, _, node2.getNode(), _, f) -} - -private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) { - jumpStep(node1.getNode(), node2.getNode(), config) -} - -private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) { - additionalJumpStep(node1.getNode(), node2.getNode(), config) -} - -private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) { - argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode()) -} - -private predicate argumentFlowsThrough( - NodeExt arg, NodeExt out, Summary summary, Configuration config -) { - argumentFlowsThrough(arg.getNode(), out.getNode(), _, _, summary, config) -} - -/** - * Holds if data can flow from `node1` to `node2` in one local step or a step - * through a callable. - */ -pragma[noinline] -private predicate localFlowStepOrFlowThroughCallable( - NodeExt node1, NodeExt node2, Configuration config -) { - exists(Node n1, Node n2 | - n1 = node1.getNode() and - n2 = node2.getNode() - | - nodeCand1(n1, config) and - localFlowStep(n1, n2, config) - or - nodeCand1(n1, config) and - argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2) - ) -} - -/** - * Holds if data can flow from `node1` to `node2` in one local step or a step - * through a callable, in both cases using an additional flow step from the - * configuration. - */ -pragma[noinline] -private predicate additionalLocalFlowStepOrFlowThroughCallable( - NodeExt node1, NodeExt node2, Configuration config -) { - exists(Node n1, Node n2 | - n1 = node1.getNode() and - n2 = node2.getNode() - | - nodeCand1(n1, config) and - additionalLocalFlowStep(n1, n2, config) - or - argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config) - ) +private predicate read(Node n1, Content f, Node n2, Configuration config) { + nodeCand1IsReadAndStored(f, config) and + nodeCand1(n2, unbind(config)) and + read(n1, f, n2) } pragma[noinline] -private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) { - result = getReturnPosition(node) and - nodeCand1(node, config) +private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) { + nodeCand1(node1, config) and + localFlowStep(node1, node2, config) +} + +pragma[noinline] +private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) { + nodeCand1(node1, config) and + additionalLocalFlowStep(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutNodeCand1( + DataFlowCall call, ReturnPosition pos, Node out, Configuration config +) { + nodeCand1(out, _, config) and + viableReturnPosOutNodeCandFwd1(call, pos, out, config) } /** - * Holds if data can flow out of a callable from `node1` to `node2`, either + * Holds if data can flow out of `call` from `ret` to `out`, either * through a `ReturnNode` or through an argument that has been mutated, and * that this step is part of a path from a source to a sink. */ -private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { - nodeCand1(node2, config) and - not outBarrier(node1, config) and - not inBarrier(node2, config) and - exists(DataFlowCall call, ReturnKindExt kind | - getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and - node2 = kind.getAnOutNode(call) - ) +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config +) { + viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and + nodeCand1(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) +} + +pragma[nomagic] +private predicate viableParamArgNodeCand1( + DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config +) { + viableParamArgNodeCandFwd1(call, p, arg, config) and + nodeCand1(arg, config) } /** - * Holds if data can flow into a callable and that this step is part of a + * Holds if data can flow into `call` and that this step is part of a * path from a source to a sink. */ -private predicate flowIntoCallable(ArgumentNode node1, ParameterNode node2, Configuration config) { - viableParamArgCand(_, node2, node1, config) +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config +) { + viableParamArgNodeCand1(call, p, arg, config) and + nodeCand1(p, config) and + not outBarrier(arg, config) and + not inBarrier(p, config) } /** @@ -904,7 +642,10 @@ private predicate flowIntoCallable(ArgumentNode node1, ParameterNode node2, Conf * contexts. */ private int branch(Node n1, Configuration conf) { - result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf)) + result = + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + ) } /** @@ -913,133 +654,128 @@ private int branch(Node n1, Configuration conf) { * contexts. */ private int join(Node n2, Configuration conf) { - result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf)) + result = + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + ) } /** - * Holds if data can flow out of a callable from `node1` to `node2`, either + * Holds if data can flow out of `call` from `ret` to `out`, either * through a `ReturnNode` or through an argument that has been mutated, and * that this step is part of a path from a source to a sink. The * `allowsFieldFlow` flag indicates whether the branching is within the limit * specified by the configuration. */ -private predicate flowOutOfCallable( - NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config ) { - exists(Node n1, Node n2 | - n1 = node1.getNode() and - n2 = node2.getNode() and - flowOutOfCallable(n1, n2, config) and - exists(int b, int j | - b = branch(n1, config) and - j = join(n2, config) and - if b.minimum(j) <= config.fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) + flowOutOfCallNodeCand1(call, ret, out, config) and + exists(int b, int j | + b = branch(ret, config) and + j = join(out, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false ) } /** - * Holds if data can flow into a callable and that this step is part of a + * Holds if data can flow into `call` and that this step is part of a * path from a source to a sink. The `allowsFieldFlow` flag indicates whether * the branching is within the limit specified by the configuration. */ -private predicate flowIntoCallable( - NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + Configuration config ) { - exists(Node n1, Node n2 | - n1 = node1.getNode() and - n2 = node2.getNode() and - flowIntoCallable(n1, n2, config) and - exists(int b, int j | - b = branch(n1, config) and - j = join(n2, config) and - if b.minimum(j) <= config.fieldFlowBranchLimit() - then allowsFieldFlow = true - else allowsFieldFlow = false - ) + flowIntoCallNodeCand1(call, arg, p, config) and + exists(int b, int j | + b = branch(arg, config) and + j = join(p, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false ) } /** - * Holds if `node` is part of a path from a source to a sink in the given - * configuration taking simple call contexts into consideration. + * Holds if `node` is reachable from a source in the configuration `config`. + * The Boolean `stored` records whether the tracked value is stored into a + * field of `node`. + * + * The Boolean `fromArg` records whether the node is reached through an + * argument in a call, and if so, `argStored` records whether the tracked + * value was stored into a field of the argument. */ -private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) { - nodeCand1(node.getNode(), config) and - config.isSource(node.getNode()) and +private predicate nodeCandFwd2( + Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config +) { + nodeCand1(node, config) and + config.isSource(node) and fromArg = false and + argStored = TBooleanNone() and stored = false or - node.isCand1(unbind(config)) and + nodeCand1(node, unbind(config)) and ( - exists(NodeExt mid | - nodeCandFwd2(mid, fromArg, stored, config) and - localFlowStepOrFlowThroughCallable(mid, node, config) + exists(Node mid | + nodeCandFwd2(mid, fromArg, argStored, stored, config) and + localFlowStepNodeCand1(mid, node, config) ) or - exists(NodeExt mid | - nodeCandFwd2(mid, fromArg, stored, config) and - additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and + exists(Node mid | + nodeCandFwd2(mid, fromArg, argStored, stored, config) and + additionalLocalFlowStepNodeCand1(mid, node, config) and stored = false ) or - exists(NodeExt mid | - nodeCandFwd2(mid, _, stored, config) and - jumpStepExt(mid, node, config) and - fromArg = false + exists(Node mid | + nodeCandFwd2(mid, _, _, stored, config) and + jumpStep(mid, node, config) and + fromArg = false and + argStored = TBooleanNone() ) or - exists(NodeExt mid | - nodeCandFwd2(mid, _, stored, config) and - additionalJumpStepExt(mid, node, config) and + exists(Node mid | + nodeCandFwd2(mid, _, _, stored, config) and + additionalJumpStep(mid, node, config) and fromArg = false and + argStored = TBooleanNone() and stored = false ) or // store - exists(NodeExt mid, Content f | - nodeCandFwd2(mid, fromArg, _, config) and - storeExt(mid, f, node) and - readStoreCand1(f, unbind(config)) and - stored = true - ) - or - // taint store - exists(NodeExt mid, Content f | - nodeCandFwd2(mid, fromArg, false, config) and - argumentFlowsThrough(mid, node, TSummaryTaintStore(f), config) and - readStoreCand1(f, unbind(config)) and + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, argStored, _, config) and + store(mid, f, node, config) and stored = true ) or // read exists(Content f | - nodeCandFwd2Read(f, node, fromArg, config) and - storeCandFwd2(f, config) and - (stored = false or stored = true) + nodeCandFwd2Read(f, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(f, stored, config) ) or - // read taint - exists(Content f | - nodeCandFwd2ReadTaint(f, node, fromArg, config) and - storeCandFwd2(f, config) and - stored = false - ) + // flow into a callable + nodeCandFwd2In(_, node, _, _, stored, config) and + fromArg = true and + if parameterThroughFlowNodeCand1(node, config) + then argStored = TBooleanSome(stored) + else argStored = TBooleanNone() or - exists(NodeExt mid, boolean allowsFieldFlow | - nodeCandFwd2(mid, _, stored, config) and - flowIntoCallable(mid, node, allowsFieldFlow, config) and - fromArg = true and - (stored = false or allowsFieldFlow = true) - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - nodeCandFwd2(mid, false, stored, config) and - flowOutOfCallable(mid, node, allowsFieldFlow, config) and - fromArg = false and - (stored = false or allowsFieldFlow = true) + // flow out of a callable + exists(DataFlowCall call | + nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and + fromArg = false + or + exists(boolean argStored0 | + nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and + nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config) + ) ) ) } @@ -1048,143 +784,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate storeCandFwd2(Content f, Configuration config) { - exists(NodeExt mid, NodeExt node | +private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { + exists(Node mid, Node node | useFieldFlow(config) and - node.isCand1(unbind(config)) and - nodeCandFwd2(mid, _, _, config) and - storeExt(mid, f, node) and - readStoreCand1(f, unbind(config)) + nodeCand1(node, unbind(config)) and + nodeCandFwd2(mid, _, _, stored, config) and + store(mid, f, node, config) ) } pragma[nomagic] -private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) { - exists(NodeExt mid | - nodeCandFwd2(mid, fromArg, true, config) and - readExt(mid, f, node) and - readStoreCand1(f, unbind(config)) - ) -} - -pragma[nomagic] -private predicate nodeCandFwd2ReadTaint( - Content f, NodeExt node, boolean fromArg, Configuration config +private predicate nodeCandFwd2Read( + Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { - exists(NodeExt mid | - nodeCandFwd2(mid, fromArg, true, config) and - argumentFlowsThrough(mid, node, TSummaryReadTaint(f), config) and - readStoreCand1(f, unbind(config)) + exists(Node mid | + nodeCandFwd2(mid, fromArg, argStored, true, config) and + read(mid, f, node, config) ) } -private predicate readCandFwd2(Content f, Configuration config) { - exists(NodeExt node | - nodeCandFwd2Read(f, node, _, config) or - nodeCandFwd2ReadTaint(f, node, _, config) +pragma[nomagic] +private predicate nodeCandFwd2In( + DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored, + Configuration config +) { + exists(ArgumentNode arg, boolean allowsFieldFlow | + nodeCandFwd2(arg, fromArg, argStored, stored, config) and + flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config) | - nodeCandFwd2(node, _, _, config) + stored = false or allowsFieldFlow = true ) } -private predicate readStoreCandFwd2(Content f, Configuration config) { - readCandFwd2(f, config) and - storeCandFwd2(f, config) +pragma[nomagic] +private predicate nodeCandFwd2Out( + DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored, + Configuration config +) { + exists(ReturnNodeExt ret, boolean allowsFieldFlow | + nodeCandFwd2(ret, fromArg, argStored, stored, config) and + flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config) + | + stored = false or allowsFieldFlow = true + ) } -private predicate summaryFwd2(Summary s, Configuration config) { - // No need for the `s = TSummaryTaint()` case as it is not used with `argumentFlowsThroughFwd2`. - exists(Content f | s = TSummaryReadTaint(f) | readStoreCandFwd2(f, config)) - or - exists(Content f | s = TSummaryTaintStore(f) | readStoreCandFwd2(f, config)) -} - -private predicate argumentFlowsThroughFwd2(NodeExt n1, NodeExt n2, Summary s, Configuration config) { - argumentFlowsThrough(n1, n2, s, config) and - nodeCandFwd2(n1, _, _, config) and - nodeCandFwd2(n2, _, _, unbind(config)) and - summaryFwd2(s, unbind(config)) +pragma[nomagic] +private predicate nodeCandFwd2OutFromArg( + DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config +) { + nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config) } /** - * Holds if `node` is part of a path from a source to a sink in the given - * configuration taking simple call contexts into consideration. + * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`. */ -private predicate nodeCand2(NodeExt node, boolean toReturn, boolean stored, Configuration config) { - nodeCandFwd2(node, _, false, config) and - config.isSink(node.getNode()) and +pragma[nomagic] +private predicate nodeCandFwd2IsEntered( + DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config +) { + exists(ParameterNode p | + nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and + parameterThroughFlowNodeCand1(p, config) + ) +} + +/** + * Holds if `node` is part of a path from a source to a sink in the + * configuration `config`. The Boolean `read` records whether the tracked + * value must be read from a field of `node` in order to reach a sink. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink, and if so, `returnRead` + * records whether a field must be read from the returned value. + */ +private predicate nodeCand2( + Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config +) { + nodeCandFwd2(node, _, _, false, config) and + config.isSink(node) and toReturn = false and - stored = false + returnRead = TBooleanNone() and + read = false or - nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and + nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and ( - exists(NodeExt mid | - localFlowStepOrFlowThroughCallable(node, mid, config) and - nodeCand2(mid, toReturn, stored, config) + exists(Node mid | + localFlowStepNodeCand1(node, mid, config) and + nodeCand2(mid, toReturn, returnRead, read, config) ) or - exists(NodeExt mid | - additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and - nodeCand2(mid, toReturn, stored, config) and - stored = false + exists(Node mid | + additionalLocalFlowStepNodeCand1(node, mid, config) and + nodeCand2(mid, toReturn, returnRead, read, config) and + read = false ) or - exists(NodeExt mid | - jumpStepExt(node, mid, config) and - nodeCand2(mid, _, stored, config) and - toReturn = false - ) - or - exists(NodeExt mid | - additionalJumpStepExt(node, mid, config) and - nodeCand2(mid, _, stored, config) and + exists(Node mid | + jumpStep(node, mid, config) and + nodeCand2(mid, _, _, read, config) and toReturn = false and - stored = false + returnRead = TBooleanNone() + ) + or + exists(Node mid | + additionalJumpStep(node, mid, config) and + nodeCand2(mid, _, _, read, config) and + toReturn = false and + returnRead = TBooleanNone() and + read = false ) or // store exists(Content f | - nodeCand2Store(f, node, toReturn, config) and - readCand2(f, config) and - (stored = false or stored = true) - ) - or - // taint store - exists(Content f | - nodeCand2TaintStore(f, node, toReturn, config) and - readCand2(f, config) and - stored = false + nodeCand2Store(f, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(f, read, config) ) or // read - exists(NodeExt mid, Content f | - readExt(node, f, mid) and - storeCandFwd2(f, unbind(config)) and - nodeCand2(mid, toReturn, _, config) and - stored = true + exists(Node mid, Content f, boolean read0 | + read(node, f, mid, config) and + nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + nodeCand2(mid, toReturn, returnRead, read0, config) and + read = true ) or - // read taint - exists(NodeExt mid, Content f | - argumentFlowsThroughFwd2(node, mid, TSummaryReadTaint(f), config) and - storeCandFwd2(f, unbind(config)) and - nodeCand2(mid, toReturn, false, config) and - stored = true + // flow into a callable + exists(DataFlowCall call | + nodeCand2In(call, node, toReturn, returnRead, read, config) and + toReturn = false + or + exists(boolean returnRead0 | + nodeCand2InToReturn(call, node, returnRead0, read, config) and + nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config) + ) ) or - exists(NodeExt mid, boolean allowsFieldFlow | - flowIntoCallable(node, mid, allowsFieldFlow, config) and - nodeCand2(mid, false, stored, config) and - toReturn = false and - (stored = false or allowsFieldFlow = true) - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - flowOutOfCallable(node, mid, allowsFieldFlow, config) and - nodeCand2(mid, _, stored, config) and - toReturn = true and - (stored = false or allowsFieldFlow = true) - ) + // flow out of a callable + nodeCand2Out(_, node, _, _, read, config) and + toReturn = true and + if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config) + then returnRead = TBooleanSome(read) + else returnRead = TBooleanNone() ) } @@ -1192,41 +933,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean stored, Conf * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate readCand2(Content f, Configuration config) { - exists(NodeExt mid, NodeExt node | +private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { + exists(Node mid, Node node | useFieldFlow(config) and - nodeCandFwd2(node, _, true, unbind(config)) and - readExt(node, f, mid) and - storeCandFwd2(f, unbind(config)) and - nodeCand2(mid, _, _, config) + nodeCandFwd2(node, _, _, true, unbind(config)) and + read(node, f, mid, config) and + nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] -private predicate nodeCand2Store(Content f, NodeExt node, boolean toReturn, Configuration config) { - exists(NodeExt mid | - storeExt(node, f, mid) and - nodeCand2(mid, toReturn, true, config) - ) -} - -pragma[nomagic] -private predicate nodeCand2TaintStore( - Content f, NodeExt node, boolean toReturn, Configuration config +private predicate nodeCand2Store( + Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Configuration config ) { - exists(NodeExt mid | - argumentFlowsThroughFwd2(node, mid, TSummaryTaintStore(f), config) and - nodeCand2(mid, toReturn, true, config) + exists(Node mid | + store(node, f, mid, config) and + nodeCand2(mid, toReturn, returnRead, true, config) and + nodeCandFwd2(node, _, _, stored, unbind(config)) ) } +/** + * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + */ pragma[nomagic] -private predicate storeCand(Content f, Configuration conf) { - exists(NodeExt node | - nodeCand2Store(f, node, _, conf) or - nodeCand2TaintStore(f, node, _, conf) - | - nodeCand2(node, _, _, conf) +private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { + exists(Node node | + nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2(node, _, _, stored, conf) ) } @@ -1235,475 +971,537 @@ private predicate storeCand(Content f, Configuration conf) { * covered by `nodeCand2`. */ pragma[noinline] -private predicate readStoreCand(Content f, Configuration conf) { - storeCand(f, conf) and - readCand2(f, conf) +private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { + exists(boolean apNonEmpty | + nodeCand2IsStored(f, apNonEmpty, conf) and + nodeCand2IsRead(f, apNonEmpty, conf) + ) } -private predicate nodeCand(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) } - -private predicate summary2(Summary s, Configuration config) { - s = TSummaryTaint() - or - exists(Content f | s = TSummaryReadTaint(f) | readStoreCand(f, config)) - or - exists(Content f | s = TSummaryTaintStore(f) | readStoreCand(f, config)) -} - -private predicate argumentFlowsThrough2( - NodeExt n1, NodeExt n2, DataFlowType t1, DataFlowType t2, Summary s, Configuration config +pragma[nomagic] +private predicate nodeCand2Out( + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read, + Configuration config ) { - argumentFlowsThrough(n1.getNode(), n2.getNode(), t1, t2, s, config) and - nodeCand(n1, config) and - nodeCand(n2, unbind(config)) and - summary2(s, unbind(config)) -} - -/** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ -private predicate localFlowEntry(Node node, Configuration config) { - nodeCand(TNormalNode(node), config) and - ( - config.isSource(node) or - jumpStep(_, node, config) or - additionalJumpStep(_, node, config) or - node instanceof ParameterNode or - node instanceof OutNode or - node instanceof PostUpdateNode or - readDirect(_, _, node) or - node instanceof CastNode + exists(Node out, boolean allowsFieldFlow | + nodeCand2(out, toReturn, returnRead, read, config) and + flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config) + | + read = false or allowsFieldFlow = true ) } -/** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ -private predicate localFlowExit(Node node, Configuration config) { - exists(Node next | nodeCand(TNormalNode(next), config) | - jumpStep(node, next, config) or - additionalJumpStep(node, next, config) or - flowIntoCallable(node, next, config) or - flowOutOfCallable(node, next, config) or - argumentFlowsThrough2(TNormalNode(node), TNormalNode(next), _, _, _, config) or - argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or - storeDirect(node, _, next) or - readDirect(node, _, next) +pragma[nomagic] +private predicate nodeCand2In( + DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read, + Configuration config +) { + exists(ParameterNode p, boolean allowsFieldFlow | + nodeCand2(p, toReturn, returnRead, read, config) and + flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config) + | + read = false or allowsFieldFlow = true ) - or - node instanceof CastNode - or - config.isSink(node) +} + +pragma[nomagic] +private predicate nodeCand2InToReturn( + DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config +) { + nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config) } /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. + * Holds if an output from `call` is reached in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate localFlowStepPlus( - Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc +private predicate nodeCand2IsReturned( + DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and - ( - localFlowEntry(node1, config) and + exists(ReturnNodeExt ret | + nodeCand2Out(call, ret, toReturn, returnRead, read, config) and + nodeCandFwd2(ret, true, TBooleanSome(_), read, config) + ) +} + +private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) } + +pragma[nomagic] +private predicate flowOutOfCallNodeCand2( + DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + nodeCand2(node2, config) and + nodeCand2(node1, unbind(config)) +} + +pragma[nomagic] +private predicate flowIntoCallNodeCand2( + DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + Configuration config +) { + flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + nodeCand2(node2, config) and + nodeCand2(node1, unbind(config)) +} + +private module LocalFlowBigStep { + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowEntry(Node node, Configuration config) { + nodeCand2(node, config) and ( - localFlowStep(node1, node2, config) and preservesValue = true + config.isSource(node) or + jumpStep(_, node, config) or + additionalJumpStep(_, node, config) or + node instanceof ParameterNode or + node instanceof OutNodeExt or + store(_, _, node) or + read(_, _, node) or + node instanceof CastNode + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(Node node, Configuration config) { + exists(Node next | nodeCand2(next, config) | + jumpStep(node, next, config) or + additionalJumpStep(node, next, config) or + flowIntoCallNodeCand1(_, node, next, config) or + flowOutOfCallNodeCand1(_, node, next, config) or + store(node, _, next) or + read(node, _, next) + ) + or + node instanceof CastNode + or + config.isSink(node) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) { + additionalLocalFlowStepNodeCand1(node1, node2, config) and + nodeCand2(node1, _, _, false, config) and + nodeCand2(node2, _, _, false, unbind(config)) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, + LocalCallContext cc + ) { + not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + ( + localFlowEntry(node1, config) and + ( + localFlowStepNodeCand1(node1, node2, config) and + preservesValue = true and + t = getErasedNodeTypeBound(node1) + or + additionalLocalFlowStepNodeCand2(node1, node2, config) and + preservesValue = false and + t = getErasedNodeTypeBound(node2) + ) and + node1 != node2 and + cc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + nodeCand2(node2, unbind(config)) or - additionalLocalFlowStep(node1, node2, config) and preservesValue = false - ) and - node1 != node2 and - cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and - nodeCand(TNormalNode(node2), unbind(config)) - or - exists(Node mid | - localFlowStepPlus(node1, mid, preservesValue, config, cc) and - localFlowStep(mid, node2, config) and - not mid instanceof CastNode and - nodeCand(TNormalNode(node2), unbind(config)) + exists(Node mid | + localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and + localFlowStepNodeCand1(mid, node2, config) and + not mid instanceof CastNode and + nodeCand2(node2, unbind(config)) + ) + or + exists(Node mid | + localFlowStepPlus(node1, mid, _, _, config, cc) and + additionalLocalFlowStepNodeCand2(mid, node2, config) and + not mid instanceof CastNode and + preservesValue = false and + t = getErasedNodeTypeBound(node2) and + nodeCand2(node2, unbind(config)) + ) ) - or - exists(Node mid | - localFlowStepPlus(node1, mid, _, config, cc) and - additionalLocalFlowStep(mid, node2, config) and - not mid instanceof CastNode and - preservesValue = false and - nodeCand(TNormalNode(node2), unbind(config)) - ) - ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config, + LocalCallContext callContext + ) { + localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and + localFlowExit(node2, config) + } } -/** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ +private import LocalFlowBigStep + pragma[nomagic] -private predicate localFlowBigStep( - Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext -) { - localFlowStepPlus(node1, node2, preservesValue, config, callContext) and - localFlowExit(node2, config) +private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { + read(node1, f, node2, config) and + nodeCand2(node1, _, _, true, unbind(config)) and + nodeCand2(node2, config) and + nodeCand2IsReadAndStored(f, unbind(config)) } pragma[nomagic] -private predicate localFlowBigStepExt( - NodeExt node1, NodeExt node2, boolean preservesValue, Configuration config -) { - localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, config, _) +private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { + store(node1, f, node2, config) and + nodeCand2(node1, config) and + nodeCand2(node2, _, _, true, unbind(config)) and + nodeCand2IsReadAndStored(f, unbind(config)) } -private newtype TAccessPathFront = - TFrontNil(DataFlowType t) or - TFrontHead(Content f) - /** - * The front of an `AccessPath`. This is either a head or a nil. - */ -private class AccessPathFront extends TAccessPathFront { - string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - or - exists(Content f | this = TFrontHead(f) | result = f.toString()) - } - - DataFlowType getType() { - this = TFrontNil(result) - or - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } - - predicate headUsesContent(Content f) { this = TFrontHead(f) } -} - -private class AccessPathFrontNil extends AccessPathFront, TFrontNil { } - -/** - * Holds if data can flow from a source to `node` with the given `apf`. + * Holds if `node` is reachable with access path front `apf` from a + * source in the configuration `config`. + * + * The Boolean `fromArg` records whether the node is reached through an + * argument in a call, and if so, `argApf` records the front of the + * access path of that argument. */ +pragma[nomagic] private predicate flowCandFwd( - NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config + Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf, + Configuration config ) { - flowCandFwd0(node, fromArg, apf, config) and - if node instanceof CastingNodeExt - then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType()) + flowCandFwd0(node, fromArg, argApf, apf, config) and + if node instanceof CastingNode + then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) else any() } -/** - * A node that requires an empty access path and should have its tracked type - * (re-)computed. This is either a source or a node reached through an - * additional step. - */ -private class AccessPathFrontNilNode extends NormalNodeExt { - AccessPathFrontNilNode() { - nodeCand(this, _) and - ( - any(Configuration c).isSource(this.getNode()) - or - localFlowBigStepExt(_, this, false, _) - or - additionalJumpStepExt(_, this, _) - ) - } - - /** Gets the `nil` path front for this node. */ - AccessPathFrontNil getApf() { result = TFrontNil(this.getErasedNodeTypeBound()) } -} - +pragma[nomagic] private predicate flowCandFwd0( - NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config + Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf, + Configuration config ) { - nodeCand2(node, _, false, config) and - config.isSource(node.getNode()) and + nodeCand2(node, _, _, false, config) and + config.isSource(node) and fromArg = false and - apf = node.(AccessPathFrontNilNode).getApf() + argApf = TAccessPathFrontNone() and + apf = TFrontNil(getErasedNodeTypeBound(node)) or - nodeCand(node, unbind(config)) and - ( - exists(NodeExt mid | - flowCandFwd(mid, fromArg, apf, config) and - localFlowBigStepExt(mid, node, true, config) - ) - or - exists(NodeExt mid, AccessPathFrontNil nil | - flowCandFwd(mid, fromArg, nil, config) and - localFlowBigStepExt(mid, node, false, config) and - apf = node.(AccessPathFrontNilNode).getApf() - ) - or - exists(NodeExt mid | - flowCandFwd(mid, _, apf, config) and - jumpStepExt(mid, node, config) and - fromArg = false - ) - or - exists(NodeExt mid, AccessPathFrontNil nil | - flowCandFwd(mid, _, nil, config) and - additionalJumpStepExt(mid, node, config) and - fromArg = false and - apf = node.(AccessPathFrontNilNode).getApf() - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - flowCandFwd(mid, _, apf, config) and - flowIntoCallable(mid, node, allowsFieldFlow, config) and - fromArg = true and - (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - flowCandFwd(mid, false, apf, config) and - flowOutOfCallable(mid, node, allowsFieldFlow, config) and - fromArg = false and - (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) - ) - or - exists(NodeExt mid | - flowCandFwd(mid, fromArg, apf, config) and - argumentValueFlowsThrough(mid, node) - ) - or - exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t | - flowCandFwd(mid, fromArg, nil, config) and - argumentFlowsThrough2(mid, node, _, t, TSummaryTaint(), config) and - apf = TFrontNil(t) - ) + exists(Node mid | + flowCandFwd(mid, fromArg, argApf, apf, config) and + localFlowBigStep(mid, node, true, _, config, _) ) or - exists(NodeExt mid, Content f | - flowCandFwd(mid, fromArg, _, config) and - storeExt(mid, f, node) and - nodeCand(node, unbind(config)) and - readStoreCand(f, unbind(config)) and - apf.headUsesContent(f) - ) - or - exists(NodeExt mid, AccessPathFrontNil nil, Content f | - flowCandFwd(mid, fromArg, nil, config) and - argumentFlowsThrough2(mid, node, _, _, TSummaryTaintStore(f), config) and + exists(Node mid, AccessPathFrontNil nil | + flowCandFwd(mid, fromArg, argApf, nil, config) and + localFlowBigStep(mid, node, false, apf, config, _) + ) + or + exists(Node mid | + flowCandFwd(mid, _, _, apf, config) and + nodeCand2(node, unbind(config)) and + jumpStep(mid, node, config) and + fromArg = false and + argApf = TAccessPathFrontNone() + ) + or + exists(Node mid, AccessPathFrontNil nil | + flowCandFwd(mid, _, _, nil, config) and + nodeCand2(node, unbind(config)) and + additionalJumpStep(mid, node, config) and + fromArg = false and + argApf = TAccessPathFrontNone() and + apf = TFrontNil(getErasedNodeTypeBound(node)) + ) + or + // store + exists(Node mid, Content f | + flowCandFwd(mid, fromArg, argApf, _, config) and + storeCand2(mid, f, node, config) and + nodeCand2(node, _, _, true, unbind(config)) and apf.headUsesContent(f) ) or + // read exists(Content f | - flowCandFwdRead(f, node, fromArg, config) and - consCandFwd(f, apf, config) + flowCandFwdRead(f, node, fromArg, argApf, config) and + flowCandFwdConsCand(f, apf, config) and + nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or - exists(Content f, AccessPathFrontNil nil, DataFlowType t | - flowCandFwdReadTaint(f, node, fromArg, t, config) and - consCandFwd(f, nil, config) and - apf = TFrontNil(t) + // flow into a callable + flowCandFwdIn(_, node, _, _, apf, config) and + fromArg = true and + if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config) + then argApf = TAccessPathFrontSome(apf) + else argApf = TAccessPathFrontNone() + or + // flow out of a callable + exists(DataFlowCall call | + flowCandFwdOut(call, node, fromArg, argApf, apf, config) and + fromArg = false + or + exists(AccessPathFront argApf0 | + flowCandFwdOutFromArg(call, node, argApf0, apf, config) and + flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config) + ) ) } -pragma[noinline] -private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) { - exists(NodeExt mid, NodeExt n | - flowCandFwd(mid, _, apf, config) and - storeExt(mid, f, n) and - nodeCand(n, unbind(config)) and - readStoreCand(f, unbind(config)) and +pragma[nomagic] +private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n | + flowCandFwd(mid, _, _, apf, config) and + storeCand2(mid, f, n, config) and + nodeCand2(n, _, _, true, unbind(config)) and compatibleTypes(apf.getType(), f.getType()) ) } pragma[nomagic] -private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) { - exists(NodeExt mid, AccessPathFront apf | - flowCandFwd(mid, fromArg, apf, config) and - readExt(mid, f, node) and - apf.headUsesContent(f) and - nodeCand(node, unbind(config)) +private predicate flowCandFwdRead( + Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config +) { + exists(Node mid, AccessPathFrontHead apf0 | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + readCand2(mid, f, node, config) and + apf0.headUsesContent(f) ) } pragma[nomagic] -private predicate flowCandFwdReadTaint( - Content f, NodeExt node, boolean fromArg, DataFlowType t, Configuration config +private predicate flowCandFwdIn( + DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFront apf, Configuration config ) { - exists(NodeExt mid, AccessPathFront apf | - flowCandFwd(mid, fromArg, apf, config) and - argumentFlowsThrough2(mid, node, _, t, TSummaryReadTaint(f), config) and - apf.headUsesContent(f) + exists(ArgumentNode arg, boolean allowsFieldFlow | + flowCandFwd(arg, fromArg, argApf, apf, config) and + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + | + apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) } -pragma[noinline] -private predicate flowCandFwdEmptyAp(NodeExt node, Configuration config) { - flowCandFwd(node, _, any(AccessPathFrontNil nil), config) -} - -pragma[noinline] -private predicate consCandFwdEmptyAp(Content f, Configuration config) { - consCandFwd(f, any(AccessPathFrontNil nil), config) -} - -private predicate argumentFlowsThrough3( - NodeExt n1, NodeExt n2, DataFlowType t1, DataFlowType t2, Summary s, Configuration config +pragma[nomagic] +private predicate flowCandFwdOut( + DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf, + Configuration config ) { - argumentFlowsThrough2(n1, n2, t1, t2, s, config) and - flowCandFwdEmptyAp(n1, config) and - flowCandFwdEmptyAp(n2, unbind(config)) and - s = TSummaryTaint() - or - exists(Content f, AccessPathFront apf | - argumentFlowsThrough2(n1, n2, t1, t2, s, config) and - flowCandFwdEmptyAp(n1, config) and - flowCandFwd(n2, _, apf, unbind(config)) and - s = TSummaryTaintStore(f) and - consCandFwdEmptyAp(f, unbind(config)) and - apf.headUsesContent(f) + exists(ReturnNodeExt ret, boolean allowsFieldFlow | + flowCandFwd(ret, fromArg, argApf, apf, config) and + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) + | + apf instanceof AccessPathFrontNil or allowsFieldFlow = true ) - or - exists(Content f, AccessPathFront apf | - argumentFlowsThrough2(n1, n2, t1, t2, s, config) and - flowCandFwd(n1, _, apf, config) and - flowCandFwdEmptyAp(n2, unbind(config)) and - s = TSummaryReadTaint(f) and - consCandFwdEmptyAp(f, unbind(config)) and - apf.headUsesContent(f) +} + +pragma[nomagic] +private predicate flowCandFwdOutFromArg( + DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config +) { + flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config) +} + +/** + * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`. + */ +pragma[nomagic] +private predicate flowCandFwdIsEntered( + DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf, + Configuration config +) { + exists(ParameterNode p | + flowCandFwdIn(call, p, fromArg, argApf, apf, config) and + nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config) ) } /** - * Holds if data can flow from a source to `node` with the given `apf` and - * from there flow to a sink. + * Holds if `node` with access path front `apf` is part of a path from a + * source to a sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink, and if so, `returnApf` + * records the front of the access path of the returned value. */ -private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) { - flowCand0(node, toReturn, apf, config) and - flowCandFwd(node, _, apf, config) +pragma[nomagic] +private predicate flowCand( + Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf, + Configuration config +) { + flowCand0(node, toReturn, returnApf, apf, config) and + flowCandFwd(node, _, _, apf, config) } +pragma[nomagic] private predicate flowCand0( - NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config + Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf, + Configuration config ) { - flowCandFwd(node, _, apf, config) and - config.isSink(node.getNode()) and + flowCandFwd(node, _, _, apf, config) and + config.isSink(node) and toReturn = false and + returnApf = TAccessPathFrontNone() and apf instanceof AccessPathFrontNil or - exists(NodeExt mid | - localFlowBigStepExt(node, mid, true, config) and - flowCand(mid, toReturn, apf, config) + exists(Node mid | + localFlowBigStep(node, mid, true, _, config, _) and + flowCand(mid, toReturn, returnApf, apf, config) ) or - exists(NodeExt mid, AccessPathFrontNil nil | - flowCandFwd(node, _, apf, config) and - localFlowBigStepExt(node, mid, false, config) and - flowCand(mid, toReturn, nil, config) and + exists(Node mid, AccessPathFrontNil nil | + flowCandFwd(node, _, _, apf, config) and + localFlowBigStep(node, mid, false, _, config, _) and + flowCand(mid, toReturn, returnApf, nil, config) and apf instanceof AccessPathFrontNil ) or - exists(NodeExt mid | - jumpStepExt(node, mid, config) and - flowCand(mid, _, apf, config) and + exists(Node mid | + jumpStep(node, mid, config) and + flowCand(mid, _, _, apf, config) and + toReturn = false and + returnApf = TAccessPathFrontNone() + ) + or + exists(Node mid, AccessPathFrontNil nil | + flowCandFwd(node, _, _, apf, config) and + additionalJumpStep(node, mid, config) and + flowCand(mid, _, _, nil, config) and + toReturn = false and + returnApf = TAccessPathFrontNone() and + apf instanceof AccessPathFrontNil + ) + or + // store + exists(Content f, AccessPathFrontHead apf0 | + flowCandStore(node, f, toReturn, returnApf, apf0, config) and + apf0.headUsesContent(f) and + flowCandConsCand(f, apf, config) + ) + or + // read + exists(Content f, AccessPathFront apf0 | + flowCandRead(node, f, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(f, apf0, config) and + apf.headUsesContent(f) + ) + or + // flow into a callable + exists(DataFlowCall call | + flowCandIn(call, node, toReturn, returnApf, apf, config) and toReturn = false + or + exists(AccessPathFront returnApf0 | + flowCandInToReturn(call, node, returnApf0, apf, config) and + flowCandIsReturned(call, toReturn, returnApf, returnApf0, config) + ) ) or - exists(NodeExt mid, AccessPathFrontNil nil | - flowCandFwd(node, _, apf, config) and - additionalJumpStepExt(node, mid, config) and - flowCand(mid, _, nil, config) and - toReturn = false and - apf instanceof AccessPathFrontNil - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - flowIntoCallable(node, mid, allowsFieldFlow, config) and - flowCand(mid, false, apf, config) and - toReturn = false and - (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - flowOutOfCallable(node, mid, allowsFieldFlow, config) and - flowCand(mid, _, apf, config) and - toReturn = true and - (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) - ) - or - exists(NodeExt mid | - argumentValueFlowsThrough(node, mid) and - flowCand(mid, toReturn, apf, config) - ) - or - exists(NodeExt mid, AccessPathFrontNil nil | - argumentFlowsThrough3(node, mid, _, _, TSummaryTaint(), config) and - flowCand(mid, toReturn, nil, config) and - apf instanceof AccessPathFrontNil and - flowCandFwd(node, _, apf, config) - ) - or - exists(Content f, AccessPathFront apf0 | - flowCandStore(node, f, toReturn, apf0, config) and - apf0.headUsesContent(f) and - consCand(f, apf, config) - ) - or - exists(NodeExt mid, Content f, AccessPathFront apf0, AccessPathFrontNil nil | - flowCandFwd(node, _, apf, config) and - apf instanceof AccessPathFrontNil and - argumentFlowsThrough3(node, mid, _, _, TSummaryTaintStore(f), config) and - flowCand(mid, toReturn, apf0, config) and - apf0.headUsesContent(f) and - consCand(f, nil, unbind(config)) - ) - or - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, apf0, config) and - consCandFwd(f, apf0, config) and - apf.headUsesContent(f) - ) - or - exists(NodeExt mid, AccessPathFrontNil nil1, AccessPathFrontNil nil2, Content f | - argumentFlowsThrough3(node, mid, _, _, TSummaryReadTaint(f), config) and - flowCand(mid, toReturn, nil1, config) and - consCandFwd(f, nil2, unbind(config)) and - apf.headUsesContent(f) - ) + // flow out of a callable + flowCandOut(_, node, _, _, apf, config) and + toReturn = true and + if flowCandFwd(node, true, _, apf, config) + then returnApf = TAccessPathFrontSome(apf) + else returnApf = TAccessPathFrontNone() } pragma[nomagic] private predicate flowCandRead( - NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config + Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, + Configuration config ) { - exists(NodeExt mid | - readExt(node, f, mid) and - flowCand(mid, toReturn, apf0, config) + exists(Node mid | + readCand2(node, f, mid, config) and + flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config + Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, + Configuration config ) { - exists(NodeExt mid | - storeExt(node, f, mid) and - flowCand(mid, toReturn, apf0, config) + exists(Node mid | + storeCand2(node, f, mid, config) and + flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] -private predicate consCand(Content f, AccessPathFront apf, Configuration config) { - consCandFwd(f, apf, config) and - exists(NodeExt n, AccessPathFront apf0 | - flowCandFwd(n, _, apf0, config) and +private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(f, apf, config) and + exists(Node n, AccessPathFrontHead apf0 | + flowCandFwd(n, _, _, apf0, config) and apf0.headUsesContent(f) and - flowCandRead(n, f, _, apf, config) + flowCandRead(n, f, _, _, apf, config) + ) +} + +pragma[nomagic] +private predicate flowCandOut( + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf, + AccessPathFront apf, Configuration config +) { + exists(Node out, boolean allowsFieldFlow | + flowCand(out, toReturn, returnApf, apf, config) and + flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) + | + apf instanceof AccessPathFrontNil or allowsFieldFlow = true + ) +} + +pragma[nomagic] +private predicate flowCandIn( + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf, + AccessPathFront apf, Configuration config +) { + exists(ParameterNode p, boolean allowsFieldFlow | + flowCand(p, toReturn, returnApf, apf, config) and + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + | + apf instanceof AccessPathFrontNil or allowsFieldFlow = true + ) +} + +pragma[nomagic] +private predicate flowCandInToReturn( + DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf, + Configuration config +) { + flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config) +} + +/** + * Holds if an output from `call` is reached in the flow covered by `flowCand`. + */ +pragma[nomagic] +private predicate flowCandIsReturned( + DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf, + Configuration config +) { + exists(ReturnNodeExt ret | + flowCandOut(call, ret, toReturn, returnApf, apf, config) and + flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config) ) } private newtype TAccessPath = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or + TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or TConsCons(Content f1, Content f2, int len) { - consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] } /** @@ -1716,25 +1514,11 @@ private newtype TAccessPath = abstract private class AccessPath extends TAccessPath { abstract string toString(); - Content getHead() { - this = TConsNil(result, _) - or - this = TConsCons(result, _, _) - } + abstract Content getHead(); - int len() { - this = TNil(_) and result = 0 - or - this = TConsNil(_, _) and result = 1 - or - this = TConsCons(_, _, result) - } + abstract int len(); - DataFlowType getType() { - this = TNil(result) - or - result = this.getHead().getContainerType() - } + abstract DataFlowType getType(); abstract AccessPathFront getFront(); @@ -1745,13 +1529,19 @@ abstract private class AccessPath extends TAccessPath { } private class AccessPathNil extends AccessPath, TNil { - override string toString() { - exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t))) - } + private DataFlowType t; - override AccessPathFront getFront() { - exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t)) - } + AccessPathNil() { this = TNil(t) } + + override string toString() { result = concat(": " + ppReprType(t)) } + + override Content getHead() { none() } + + override int len() { result = 0 } + + override DataFlowType getType() { result = t } + + override AccessPathFront getFront() { result = TFrontNil(t) } override predicate pop(Content head, AccessPath tail) { none() } } @@ -1759,41 +1549,55 @@ private class AccessPathNil extends AccessPath, TNil { abstract private class AccessPathCons extends AccessPath { } private class AccessPathConsNil extends AccessPathCons, TConsNil { + private Content f; + private DataFlowType t; + + AccessPathConsNil() { this = TConsNil(f, t) } + override string toString() { - exists(Content f, DataFlowType t | this = TConsNil(f, t) | - // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) - ) + // The `concat` becomes "" if `ppReprType` has no result. + result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) } - override AccessPathFront getFront() { - exists(Content f | this = TConsNil(f, _) | result = TFrontHead(f)) - } + override Content getHead() { result = f } - override predicate pop(Content head, AccessPath tail) { - exists(DataFlowType t | this = TConsNil(head, t) and tail = TNil(t)) - } + override int len() { result = 1 } + + override DataFlowType getType() { result = f.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(f) } + + override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } } private class AccessPathConsCons extends AccessPathCons, TConsCons { + private Content f1; + private Content f2; + private int len; + + AccessPathConsCons() { this = TConsCons(f1, f2, len) } + override string toString() { - exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) | - if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" - ) + if len = 2 + then result = "[" + f1.toString() + ", " + f2.toString() + "]" + else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" } - override AccessPathFront getFront() { - exists(Content f | this = TConsCons(f, _, _) | result = TFrontHead(f)) - } + override Content getHead() { result = f1 } + + override int len() { result = len } + + override DataFlowType getType() { result = f1.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(f1) } override predicate pop(Content head, AccessPath tail) { - exists(int len, Content next | this = TConsCons(head, next, len) | - tail = TConsCons(next, _, len - 1) + head = f1 and + ( + tail = TConsCons(f2, _, len - 1) or len = 2 and - tail = TConsNil(next, _) + tail = TConsNil(f2, _) ) } } @@ -1801,328 +1605,399 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { /** Gets the access path obtained by popping `f` from `ap`, if any. */ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } -/** Holds if `ap0` corresponds to the cons of `f` and `ap` and `apf` is the front of `ap`. */ -pragma[noinline] -private predicate popWithFront(AccessPath ap0, Content f, AccessPathFront apf, AccessPath ap) { - ap = pop(f, ap0) and apf = ap.getFront() -} - /** Gets the access path obtained by pushing `f` onto `ap`. */ private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } -/** - * A node that requires an empty access path and should have its tracked type - * (re-)computed. This is either a source or a node reached through an - * additional step. - */ -private class AccessPathNilNode extends NormalNodeExt { - AccessPathNilNode() { flowCand(this.(AccessPathFrontNilNode), _, _, _) } +private newtype TAccessPathOption = + TAccessPathNone() or + TAccessPathSome(AccessPath ap) - /** Gets the `nil` path for this node. */ - AccessPathNil getAp() { result = TNil(this.getErasedNodeTypeBound()) } +private class AccessPathOption extends TAccessPathOption { + string toString() { + this = TAccessPathNone() and result = "" + or + this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + } } /** - * Holds if data can flow from a source to `node` with the given `ap`. + * Holds if `node` is reachable with access path `ap` from a source in + * the configuration `config`. + * + * The Boolean `fromArg` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. */ private predicate flowFwd( - NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config + Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Configuration config ) { - flowFwd0(node, fromArg, apf, ap, config) and - flowCand(node, _, apf, config) + flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowCand(node, _, _, apf, config) } private predicate flowFwd0( - NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config + Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, + Configuration config ) { - flowCand(node, _, _, config) and - config.isSource(node.getNode()) and + flowCand(node, _, _, _, config) and + config.isSource(node) and fromArg = false and - ap = node.(AccessPathNilNode).getAp() and + argAp = TAccessPathNone() and + ap = TNil(getErasedNodeTypeBound(node)) and apf = ap.(AccessPathNil).getFront() or - flowCand(node, _, _, unbind(config)) and + flowCand(node, _, _, _, unbind(config)) and ( - exists(NodeExt mid | - flowFwd(mid, fromArg, apf, ap, config) and - localFlowBigStepExt(mid, node, true, config) + exists(Node mid | + flowFwd(mid, fromArg, argAp, apf, ap, config) and + localFlowBigStep(mid, node, true, _, config, _) ) or - exists(NodeExt mid, AccessPathNil nil | - flowFwd(mid, fromArg, _, nil, config) and - localFlowBigStepExt(mid, node, false, config) and - ap = node.(AccessPathNilNode).getAp() and + exists(Node mid, AccessPathNil nil | + flowFwd(mid, fromArg, argAp, _, nil, config) and + localFlowBigStep(mid, node, false, apf, config, _) and apf = ap.(AccessPathNil).getFront() ) or - exists(NodeExt mid | - flowFwd(mid, _, apf, ap, config) and - jumpStepExt(mid, node, config) and - fromArg = false - ) - or - exists(NodeExt mid, AccessPathNil nil | - flowFwd(mid, _, _, nil, config) and - additionalJumpStepExt(mid, node, config) and + exists(Node mid | + flowFwd(mid, _, _, apf, ap, config) and + jumpStep(mid, node, config) and fromArg = false and - ap = node.(AccessPathNilNode).getAp() and - apf = ap.(AccessPathNil).getFront() + argAp = TAccessPathNone() ) or - exists(NodeExt mid, boolean allowsFieldFlow | - flowFwd(mid, _, apf, ap, config) and - flowIntoCallable(mid, node, allowsFieldFlow, config) and - fromArg = true and - (ap instanceof AccessPathNil or allowsFieldFlow = true) - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - flowFwd(mid, false, apf, ap, config) and - flowOutOfCallable(mid, node, allowsFieldFlow, config) and + exists(Node mid, AccessPathNil nil | + flowFwd(mid, _, _, _, nil, config) and + additionalJumpStep(mid, node, config) and fromArg = false and - (ap instanceof AccessPathNil or allowsFieldFlow = true) - ) - or - exists(NodeExt mid | - flowFwd(mid, fromArg, apf, ap, config) and - argumentValueFlowsThrough(mid, node) - ) - or - exists(NodeExt mid, AccessPathNil nil, DataFlowType t | - flowFwd(mid, fromArg, _, nil, config) and - argumentFlowsThrough3(mid, node, _, t, TSummaryTaint(), config) and - ap = TNil(t) and + argAp = TAccessPathNone() and + ap = TNil(getErasedNodeTypeBound(node)) and apf = ap.(AccessPathNil).getFront() ) ) or + // store exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, config) and + flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and ap = push(f, ap0) ) or - exists(Content f, AccessPath ap0 | - flowFwdRead(node, f, ap0, fromArg, config) and - popWithFront(ap0, f, apf, ap) + // read + exists(Content f | + flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and + flowFwdConsCand(f, apf, ap, config) ) or - exists(Content f, NodeExt mid, AccessPathFront apf0, DataFlowType t | - flowFwd(mid, fromArg, apf0, any(AccessPathConsNil consnil), config) and - argumentFlowsThrough3(mid, node, _, t, TSummaryReadTaint(f), config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, unbind(config)) and - ap = TNil(t) and - apf = ap.(AccessPathNil).getFront() + // flow into a callable + flowFwdIn(_, node, _, _, apf, ap, config) and + fromArg = true and + if flowCand(node, true, _, apf, config) + then argAp = TAccessPathSome(ap) + else argAp = TAccessPathNone() + or + // flow out of a callable + exists(DataFlowCall call | + flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and + fromArg = false + or + exists(AccessPath argAp0 | + flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and + flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + ) ) } pragma[nomagic] private predicate flowFwdStore( - NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - Configuration config + Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, + AccessPathOption argAp, Configuration config ) { - exists(NodeExt mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, apf0, ap0, config) and + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, argAp, apf0, ap0, config) and flowFwdStore1(mid, f, node, apf0, apf, config) ) - or - exists(NodeExt mid, DataFlowType t | - flowFwd(mid, fromArg, _, any(AccessPathNil nil), config) and - argumentFlowsThrough3(mid, node, t, _, TSummaryTaintStore(f), config) and - consCand(f, TFrontNil(t), unbind(config)) and - ap0 = TNil(t) and - apf.headUsesContent(f) and - flowCand(node, _, apf, unbind(config)) - ) } -pragma[noinline] +pragma[nomagic] private predicate flowFwdStore0( - NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config + Node mid, Content f, Node node, AccessPathFront apf0, Configuration config ) { - storeExt(mid, f, node) and - consCand(f, apf0, config) + storeCand2(mid, f, node, config) and + flowCand(mid, _, _, apf0, config) } pragma[noinline] private predicate flowFwdStore1( - NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFront apf, + Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { flowFwdStore0(mid, f, node, apf0, config) and + flowCandConsCand(f, apf0, config) and apf.headUsesContent(f) and - flowCand(node, _, apf, unbind(config)) + flowCand(node, _, _, apf, unbind(config)) } pragma[nomagic] private predicate flowFwdRead( - NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config + Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, + Configuration config ) { - exists(NodeExt mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, apf0, ap0, config) and - readExt(mid, f, node) and + exists(Node mid, AccessPathFrontHead apf0 | + flowFwd(mid, fromArg, argAp, apf0, ap0, config) and + readCand2(mid, f, node, config) and apf0.headUsesContent(f) and - flowCand(node, _, _, unbind(config)) + flowCand(node, _, _, _, unbind(config)) + ) +} + +pragma[nomagic] +private predicate flowFwdConsCand( + Content f, AccessPathFront apf, AccessPath ap, Configuration config +) { + exists(Node n | + flowFwd(n, _, _, apf, ap, config) and + flowFwdStore1(n, f, _, apf, _, config) + ) +} + +pragma[nomagic] +private predicate flowFwdIn( + DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, + AccessPath ap, Configuration config +) { + exists(ArgumentNode arg, boolean allowsFieldFlow | + flowFwd(arg, fromArg, argAp, apf, ap, config) and + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and + flowCand(p, _, _, _, unbind(config)) + | + ap instanceof AccessPathNil or allowsFieldFlow = true + ) +} + +pragma[nomagic] +private predicate flowFwdOut( + DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, + AccessPath ap, Configuration config +) { + exists(ReturnNodeExt ret, boolean allowsFieldFlow | + flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and + flowCand(node, _, _, _, unbind(config)) + | + ap instanceof AccessPathNil or allowsFieldFlow = true + ) +} + +pragma[nomagic] +private predicate flowFwdOutFromArg( + DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + Configuration config +) { + flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) +} + +/** + * Holds if an argument to `call` is reached in the flow covered by `flowFwd`. + */ +pragma[nomagic] +private predicate flowFwdIsEntered( + DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config +) { + exists(ParameterNode p, AccessPathFront apf | + flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if data can flow from a source to `node` with the given `ap` and - * from there flow to a sink. + * Holds if `node` with access path `ap` is part of a path from a source to + * a sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. */ -private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) { - flow0(node, toReturn, ap, config) and - flowFwd(node, _, _, ap, config) +private predicate flow( + Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config +) { + flow0(node, toReturn, returnAp, ap, config) and + flowFwd(node, _, _, _, ap, config) } -private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) { - flowFwd(node, _, _, ap, config) and - config.isSink(node.getNode()) and +private predicate flow0( + Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config +) { + flowFwd(node, _, _, _, ap, config) and + config.isSink(node) and toReturn = false and + returnAp = TAccessPathNone() and ap instanceof AccessPathNil or - exists(NodeExt mid | - localFlowBigStepExt(node, mid, true, config) and - flow(mid, toReturn, ap, config) + exists(Node mid | + localFlowBigStep(node, mid, true, _, config, _) and + flow(mid, toReturn, returnAp, ap, config) ) or - exists(NodeExt mid, AccessPathNil nil | - flowFwd(node, _, _, ap, config) and - localFlowBigStepExt(node, mid, false, config) and - flow(mid, toReturn, nil, config) and + exists(Node mid, AccessPathNil nil | + flowFwd(node, _, _, _, ap, config) and + localFlowBigStep(node, mid, false, _, config, _) and + flow(mid, toReturn, returnAp, nil, config) and ap instanceof AccessPathNil ) or - exists(NodeExt mid | - jumpStepExt(node, mid, config) and - flow(mid, _, ap, config) and + exists(Node mid | + jumpStep(node, mid, config) and + flow(mid, _, _, ap, config) and + toReturn = false and + returnAp = TAccessPathNone() + ) + or + exists(Node mid, AccessPathNil nil | + flowFwd(node, _, _, _, ap, config) and + additionalJumpStep(node, mid, config) and + flow(mid, _, _, nil, config) and + toReturn = false and + returnAp = TAccessPathNone() and + ap instanceof AccessPathNil + ) + or + // store + exists(Content f | + flowStore(f, node, toReturn, returnAp, ap, config) and + flowConsCand(f, ap, config) + ) + or + // read + exists(Node mid, AccessPath ap0 | + readFlowFwd(node, _, mid, ap, ap0, config) and + flow(mid, toReturn, returnAp, ap0, config) + ) + or + // flow into a callable + exists(DataFlowCall call | + flowIn(call, node, toReturn, returnAp, ap, config) and toReturn = false + or + exists(AccessPath returnAp0 | + flowInToReturn(call, node, returnAp0, ap, config) and + flowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) ) or - exists(NodeExt mid, AccessPathNil nil | - flowFwd(node, _, _, ap, config) and - additionalJumpStepExt(node, mid, config) and - flow(mid, _, nil, config) and - toReturn = false and - ap instanceof AccessPathNil - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - flowIntoCallable(node, mid, allowsFieldFlow, config) and - flow(mid, false, ap, config) and - toReturn = false and - (ap instanceof AccessPathNil or allowsFieldFlow = true) - ) - or - exists(NodeExt mid, boolean allowsFieldFlow | - flowOutOfCallable(node, mid, allowsFieldFlow, config) and - flow(mid, _, ap, config) and - toReturn = true and - (ap instanceof AccessPathNil or allowsFieldFlow = true) - ) - or - exists(NodeExt mid | - argumentValueFlowsThrough(node, mid) and - flow(mid, toReturn, ap, config) - ) - or - exists(NodeExt mid, AccessPathNil ap0 | - argumentFlowsThrough3(node, mid, _, _, TSummaryTaint(), config) and - flow(mid, toReturn, ap0, config) and - ap instanceof AccessPathNil and - flowFwd(node, _, _, ap, config) - ) - or - exists(NodeExt mid, AccessPath ap0 | - storeFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, ap0, config) - ) - or - exists(Content f, AccessPath ap0 | - flowTaintStore(node, f, toReturn, ap0, config) and - pop(f, ap0) instanceof AccessPathNil and - ap instanceof AccessPathNil and - flowFwd(node, _, _, ap, config) - ) - or - exists(NodeExt mid, AccessPath ap0 | - readFwd(node, mid, ap, ap0, config) and - flow(mid, toReturn, ap0, config) - ) - or - exists(NodeExt mid, Content f | - argumentFlowsThrough3(node, mid, _, _, TSummaryReadTaint(f), config) and - flow(mid, toReturn, any(AccessPathNil nil1), config) and - ap = push(f, any(AccessPathNil nil2)) and - flowFwd(node, _, _, ap, config) - ) + // flow out of a callable + flowOut(_, node, _, _, ap, config) and + toReturn = true and + if flowFwd(node, true, TAccessPathSome(_), _, ap, config) + then returnAp = TAccessPathSome(ap) + else returnAp = TAccessPathNone() } pragma[nomagic] -private predicate storeFwd( - NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config +private predicate storeFlowFwd( + Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - storeExt(node1, f, node2) and - flowFwdStore(node2, f, ap, _, _, config) and + storeCand2(node1, f, node2, config) and + flowFwdStore(node2, f, ap, _, _, _, config) and ap0 = push(f, ap) } pragma[nomagic] -private predicate flowTaintStore( - NodeExt node, Content f, boolean toReturn, AccessPath ap0, Configuration config +private predicate flowStore( + Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + Configuration config ) { - exists(NodeExt mid | - argumentFlowsThrough3(node, mid, _, _, TSummaryTaintStore(f), config) and - flow(mid, toReturn, ap0, config) + exists(Node mid, AccessPath ap0 | + storeFlowFwd(node, f, mid, ap, ap0, config) and + flow(mid, toReturn, returnAp, ap0, config) ) } pragma[nomagic] -private predicate readFwd( - NodeExt node1, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config +private predicate readFlowFwd( + Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config ) { - exists(Content f | - readExt(node1, f, node2) and - flowFwdRead(node2, f, ap, _, config) and - ap0 = pop(f, ap) + readCand2(node1, f, node2, config) and + flowFwdRead(node2, f, ap, _, _, config) and + ap0 = pop(f, ap) and + flowFwdConsCand(f, _, ap0, unbind(config)) +} + +pragma[nomagic] +private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { + exists(Node n, Node mid | + flow(mid, _, _, ap, config) and + readFlowFwd(n, f, mid, _, ap, config) + ) +} + +pragma[nomagic] +private predicate flowOut( + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + Configuration config +) { + exists(Node out, boolean allowsFieldFlow | + flow(out, toReturn, returnAp, ap, config) and + flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) + | + ap instanceof AccessPathNil or allowsFieldFlow = true + ) +} + +pragma[nomagic] +private predicate flowIn( + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + Configuration config +) { + exists(ParameterNode p, boolean allowsFieldFlow | + flow(p, toReturn, returnAp, ap, config) and + flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) + | + ap instanceof AccessPathNil or allowsFieldFlow = true + ) +} + +pragma[nomagic] +private predicate flowInToReturn( + DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config +) { + flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) +} + +/** + * Holds if an output from `call` is reached in the flow covered by `flow`. + */ +pragma[nomagic] +private predicate flowIsReturned( + DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + Configuration config +) { + exists(ReturnNodeExt ret | + flowOut(call, ret, toReturn, returnAp, ap, config) and + flowFwd(ret, true, TAccessPathSome(_), _, ap, config) ) } bindingset[conf, result] private Configuration unbind(Configuration conf) { result >= conf and result <= conf } -private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) } +private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) } + +pragma[noinline] +private predicate parameterFlow( + ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config +) { + flow(p, true, _, ap, config) and + c = p.getEnclosingCallable() +} private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) | - exists(Summary summary | - parameterFlowReturn(p, ret, _, _, _, summary, config) and - flow(ret, unbind(config)) - | - // taint through - summary = TSummaryTaint() and - ap instanceof AccessPathNil - or - // taint setter - summary = TSummaryTaintStore(_) and - ap instanceof AccessPathNil - or - // taint getter - summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead()) - ) - or - exists(ContentOption contentIn | - parameterValueFlowReturn(p, ret, _, contentIn, _) and - flow(ret, unbind(config)) - | - // value through/setter - contentIn = TContentNone() - or - // value getter (+ setter) - contentIn = TContentSome(ap.getHead()) - ) + exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | + parameterFlow(p, ap, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathSome(_), ap0, config) and + flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) ) } @@ -2148,6 +2023,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } + int getParameterPos() { p.isParameterOf(_, result) } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2164,13 +2041,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = any(AccessPathNilNode nil | nil.getNode() = node).getAp() + ap = TNil(getErasedNodeTypeBound(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(TNormalNode(node), _, ap, unbind(config)) + flow(node, _, _, ap, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2350,19 +2227,19 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists( - AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing, - LocalCallContext localCC + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() | - pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and - localCC = getLocalCallContext(cc, enclosing) - | - localFlowBigStep(midnode, node, true, conf, localCC) and + localFlowBigStep(midnode, node, true, _, conf, localCC) and ap = ap0 or - localFlowBigStep(midnode, node, false, conf, localCC) and - ap0 instanceof AccessPathNil and - ap = any(AccessPathNilNode nil | nil.getNode() = node).getAp() + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil ) or jumpStep(mid.getNode(), node, mid.getConfiguration()) and @@ -2374,12 +2251,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = any(AccessPathNilNode nil | nil.getNode() = node).getAp() + ap = TNil(getErasedNodeTypeBound(node)) or - exists(Content f, AccessPath ap0 | pathReadStep(mid, node, ap0, f, cc) and ap = pop(f, ap0)) and + exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and sc = mid.getSummaryCtx() or - exists(Content f, AccessPath ap0 | pathStoreStep(mid, node, ap0, f, cc) and ap = push(f, ap0)) and + exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2389,23 +2266,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } -pragma[nomagic] -private predicate pathIntoLocalStep( - PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc, - AccessPath ap0, Configuration conf -) { - midnode = mid.getNode() and - cc = mid.getCallContext() and - conf = mid.getConfiguration() and - localFlowBigStep(midnode, _, _, conf, _) and - enclosing = midnode.getEnclosingCallable() and - sc = mid.getSummaryCtx() and - ap0 = mid.getAp() -} - pragma[nomagic] private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - readDirect(node1, f, node2) and + read(node1, f, node2) and flow(node2, config) } @@ -2418,7 +2281,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte pragma[nomagic] private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - storeDirect(node1, f, node2) and + store(node1, f, node2) and flow(node2, config) } @@ -2457,11 +2320,11 @@ private predicate pathOutOfCallable1( } pragma[noinline] -private Node getAnOutNodeCand( +private Node getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config ) { result = kind.getAnOutNode(call) and - flow(TNormalNode(result), _, ap, config) + flow(result, _, _, ap, config) } /** @@ -2473,7 +2336,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | pathOutOfCallable1(mid, call, kind, cc, ap, config) | - out = getAnOutNodeCand(kind, call, ap, config) + out = getAnOutNodeFlow(kind, call, ap, config) ) } @@ -2497,7 +2360,7 @@ private predicate parameterCand( DataFlowCallable callable, int i, AccessPath ap, Configuration config ) { exists(ParameterNode p | - flow(TNormalNode(p), _, ap, config) and + flow(p, _, _, ap, config) and p.isParameterOf(callable, i) ) } @@ -2542,13 +2405,15 @@ pragma[nomagic] private predicate paramFlowsThrough( ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config ) { - exists(PathNodeMid mid, ReturnNodeExt ret | + exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and kind = ret.getKind() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and config = mid.getConfiguration() and - ap = mid.getAp() + ap = mid.getAp() and + pos = sc.getParameterPos() and + not kind.(ParamUpdateReturnKind).getPosition() = pos ) } @@ -2570,7 +2435,7 @@ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { exists(DataFlowCall call, ReturnKindExt kind | pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration()) + out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) ) } @@ -2611,10 +2476,7 @@ private module FlowExploration { viableParamArg(_, node2, node1) or // flow out of a callable - exists(DataFlowCall call, ReturnKindExt kind | - getReturnPosition(node1) = viableReturnPos(call, kind) and - node2 = kind.getAnOutNode(call) - ) + viableReturnPosOut(_, getReturnPosition(node1), node2) | c1 = node1.getEnclosingCallable() and c2 = node2.getEnclosingCallable() and @@ -2910,7 +2772,7 @@ private module FlowExploration { PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 ) { ap1 = mid.getAp() and - storeDirect(mid.getNode(), f, node) and + store(mid.getNode(), f, node) and ap2.getHead() = f and ap2.len() = unbindInt(ap1.len() + 1) and compatibleTypes(ap1.getType(), f.getType()) @@ -2949,7 +2811,7 @@ private module FlowExploration { config = mid.getConfiguration() } - pragma[noinline] + pragma[nomagic] private predicate partialPathOutOfCallable1( PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowImplCommon.qll b/ql/src/semmle/go/dataflow/internal/DataFlowImplCommon.qll index ffbeeb94efc..852f54974e2 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowImplCommon.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowImplCommon.qll @@ -26,13 +26,30 @@ private module Cached { ) } - /** Provides predicates for calculating flow-through summaries. */ + pragma[nomagic] + private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { + viableCallable(call) = result.getCallable() and + kind = result.getKind() + } + + /** + * Holds if a value at return position `pos` can be returned to `out` via `call`, + * taking virtual dispatch into account. + */ cached + predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { + exists(ReturnKindExt kind | + pos = viableReturnPos(call, kind) and + out = kind.getAnOutNode(call) + ) + } + + /** Provides predicates for calculating flow-through summaries. */ private module FlowThrough { /** * The first flow-through approximation: * - * - Input/output access paths are abstracted with a Boolean parameter + * - Input access paths are abstracted with a Boolean parameter * that indicates (non-)emptiness. */ private module Cand { @@ -40,83 +57,47 @@ private module Cached { * Holds if `p` can flow to `node` in the same callable using only * value-preserving steps. * - * `read` indicates whether it is contents of `p` that can flow to `node`, - * and `stored` indicates whether it flows to contents of `node`. + * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand( - ParameterNode p, Node node, boolean read, boolean stored - ) { + private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { p = node and - read = false and - stored = false + read = false or // local flow exists(Node mid | - parameterValueFlowCand(p, mid, read, stored) and + parameterValueFlowCand(p, mid, read) and simpleLocalFlowStep(mid, node) ) or // read - exists(Node mid, boolean readMid, boolean storedMid | - parameterValueFlowCand(p, mid, readMid, storedMid) and - readStep(mid, _, node) and - stored = false - | - // value neither read nor stored prior to read - readMid = false and - storedMid = false and - read = true - or - // value (possibly read and then) stored prior to read (same content) - read = readMid and - storedMid = true - ) - or - // store exists(Node mid | - parameterValueFlowCand(p, mid, read, false) and - storeStep(mid, _, node) and - stored = true + parameterValueFlowCand(p, mid, false) and + readStep(mid, _, node) and + read = true ) or - // flow through: no prior read or store + // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArgCand(p, arg, false, false) and - argumentValueFlowsThroughCand(arg, node, read, stored) + parameterValueFlowArgCand(p, arg, false) and + argumentValueFlowsThroughCand(arg, node, read) ) or - // flow through: no read or store inside method + // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArgCand(p, arg, read, stored) and - argumentValueFlowsThroughCand(arg, node, false, false) - ) - or - // flow through: possible prior read and prior store with compatible - // flow-through method - exists(ArgumentNode arg, boolean mid | - parameterValueFlowArgCand(p, arg, read, mid) and - argumentValueFlowsThroughCand(arg, node, mid, stored) + parameterValueFlowArgCand(p, arg, read) and + argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNode p, ArgumentNode arg, boolean read, boolean stored - ) { - parameterValueFlowCand(p, arg, read, stored) + private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + parameterValueFlowCand(p, arg, read) } pragma[nomagic] predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { - parameterValueFlowCand(p, n.getPreUpdateNode(), false, false) - } - - pragma[nomagic] - private predicate parameterValueFlowsToPostUpdateCand( - ParameterNode p, PostUpdateNode n, boolean read - ) { - parameterValueFlowCand(p, n, read, true) + parameterValueFlowCand(p, n.getPreUpdateNode(), false) } /** @@ -125,33 +106,21 @@ private module Cached { * into account. * * `read` indicates whether it is contents of `p` that can flow to the return - * node, and `stored` indicates whether it flows to contents of the return * node. */ - predicate parameterValueFlowReturnCand( - ParameterNode p, ReturnKindExt kind, boolean read, boolean stored - ) { + predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | - parameterValueFlowCand(p, ret, read, stored) and - kind = TValueReturn(ret.getKind()) - ) - or - exists(ParameterNode p2, int pos2, PostUpdateNode n | - parameterValueFlowsToPostUpdateCand(p, n, read) and - parameterValueFlowsToPreUpdateCand(p2, n) and - p2.isParameterOf(_, pos2) and - kind = TParamUpdate(pos2) and - p != p2 and - stored = true + parameterValueFlowCand(p, ret, read) and + kind = ret.getKind() ) } pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, boolean read, boolean stored + DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturnCand(param, kind, read, stored) + parameterValueFlowReturnCand(param, kind, read) ) } @@ -159,22 +128,19 @@ private module Cached { * Holds if `arg` flows to `out` through a call using only value-preserving steps, * not taking call contexts into account. * - * `read` indicates whether it is contents of `arg` that can flow to `out`, and - * `stored` indicates whether it flows to contents of `out`. + * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand( - ArgumentNode arg, Node out, boolean read, boolean stored - ) { - exists(DataFlowCall call, ReturnKindExt kind | - argumentValueFlowsThroughCand0(call, arg, kind, read, stored) and - out = kind.getAnOutNode(call) + predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + exists(DataFlowCall call, ReturnKind kind | + argumentValueFlowsThroughCand0(call, arg, kind, read) and + out = getAnOutNode(call, kind) ) } predicate cand(ParameterNode p, Node n) { - parameterValueFlowCand(p, n, _, _) and + parameterValueFlowCand(p, n, _) and ( - parameterValueFlowReturnCand(p, _, _, _) + parameterValueFlowReturnCand(p, _, _) or parameterValueFlowsToPreUpdateCand(p, _) ) @@ -187,7 +153,6 @@ private module Cached { ( n instanceof ParameterNode or n instanceof OutNode or - n instanceof PostUpdateNode or readStep(_, _, n) or n instanceof CastNode ) @@ -200,10 +165,6 @@ private module Cached { or n instanceof ReturnNode or - Cand::parameterValueFlowsToPreUpdateCand(_, n) - or - storeStep(n, _, _) - or readStep(n, _, _) or n instanceof CastNode @@ -237,230 +198,140 @@ private module Cached { /** * The final flow-through calculation: * - * - Input/output access paths are abstracted with a `ContentOption` parameter + * - Input access paths are abstracted with a `ContentOption` parameter * that represents the head of the access path. `TContentNone()` means that * the access path is unrestricted. * - Types are checked using the `compatibleTypes()` relation. */ - cached - module Final { + private module Final { /** * Holds if `p` can flow to `node` in the same callable using only * value-preserving steps, not taking call contexts into account. * * `contentIn` describes the content of `p` that can flow to `node` - * (if any), and `contentOut` describes the content of `node` that - * it flows to (if any). + * (if any). */ - private predicate parameterValueFlow( - ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut - ) { - parameterValueFlow0(p, node, contentIn, contentOut) and + predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { + parameterValueFlow0(p, node, contentIn) and if node instanceof CastingNode then // normal flow through contentIn = TContentNone() and - contentOut = TContentNone() and compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) or // getter exists(Content fIn | contentIn.getContent() = fIn and - contentOut = TContentNone() and compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) ) - or - // (getter+)setter - exists(Content fOut | - contentOut.getContent() = fOut and - compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(node)) - ) else any() } pragma[nomagic] - private predicate parameterValueFlow0( - ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut - ) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() and - contentOut = TContentNone() + contentIn = TContentNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn, contentOut) and + parameterValueFlow(p, mid, contentIn) and LocalFlowBigStep::localFlowBigStep(mid, node) ) or // read - exists(Node mid, Content f, ContentOption contentInMid, ContentOption contentOutMid | - parameterValueFlow(p, mid, contentInMid, contentOutMid) and - readStep(mid, f, node) - | - // value neither read nor stored prior to read - contentInMid = TContentNone() and - contentOutMid = TContentNone() and - contentIn.getContent() = f and - contentOut = TContentNone() and - Cand::parameterValueFlowReturnCand(p, _, true, _) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) - or - // value (possibly read and then) stored prior to read (same content) - contentIn = contentInMid and - contentOutMid.getContent() = f and - contentOut = TContentNone() - ) - or - // store exists(Node mid, Content f | - parameterValueFlow(p, mid, contentIn, TContentNone()) and - storeStep(mid, f, node) and - contentOut.getContent() = f - | - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(p), f.getType()) - or - compatibleTypes(contentIn.getContent().getType(), f.getType()) + parameterValueFlow(p, mid, TContentNone()) and + readStep(mid, f, node) and + contentIn.getContent() = f and + Cand::parameterValueFlowReturnCand(p, _, true) and + compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) ) or - // flow through: no prior read or store + // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone(), TContentNone()) and - argumentValueFlowsThrough(_, arg, contentIn, contentOut, node) + parameterValueFlowArg(p, arg, TContentNone()) and + argumentValueFlowsThrough(arg, contentIn, node) ) or - // flow through: no read or store inside method + // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn, contentOut) and - argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) - ) - or - // flow through: possible prior read and prior store with compatible - // flow-through method - exists(ArgumentNode arg, ContentOption contentMid | - parameterValueFlowArg(p, arg, contentIn, contentMid) and - argumentValueFlowsThrough(_, arg, contentMid, contentOut, node) + parameterValueFlowArg(p, arg, contentIn) and + argumentValueFlowsThrough(arg, TContentNone(), node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut + ParameterNode p, ArgumentNode arg, ContentOption contentIn ) { - parameterValueFlow(p, arg, contentIn, contentOut) and - Cand::argumentValueFlowsThroughCand(arg, _, _, _) + parameterValueFlow(p, arg, contentIn) and + Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, ContentOption contentIn, - ContentOption contentOut + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, _, kind, contentIn, contentOut) + parameterValueFlowReturn(param, kind, contentIn) ) } /** - * Holds if `arg` flows to `out` through `call` using only value-preserving steps, + * Holds if `arg` flows to `out` through a call using only value-preserving steps, * not taking call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any), and - * `contentOut` describes the content of `out` that it flows to (if any). + * `contentIn` describes the content of `arg` that can flow to `out` (if any). */ - cached - predicate argumentValueFlowsThrough( - DataFlowCall call, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut, - Node out - ) { - exists(ReturnKindExt kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn, contentOut) and - out = kind.getAnOutNode(call) + pragma[nomagic] + predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + exists(DataFlowCall call, ReturnKind kind | + argumentValueFlowsThrough0(call, arg, kind, contentIn) and + out = getAnOutNode(call, kind) | // normal flow through contentIn = TContentNone() and - contentOut = TContentNone() and compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) or // getter exists(Content fIn | contentIn.getContent() = fIn and - contentOut = TContentNone() and compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) ) - or - // setter - exists(Content fOut | - contentIn = TContentNone() and - contentOut.getContent() = fOut and - compatibleTypes(getErasedNodeTypeBound(arg), fOut.getType()) and - compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(out)) - ) - or - // getter+setter - exists(Content fIn, Content fOut | - contentIn.getContent() = fIn and - contentOut.getContent() = fOut and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(out)) - ) ) } - /** - * Holds if `p` can flow to the pre-update node associated with post-update - * node `n`, in the same callable, using only value-preserving steps. - */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), TContentNone()) - } - - pragma[nomagic] - private predicate parameterValueFlowsToPostUpdate( - ParameterNode p, PostUpdateNode n, ContentOption contentIn, ContentOption contentOut - ) { - parameterValueFlow(p, n, contentIn, contentOut) and - contentOut.hasContent() - } - /** * Holds if `p` can flow to a return node of kind `kind` in the same * callable using only value-preserving steps. * * `contentIn` describes the content of `p` that can flow to the return - * node (if any), and `contentOut` describes the content of the return - * node that it flows to (if any). + * node (if any). */ - cached - predicate parameterValueFlowReturn( - ParameterNode p, Node ret, ReturnKindExt kind, ContentOption contentIn, - ContentOption contentOut + private predicate parameterValueFlowReturn( + ParameterNode p, ReturnKind kind, ContentOption contentIn ) { - ret = - any(ReturnNode n | - parameterValueFlow(p, n, contentIn, contentOut) and - kind = TValueReturn(n.getKind()) - ) - or - ret = - any(PostUpdateNode n | - exists(ParameterNode p2, int pos2 | - parameterValueFlowsToPostUpdate(p, n, contentIn, contentOut) and - parameterValueFlowsToPreUpdate(p2, n) and - p2.isParameterOf(_, pos2) and - kind = TParamUpdate(pos2) and - p != p2 - ) - ) + exists(ReturnNode ret | + parameterValueFlow(p, ret, contentIn) and + kind = ret.getKind() + ) } } import Final } + /** + * Holds if `p` can flow to the pre-update node associated with post-update + * node `n`, in the same callable, using only value-preserving steps. + */ + cached + predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -469,14 +340,14 @@ private module Cached { * been stored into, in order to handle cases like `x.f1.f2 = y`. */ cached - predicate storeDirect(Node node1, Content f, Node node2) { + predicate store(Node node1, Content f, Node node2) { storeStep(node1, f, node2) and readStep(_, f, _) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and n2 = node2.(PostUpdateNode).getPreUpdateNode() | - argumentValueFlowsThrough(_, n2, TContentSome(f), TContentNone(), n1) + argumentValueFlowsThrough(n2, TContentSome(f), n1) or readStep(n2, f, n1) ) @@ -520,6 +391,21 @@ private module Cached { newtype TReturnKindExt = TValueReturn(ReturnKind kind) or TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + + cached + newtype TBooleanOption = + TBooleanNone() or + TBooleanSome(boolean b) { b = true or b = false } + + cached + newtype TAccessPathFront = + TFrontNil(DataFlowType t) or + TFrontHead(Content f) + + cached + newtype TAccessPathFrontOption = + TAccessPathFrontNone() or + TAccessPathFrontSome(AccessPathFront apf) } /** @@ -529,8 +415,7 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNode or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + this instanceof OutNodeExt } } @@ -538,7 +423,7 @@ newtype TContentOption = TContentNone() or TContentSome(Content f) -class ContentOption extends TContentOption { +private class ContentOption extends TContentOption { Content getContent() { this = TContentSome(result) } predicate hasContent() { exists(this.getContent()) } @@ -678,6 +563,18 @@ class ReturnNodeExt extends Node { } } +/** + * A node to which data can flow from a call. Either an ordinary out node + * or a post-update node associated with a call argument. + */ +class OutNodeExt extends Node { + OutNodeExt() { + this instanceof OutNode + or + this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + } +} + /** * An extended return kind. A return kind describes how data can be returned * from a callable. This can either be through a returned value or an updated @@ -688,7 +585,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract Node getAnOutNode(DataFlowCall call); + abstract OutNodeExt getAnOutNode(DataFlowCall call); } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -700,7 +597,9 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { override string toString() { result = kind.toString() } - override Node getAnOutNode(DataFlowCall call) { result = getAnOutNode(call, this.getKind()) } + override OutNodeExt getAnOutNode(DataFlowCall call) { + result = getAnOutNode(call, this.getKind()) + } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -712,9 +611,9 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { override string toString() { result = "param update " + pos } - override PostUpdateNode getAnOutNode(DataFlowCall call) { + override OutNodeExt getAnOutNode(DataFlowCall call) { exists(ArgumentNode arg | - result.getPreUpdateNode() = arg and + result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, this.getPosition()) ) } @@ -779,77 +678,58 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -newtype TSummary = - TSummaryVal() or - TSummaryTaint() or - TSummaryReadVal(Content f) or - TSummaryReadTaint(Content f) or - TSummaryTaintStore(Content f) - -/** - * A summary of flow through a callable. This can either be value-preserving - * if no additional steps are used, taint-flow if at least one additional step - * is used, or any one of those combined with a store or a read. Summaries - * recorded at a return node are restricted to include at least one additional - * step, as the value-based summaries are calculated independent of the - * configuration. - */ -class Summary extends TSummary { - string toString() { - result = "Val" and this = TSummaryVal() - or - result = "Taint" and this = TSummaryTaint() - or - exists(Content f | - result = "ReadVal " + f.toString() and this = TSummaryReadVal(f) - or - result = "ReadTaint " + f.toString() and this = TSummaryReadTaint(f) - or - result = "TaintStore " + f.toString() and this = TSummaryTaintStore(f) - ) - } - - /** Gets the summary that results from extending this with an additional step. */ - Summary additionalStep() { - this = TSummaryVal() and result = TSummaryTaint() - or - this = TSummaryTaint() and result = TSummaryTaint() - or - exists(Content f | this = TSummaryReadVal(f) and result = TSummaryReadTaint(f)) - or - exists(Content f | this = TSummaryReadTaint(f) and result = TSummaryReadTaint(f)) - } - - /** Gets the summary that results from extending this with a read. */ - Summary readStep(Content f) { this = TSummaryVal() and result = TSummaryReadVal(f) } - - /** Gets the summary that results from extending this with a store. */ - Summary storeStep(Content f) { this = TSummaryTaint() and result = TSummaryTaintStore(f) } - - /** Gets the summary that results from extending this with `step`. */ - bindingset[this, step] - Summary compose(Summary step) { - this = TSummaryVal() and result = step - or - this = TSummaryTaint() and - (step = TSummaryTaint() or step = TSummaryTaintStore(_)) and - result = step - or - exists(Content f | - this = TSummaryReadVal(f) and step = TSummaryTaint() and result = TSummaryReadTaint(f) - ) - or - this = TSummaryReadTaint(_) and step = TSummaryTaint() and result = this - } - - /** Holds if this summary does not include any taint steps. */ - predicate isPartial() { - this = TSummaryVal() or - this = TSummaryReadVal(_) - } -} - pragma[noinline] DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } -predicate readDirect = readStep/3; +predicate read = readStep/3; + +/** An optional Boolean value. */ +class BooleanOption extends TBooleanOption { + string toString() { + this = TBooleanNone() and result = "" + or + this = TBooleanSome(any(boolean b | result = b.toString())) + } +} + +/** + * The front of an access path. This is either a head or a nil. + */ +abstract class AccessPathFront extends TAccessPathFront { + abstract string toString(); + + abstract DataFlowType getType(); + + abstract boolean toBoolNonEmpty(); + + predicate headUsesContent(Content f) { this = TFrontHead(f) } +} + +class AccessPathFrontNil extends AccessPathFront, TFrontNil { + override string toString() { + exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) + } + + override DataFlowType getType() { this = TFrontNil(result) } + + override boolean toBoolNonEmpty() { result = false } +} + +class AccessPathFrontHead extends AccessPathFront, TFrontHead { + override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + + override DataFlowType getType() { + exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) + } + + override boolean toBoolNonEmpty() { result = true } +} + +/** An optional access path front. */ +class AccessPathFrontOption extends TAccessPathFrontOption { + string toString() { + this = TAccessPathFrontNone() and result = "" + or + this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) + } +} diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll b/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll index 16b85fa8aef..d8e708a8c7c 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll @@ -3,8 +3,7 @@ private import DataFlowUtil private import DataFlowImplCommon private newtype TReturnKind = - TSingleReturn() or - TMultiReturn(int i) { exists(SignatureType st | exists(st.getResultType(i))) } + MkReturnKind(int i) { exists(SignatureType st | exists(st.getResultType(i))) } /** * A return kind. A return kind describes how a value can be returned @@ -13,23 +12,14 @@ private newtype TReturnKind = */ class ReturnKind extends TReturnKind { /** Gets a textual representation of this return kind. */ - string toString() { - this = TSingleReturn() and - result = "return" - or - exists(int i | this = TMultiReturn(i) | result = "return[" + i + "]") - } + string toString() { exists(int i | this = MkReturnKind(i) | result = "return[" + i + "]") } } /** A data flow node that represents returning a value from a function. */ class ReturnNode extends ResultNode { ReturnKind kind; - ReturnNode() { - exists(int nr | nr = fd.getType().getNumResult() | - if nr = 1 then kind = TSingleReturn() else kind = TMultiReturn(i) - ) - } + ReturnNode() { kind = MkReturnKind(i) } /** Gets the kind of this returned value. */ ReturnKind getKind() { result = kind } @@ -40,12 +30,7 @@ class OutNode extends DataFlow::Node { DataFlow::CallNode call; int i; - OutNode() { - this = call.getResult() and - i = -1 - or - this = call.getResult(i) - } + OutNode() { this = call.getResult(i) } /** Gets the underlying call. */ DataFlowCall getCall() { result = call.asExpr() } @@ -56,11 +41,8 @@ class OutNode extends DataFlow::Node { * `kind`. */ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { - exists(DataFlow::CallNode c | c.asExpr() = call | - kind = TSingleReturn() and - result = c.getResult() - or - exists(int i | kind = TMultiReturn(i) | result = c.getResult(i)) + exists(DataFlow::CallNode c, int i | c.asExpr() = call and kind = MkReturnKind(i) | + result = c.getResult(i) ) } @@ -272,3 +254,14 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { } int accessPathLimit() { result = 5 } + +/** + * Gets the `i`th argument of call `c`, where the receiver of a method call + * counts as argument -1. + */ +Node getArgument(CallNode c, int i) { + result = c.getArgument(i) + or + result = c.(MethodCallNode).getReceiver() and + i = -1 +} diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll b/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll index 09e396a9563..a5846c2caee 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll @@ -4,6 +4,7 @@ import go import semmle.go.dataflow.FunctionInputsAndOutputs +private import DataFlowPrivate cached private newtype TNode = @@ -326,8 +327,15 @@ class CallNode extends ExprNode { /** Gets a function passed as the `i`th argument of this call. */ FunctionNode getCallback(int i) { result.getASuccessor*() = this.getArgument(i) } - /** Gets the data-flow node corresponding to the `i`th result of this call. */ - Node getResult(int i) { result = extractTupleElement(this, i) } + /** + * Gets the data-flow node corresponding to the `i`th result of this call. + * + * If there is a single result then it is considered to be the 0th result. */ + Node getResult(int i) { + i = 0 and result = getResult() + or + result = extractTupleElement(this, i) + } /** * Gets the data-flow node corresponding to the result of this call. @@ -337,6 +345,9 @@ class CallNode extends ExprNode { */ Node getResult() { not getType() instanceof TupleType and result = this } + /** Gets a result of this call. */ + Node getAResult() { result = this.getResult(_) } + /** Gets the data flow node corresponding to the receiver of this call, if any. */ Node getReceiver() { result = getACalleeSource().(MethodReadNode).getReceiver() } } @@ -423,17 +434,6 @@ class PostUpdateNode extends Node { Node getPreUpdateNode() { result = preupd } } -/** - * Gets the `i`th argument of call `c`, where the receiver of a method call - * counts as argument -1. - */ -private Node getArgument(CallNode c, int i) { - result = c.getArgument(i) - or - result = c.(MethodCallNode).getReceiver() and - i = -1 -} - /** * A data-flow node that occurs as an argument in a call, including receiver arguments. */ @@ -473,11 +473,12 @@ class ArgumentNode extends Node { * mutate it or something it points to. */ predicate mutableType(Type tp) { - tp instanceof ArrayType or - tp instanceof SliceType or - tp instanceof MapType or - tp instanceof PointerType or - tp instanceof InterfaceType + exists(Type underlying | underlying = tp.getUnderlyingType() | + not underlying instanceof BoolType and + not underlying instanceof NumericType and + not underlying instanceof StringType and + not underlying instanceof LiteralType + ) } /** @@ -766,6 +767,9 @@ class EqualityTestNode extends BinaryOperationNode, ExprNode { outcome = expr.getPolarity() and expr.hasOperands(lhs.asExpr(), rhs.asExpr()) } + + /** Gets the polarity of this equality test, that is, `true` for `==` and `false` for `!=`. */ + boolean getPolarity() { result = expr.getPolarity() } } /** diff --git a/ql/src/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/ql/src/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/ql/src/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/ql/src/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/ql/src/semmle/go/frameworks/Email.qll b/ql/src/semmle/go/frameworks/Email.qll new file mode 100644 index 00000000000..32f70aeb405 --- /dev/null +++ b/ql/src/semmle/go/frameworks/Email.qll @@ -0,0 +1,112 @@ +/** Provides classes for working with email-related APIs. */ + +import go + +/** + * A data-flow node that represents data written to an email, either as part + * of the headers or as part of the body. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `EmailData::Range` instead. + */ +class EmailData extends DataFlow::Node { + EmailData::Range self; + + EmailData() { this = self } +} + +/** Provides classes for working with data that is incorporated into an email. */ +module EmailData { + /** + * A data-flow node that represents data which is written to an email, either as part + * of the headers or as part of the body. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `EmailData` instead. + */ + abstract class Range extends DataFlow::Node { } + + /** A data-flow node that is written to an email using the net/smtp package. */ + private class SmtpData extends Range { + SmtpData() { + // func (c *Client) Data() (io.WriteCloser, error) + exists(Method data | + data.hasQualifiedName("net/smtp", "Client", "Data") and + this.(DataFlow::SsaNode).getInit() = data.getACall().getResult(0) + ) + or + // func SendMail(addr string, a Auth, from string, to []string, msg []byte) error + exists(Function sendMail | + sendMail.hasQualifiedName("net/smtp", "SendMail") and + this = sendMail.getACall().getArgument(4) + ) + } + } + + /** Gets the package name `github.com/sendgrid/sendgrid-go/helpers/mail`. */ + private string sendgridMail() { result = "github.com/sendgrid/sendgrid-go/helpers/mail" } + + private class NewContent extends TaintTracking::FunctionModel { + NewContent() { + // func NewContent(contentType string, value string) *Content + this.hasQualifiedName(sendgridMail(), "NewContent") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(1) and output.isResult() + } + } + + /** A data-flow node that is written to an email using the sendgrid/sendgrid-go package. */ + private class SendGridEmail extends Range { + SendGridEmail() { + // func NewSingleEmail(from *Email, subject string, to *Email, plainTextContent string, htmlContent string) *SGMailV3 + exists(Function newSingleEmail | + newSingleEmail.hasQualifiedName(sendgridMail(), "NewSingleEmail") and + this = newSingleEmail.getACall().getArgument([1, 3, 4]) + ) + or + // func NewV3MailInit(from *Email, subject string, to *Email, content ...*Content) *SGMailV3 + exists(Function newv3MailInit | + newv3MailInit.hasQualifiedName(sendgridMail(), "NewV3MailInit") and + this = newv3MailInit.getACall().getArgument(any(int i | i = 1 or i >= 3)) + ) + or + // func (s *SGMailV3) AddContent(c ...*Content) *SGMailV3 + exists(Method addContent | + addContent.hasQualifiedName(sendgridMail(), "SGMailV3", "AddContent") and + this = addContent.getACall().getAnArgument() + ) + } + } +} + +/** + * A taint model of the `Writer.CreatePart` method from `mime/multipart`. + * + * If tainted data is written to the multipart section created by this method, the underlying writer + * should be considered tainted as well. + */ +private class MultipartWriterCreatePartModel extends TaintTracking::FunctionModel, Method { + MultipartWriterCreatePartModel() { + this.hasQualifiedName("mime/multipart", "Writer", "CreatePart") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isResult(0) and output.isReceiver() + } +} + +/** + * A taint model of the `NewWriter` function from `mime/multipart`. + * + * If tainted data is written to the writer created by this function, the underlying writer + * should be considered tainted as well. + */ +private class MultipartNewWriterModel extends TaintTracking::FunctionModel { + MultipartNewWriterModel() { this.hasQualifiedName("mime/multipart", "NewWriter") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isResult() and output.isParameter(0) + } +} diff --git a/ql/src/semmle/go/frameworks/HTTP.qll b/ql/src/semmle/go/frameworks/HTTP.qll index 44e0cd23533..a159d75db7b 100644 --- a/ql/src/semmle/go/frameworks/HTTP.qll +++ b/ql/src/semmle/go/frameworks/HTTP.qll @@ -54,13 +54,21 @@ private module StdlibHttp { } } + /** The declaration of a variable which either is or has a field that implements the http.ResponseWriter type */ private class StdlibResponseWriter extends HTTP::ResponseWriter::Range { - StdlibResponseWriter() { this.getType().implements("net/http", "ResponseWriter") } + SsaWithFields v; + + StdlibResponseWriter() { + this = v.getBaseVariable().getSourceVariable() and + exists(Type t | t.implements("net/http", "ResponseWriter") | v.getType() = t) + } + + override DataFlow::Node getANode() { result = v.similar().getAUse().getASuccessor*() } /** Gets a header object that corresponds to this HTTP response. */ DataFlow::MethodCallNode getAHeaderObject() { - result.getTarget().hasQualifiedName("net/http", _, "Header") and - this.getARead() = result.getReceiver() + result.getTarget().getName() = "Header" and + this.getANode() = result.getReceiver() } } diff --git a/ql/src/semmle/go/frameworks/Macaron.qll b/ql/src/semmle/go/frameworks/Macaron.qll new file mode 100644 index 00000000000..a38b2b20da0 --- /dev/null +++ b/ql/src/semmle/go/frameworks/Macaron.qll @@ -0,0 +1,30 @@ +/** + * Provides classes for working with concepts relating to the Macaron web framework + */ + +import go + +private module Macaron { + private class Context extends HTTP::ResponseWriter::Range { + SsaWithFields v; + + Context() { + this = v.getBaseVariable().getSourceVariable() and + exists(Method m | m.hasQualifiedName("gopkg.in/macaron.v1", "Context", "Redirect") | + v.getType().getMethod("Redirect") = m + ) + } + + override DataFlow::Node getANode() { result = v.similar().getAUse().getASuccessor*() } + } + + private class RedirectCall extends HTTP::Redirect::Range, DataFlow::MethodCallNode { + RedirectCall() { + this.getTarget().hasQualifiedName("gopkg.in/macaron.v1", "Context", "Redirect") + } + + override DataFlow::Node getUrl() { result = this.getArgument(0) } + + override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } + } +} diff --git a/ql/src/semmle/go/frameworks/Mux.qll b/ql/src/semmle/go/frameworks/Mux.qll new file mode 100644 index 00000000000..2c2d783f566 --- /dev/null +++ b/ql/src/semmle/go/frameworks/Mux.qll @@ -0,0 +1,15 @@ +/** + * Provides classes for working with concepts in the Mux HTTP middleware library. + */ + +import go + +/** + * Provides classes for working with concepts in the Mux HTTP middleware library. + */ +module Mux { + /** An access to a Mux middleware variable. */ + class RequestVars extends DataFlow::UntrustedFlowSource::Range, DataFlow::CallNode { + RequestVars() { this.getTarget().hasQualifiedName("github.com/gorilla/mux", "Vars") } + } +} diff --git a/ql/src/semmle/go/frameworks/NoSQL.qll b/ql/src/semmle/go/frameworks/NoSQL.qll new file mode 100644 index 00000000000..75ab2dc3765 --- /dev/null +++ b/ql/src/semmle/go/frameworks/NoSQL.qll @@ -0,0 +1,120 @@ +/** + * Provides classes for working with NoSQL-related concepts such as queries. + */ + +import go + +/** Provides classes for working with NoSQL-related APIs. */ +module NoSQL { + /** + * A data-flow node whose value is interpreted as (part of) a NoSQL query. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `NoSQL::Query::Range` instead. + */ + class Query extends DataFlow::Node { + Query::Range self; + + Query() { this = self } + } + + /** Provides classes for working with NoSQL queries. */ + module Query { + /** + * A data-flow node whose value is interpreted as (part of) a NoSQL query. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `NoSQL::Query` instead. + */ + abstract class Range extends DataFlow::Node { } + + /** + * Holds if method `name` of struct `Collection` from package + * [go.mongodb.org/mongo-driver/mongo](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo) + * interprets parameter `n` as a query. + */ + private predicate mongoDbCollectionMethod(string name, int n) { + // func (coll *Collection) CountDocuments(ctx context.Context, filter interface{}, + // opts ...*options.CountOptions) (int64, error) + name = "CountDocuments" and n = 1 + or + // func (coll *Collection) DeleteMany(ctx context.Context, filter interface{}, + // opts ...*options.DeleteOptions) (*DeleteResult, error) + name = "DeleteMany" and n = 1 + or + // func (coll *Collection) DeleteOne(ctx context.Context, filter interface{}, + // opts ...*options.DeleteOptions) (*DeleteResult, error) + name = "DeleteOne" and n = 1 + or + // func (coll *Collection) Distinct(ctx context.Context, fieldName string, filter interface{}, + // ...) ([]interface{}, error) + name = "Distinct" and n = 2 + or + // func (coll *Collection) Find(ctx context.Context, filter interface{}, + // opts ...*options.FindOptions) (*Cursor, error) + name = "Find" and n = 1 + or + // func (coll *Collection) FindOne(ctx context.Context, filter interface{}, + // opts ...*options.FindOneOptions) *SingleResult + name = "FindOne" and n = 1 + or + // func (coll *Collection) FindOneAndDelete(ctx context.Context, filter interface{}, ...) + // *SingleResult + name = "FindOneAndDelete" and n = 1 + or + // func (coll *Collection) FindOneAndReplace(ctx context.Context, filter interface{}, + // replacement interface{}, ...) *SingleResult + name = "FindOneAndReplace" and n = 1 + or + // func (coll *Collection) FindOneAndUpdate(ctx context.Context, filter interface{}, + // update interface{}, ...) *SingleResult + name = "FindOneAndUpdate" and n = 1 + or + // func (coll *Collection) ReplaceOne(ctx context.Context, filter interface{}, + // replacement interface{}, ...) (*UpdateResult, error) + name = "ReplaceOne" and n = 1 + or + // func (coll *Collection) UpdateMany(ctx context.Context, filter interface{}, + // update interface{}, ...) (*UpdateResult, error) + name = "UpdateMany" and n = 1 + or + // func (coll *Collection) UpdateOne(ctx context.Context, filter interface{}, + // update interface{}, ...) (*UpdateResult, error) + name = "UpdateOne" and n = 1 + or + // func (coll *Collection) Watch(ctx context.Context, pipeline interface{}, ...) + // (*ChangeStream, error) + name = "Watch" and n = 1 + or + // func (coll *Collection) Aggregate(ctx context.Context, pipeline interface{}, + // opts ...*options.AggregateOptions) (*Cursor, error) + name = "Aggregate" and n = 1 + } + + /** + * A query used in an API function acting on a `Collection` struct of package + * [go.mongodb.org/mongo-driver/mongo](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo). + */ + private class MongoDbCollectionQuery extends Range { + MongoDbCollectionQuery() { + exists(Method meth, string methodName, int n | + mongoDbCollectionMethod(methodName, n) and + meth.hasQualifiedName("go.mongodb.org/mongo-driver/mongo", "Collection", methodName) and + this = meth.getACall().getArgument(n) + ) + } + } + } + + /** + * Holds if taint flows from `pred` to `succ` through a MongoDB-specific API. + */ + predicate isAdditionalMongoTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + // Taint an entry if the `Value` is tainted + exists(Write w, DataFlow::Node base, Field f | w.writesField(base, f, pred) | + base = succ.(DataFlow::PostUpdateNode).getPreUpdateNode() and + base.getType().hasQualifiedName("go.mongodb.org/mongo-driver/bson/primitive", "E") and + f.getName() = "Value" + ) + } +} diff --git a/ql/src/semmle/go/frameworks/SQL.qll b/ql/src/semmle/go/frameworks/SQL.qll index 49f61a74452..1244e5cc778 100644 --- a/ql/src/semmle/go/frameworks/SQL.qll +++ b/ql/src/semmle/go/frameworks/SQL.qll @@ -9,7 +9,7 @@ module SQL { /** * A data-flow node whose string value is interpreted as (part of) a SQL query. * - * Extends this class to refine existing API models. If you want to model new APIs, + * Extend this class to refine existing API models. If you want to model new APIs, * extend `SQL::QueryString::Range` instead. */ class QueryString extends DataFlow::Node { @@ -76,11 +76,11 @@ module SQL { /** A string that might identify package `go-pg/pg` or a specific version of it. */ bindingset[result] - private string gopg() { result.regexpMatch("github.com/go-pg/pg(/v[^/]+)?") } + private string gopg() { result = package("github.com/go-pg/pg", "") } /** A string that might identify package `go-pg/pg/orm` or a specific version of it. */ bindingset[result] - private string gopgorm() { result.regexpMatch("github.com/go-pg/pg(/v[^/]+)?/orm") } + private string gopgorm() { result = package("github.com/go-pg/pg", "orm") } /** * A string argument to an API of `go-pg/pg` that is directly interpreted as SQL without diff --git a/ql/src/semmle/go/frameworks/Stdlib.qll b/ql/src/semmle/go/frameworks/Stdlib.qll index ec496aca063..6eeee5d8bfb 100644 --- a/ql/src/semmle/go/frameworks/Stdlib.qll +++ b/ql/src/semmle/go/frameworks/Stdlib.qll @@ -62,7 +62,18 @@ module PathFilePath { override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { inp.isParameter(_) and - (outp.isResult() or outp.isResult(_)) + outp.isResult(_) + } + } +} + +/** Provides models of commonly used functions in the `bytes` package. */ +private module Bytes { + private class BufferBytes extends TaintTracking::FunctionModel, Method { + BufferBytes() { this.hasQualifiedName("bytes", "Buffer", ["Bytes", "String"]) } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isReceiver() and output.isResult() } } } @@ -71,39 +82,272 @@ module PathFilePath { module Fmt { /** The `Sprint` function or one of its variants. */ class Sprinter extends TaintTracking::FunctionModel { - Sprinter() { - exists(string sprint | sprint.matches("Sprint%") | hasQualifiedName("fmt", sprint)) - } + Sprinter() { this.hasQualifiedName("fmt", ["Sprint", "Sprintf", "Sprintln"]) } override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { inp.isParameter(_) and outp.isResult() } } + /** The `Print` function or one of its variants. */ + private class Printer extends Function { + Printer() { this.hasQualifiedName("fmt", ["Print", "Printf", "Println"]) } + } + + /** A call to `Print`, `Fprint`, or similar. */ private class PrintCall extends LoggerCall::Range, DataFlow::CallNode { - PrintCall() { - exists(string fn | - fn = "Print%" - or - fn = "Fprint%" - | - this.getTarget().hasQualifiedName("fmt", fn) - ) - } + PrintCall() { this.getTarget() instanceof Printer or this.getTarget() instanceof Fprinter } override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() } } + + /** The `Fprint` function or one of its variants. */ + private class Fprinter extends TaintTracking::FunctionModel { + Fprinter() { this.hasQualifiedName("fmt", ["Fprint", "Fprintf", "Fprintln"]) } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(any(int i | i > 0)) and output.isParameter(0) + } + } + + /** The `Sscan` function or one of its variants. */ + private class Sscanner extends TaintTracking::FunctionModel { + Sscanner() { this.hasQualifiedName("fmt", ["Sscan", "Sscanf", "Sscanln"]) } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and + exists(int i | if getName() = "Sscanf" then i > 1 else i > 0 | output.isParameter(i)) + } + } } /** Provides models of commonly used functions in the `io` package. */ module Io { - private class ReaderRead extends TaintTracking::FunctionModel, Method { - ReaderRead() { - exists(Method im | im.hasQualifiedName("io", "Reader", "Read") | this.implements(im)) + private class Copy extends TaintTracking::FunctionModel { + Copy() { + // func Copy(dst Writer, src Reader) (written int64, err error) + // func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) + // func CopyN(dst Writer, src Reader, n int64) (written int64, err error) + hasQualifiedName("io", "Copy") or + hasQualifiedName("io", "CopyBuffer") or + hasQualifiedName("io", "CopyN") } - override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { - inp.isReceiver() and outp.isParameter(0) + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(1) and output.isParameter(0) + } + } + + private class Pipe extends TaintTracking::FunctionModel { + Pipe() { + // func Pipe() (*PipeReader, *PipeWriter) + hasQualifiedName("io", "Pipe") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isResult(0) and output.isResult(1) + } + } + + private class ReadAtLeast extends TaintTracking::FunctionModel { + ReadAtLeast() { + // func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) + // func ReadFull(r Reader, buf []byte) (n int, err error) + hasQualifiedName("io", "ReadAtLeast") or + hasQualifiedName("io", "ReadFull") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isParameter(1) + } + } + + private class WriteString extends TaintTracking::FunctionModel { + WriteString() { + // func WriteString(w Writer, s string) (n int, err error) + this.hasQualifiedName("io", "WriteString") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(1) and output.isParameter(0) + } + } + + private class ByteReaderReadByte extends TaintTracking::FunctionModel, Method { + ByteReaderReadByte() { + // func ReadByte() (byte, error) + this.implements("io", "ByteReader", "ReadByte") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isReceiver() and output.isResult(0) + } + } + + private class ByteWriterWriteByte extends TaintTracking::FunctionModel, Method { + ByteWriterWriteByte() { + // func WriteByte(c byte) error + this.implements("io", "ByteWriter", "WriteByte") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isReceiver() + } + } + + private class ReaderRead extends TaintTracking::FunctionModel, Method { + ReaderRead() { + // func Read(p []byte) (n int, err error) + this.implements("io", "Reader", "Read") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isReceiver() and output.isParameter(0) + } + } + + private class LimitReader extends TaintTracking::FunctionModel { + LimitReader() { + // func LimitReader(r Reader, n int64) Reader + this.hasQualifiedName("io", "LimitReader") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isResult() + } + } + + private class MultiReader extends TaintTracking::FunctionModel { + MultiReader() { + // func MultiReader(readers ...Reader) Reader + this.hasQualifiedName("io", "MultiReader") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(_) and output.isResult() + } + } + + private class TeeReader extends TaintTracking::FunctionModel { + TeeReader() { + // func TeeReader(r Reader, w Writer) Reader + this.hasQualifiedName("io", "TeeReader") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isResult() + or + input.isParameter(0) and output.isParameter(1) + } + } + + private class ReaderAtReadAt extends TaintTracking::FunctionModel, Method { + ReaderAtReadAt() { + // func ReadAt(p []byte, off int64) (n int, err error) + this.implements("io", "ReaderAt", "ReadAt") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isReceiver() and output.isParameter(0) + } + } + + private class ReaderFromReadFrom extends TaintTracking::FunctionModel, Method { + ReaderFromReadFrom() { + // func ReadFrom(r Reader) (n int64, err error) + this.implements("io", "ReaderFrom", "ReadFrom") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isReceiver() + } + } + + private class RuneReaderReadRune extends TaintTracking::FunctionModel, Method { + RuneReaderReadRune() { + // func ReadRune() (r rune, size int, err error) + this.implements("io", "RuneReader", "ReadRune") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isReceiver() and output.isResult(0) + } + } + + private class NewSectionReader extends TaintTracking::FunctionModel { + NewSectionReader() { + // func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader + this.hasQualifiedName("io", "NewSectionReader") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isResult() + } + } + + private class StringWriterWriteString extends TaintTracking::FunctionModel, Method { + StringWriterWriteString() { + // func WriteString(s string) (n int, err error) + this.implements("io", "StringWriter", "WriteString") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isReceiver() + } + } + + private class WriterWrite extends TaintTracking::FunctionModel, Method { + WriterWrite() { + // func Write(p []byte) (n int, err error) + this.implements("io", "Writer", "Write") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isReceiver() + } + } + + private class MultiWriter extends TaintTracking::FunctionModel { + MultiWriter() { + // func MultiWriter(writers ...Writer) Writer + hasQualifiedName("io", "MultiWriter") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isResult() and output.isParameter(_) + } + } + + private class WriterAtWriteAt extends TaintTracking::FunctionModel, Method { + WriterAtWriteAt() { + // func WriteAt(p []byte, off int64) (n int, err error) + this.implements("io", "WriterAt", "WriteAt") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and output.isReceiver() + } + } + + private class WriterToWriteTo extends TaintTracking::FunctionModel, Method { + WriterToWriteTo() { + // func WriteTo(w Writer) (n int64, err error) + this.implements("io", "WriterTo", "WriteTo") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isReceiver() and output.isParameter(0) + } + } +} + +/** Provides models of commonly used functions in the `bufio` package. */ +module Bufio { + private class NewWriter extends TaintTracking::FunctionModel { + NewWriter() { this.hasQualifiedName("bufio", "NewWriter") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isResult() and output.isParameter(0) } } } @@ -115,11 +359,13 @@ module IoUtil { exists(string fn | getTarget().hasQualifiedName("io/ioutil", fn) | fn = "ReadDir" or fn = "ReadFile" or + fn = "TempDir" or + fn = "TempFile" or fn = "WriteFile" ) } - override DataFlow::Node getAPathArgument() { result = getArgument(0) } + override DataFlow::Node getAPathArgument() { result = getAnArgument() } } /** @@ -226,7 +472,7 @@ module Path { override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { inp.isParameter(_) and - (outp.isResult() or outp.isResult(_)) + outp.isResult(_) } } } @@ -438,8 +684,7 @@ module URL { } override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp.isReceiver() and - if getName() = "Password" then outp.isResult(0) else outp.isResult() + inp.isReceiver() and outp.isResult(0) } } @@ -545,7 +790,8 @@ module Log { /** Provides models of some functions in the `encoding/json` package. */ module EncodingJson { - private class MarshalFunction extends TaintTracking::FunctionModel, MarshalingFunction::Range { + /** The `Marshal` or `MarshalIndent` function in the `encoding/json` package. */ + class MarshalFunction extends TaintTracking::FunctionModel, MarshalingFunction::Range { MarshalFunction() { this.hasQualifiedName("encoding/json", "Marshal") or this.hasQualifiedName("encoding/json", "MarshalIndent") diff --git a/ql/src/semmle/go/frameworks/SystemCommandExecutors.qll b/ql/src/semmle/go/frameworks/SystemCommandExecutors.qll index 940d30f6142..654fa9eb6dc 100644 --- a/ql/src/semmle/go/frameworks/SystemCommandExecutors.qll +++ b/ql/src/semmle/go/frameworks/SystemCommandExecutors.qll @@ -29,6 +29,16 @@ private class SystemCommandExecutors extends SystemCommandExecution::Range, Data pkg = "os/exec" and name = "Command" and cmdArg = 0 or pkg = "os/exec" and name = "CommandContext" and cmdArg = 1 + or + // NOTE: syscall.ForkExec exists only on unix. + // NOTE: syscall.CreateProcess and syscall.CreateProcessAsUser exist only on windows. + pkg = "syscall" and + (name = "Exec" or name = "ForkExec" or name = "StartProcess" or name = "CreateProcess") and + cmdArg = 0 + or + pkg = "syscall" and + name = "CreateProcessAsUser" and + cmdArg = 1 ) } diff --git a/ql/src/semmle/go/frameworks/WebSocket.qll b/ql/src/semmle/go/frameworks/WebSocket.qll new file mode 100644 index 00000000000..0919ae2aafa --- /dev/null +++ b/ql/src/semmle/go/frameworks/WebSocket.qll @@ -0,0 +1,274 @@ +/** Provides classes for working with WebSocket-related APIs. */ + +import go + +/** + * A function call that establishes a new WebSocket connection. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `WebSocketRequestCall::Range` instead. + */ +class WebSocketRequestCall extends DataFlow::CallNode { + WebSocketRequestCall::Range self; + + WebSocketRequestCall() { this = self } + + /** Gets the URL of the request. */ + DataFlow::Node getRequestUrl() { result = self.getRequestUrl() } +} + +/** Provides classes for working with WebSocket request functions. */ +module WebSocketRequestCall { + /** + * A function call that establishes a new WebSocket connection. + * + * Extend this class to model new APIs. If you want to refine existing + * API models, extend `WebSocketRequestCall` instead. + */ + abstract class Range extends DataFlow::CallNode { + /** Gets the URL of the request. */ + abstract DataFlow::Node getRequestUrl(); + } + + /** + * A call to the `Dial` function of the `golang.org/x/net/websocket` package. + */ + private class GolangXNetDialFunc extends Range { + GolangXNetDialFunc() { + // func Dial(url_, protocol, origin string) (ws *Conn, err error) + this.getTarget().hasQualifiedName(package("golang.org/x/net", "websocket"), "Dial") + } + + override DataFlow::Node getRequestUrl() { result = this.getArgument(0) } + } + + /** + * A call to the `DialConfig` function of the `golang.org/x/net/websocket` package. + */ + private class GolangXNetDialConfigFunc extends Range { + GolangXNetDialConfigFunc() { + // func DialConfig(config *Config) (ws *Conn, err error) + this.getTarget().hasQualifiedName(package("golang.org/x/net", "websocket"), "DialConfig") + } + + override DataFlow::Node getRequestUrl() { + exists(DataFlow::CallNode cn | + // func NewConfig(server, origin string) (config *Config, err error) + cn.getTarget().hasQualifiedName(package("golang.org/x/net", "websocket"), "NewConfig") and + this.getArgument(0) = cn.getResult(0).getASuccessor*() and + result = cn.getArgument(0) + ) + } + } + + /** + * A call to the `Dialer` or `DialContext` function of the `github.com/gorilla/websocket` package. + */ + private class GorillaWebSocketDialFunc extends Range { + DataFlow::Node url; + + GorillaWebSocketDialFunc() { + // func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) + // func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) + exists(string name, Method f | + f = this.getTarget() and + f.hasQualifiedName(package("github.com/gorilla", "websocket"), "Dialer", name) + | + name = "Dial" and this.getArgument(0) = url + or + name = "DialContext" and this.getArgument(1) = url + ) + } + + override DataFlow::Node getRequestUrl() { result = url } + } + + /** + * A call to the `Dialer.Dial` method of the `github.com/gobwas/ws` package. + */ + private class GobwasWsDialFunc extends Range { + GobwasWsDialFunc() { + // func (d Dialer) Dial(ctx context.Context, urlstr string) (conn net.Conn, br *bufio.Reader, hs Handshake, err error) + exists(Method m | + m.hasQualifiedName(package("github.com/gobwas", "ws"), "Dialer", "Dial") and + m = this.getTarget() + ) + or + // func Dial(ctx context.Context, urlstr string) (net.Conn, *bufio.Reader, Handshake, error) + this.getTarget().hasQualifiedName(package("github.com/gobwas", "ws"), "Dial") + } + + override DataFlow::Node getRequestUrl() { result = this.getArgument(1) } + } + + /** + * A call to the `Dial` function of the `nhooyr.io/websocket` package. + */ + private class NhooyrWebSocketDialFunc extends Range { + NhooyrWebSocketDialFunc() { + // func Dial(ctx context.Context, u string, opts *DialOptions) (*Conn, *http.Response, error) + this.getTarget().hasQualifiedName(package("nhooyr.io", "websocket"), "Dial") + } + + override DataFlow::Node getRequestUrl() { result = this.getArgument(1) } + } + + /** + * A call to the `BuildProxy` or `New` function of the `github.com/sacOO7/gowebsocket` package. + */ + private class SacOO7DialFunc extends Range { + SacOO7DialFunc() { + // func BuildProxy(Url string) func(*http.Request) (*url.URL, error) + // func New(url string) Socket + this.getTarget().hasQualifiedName("github.com/sacOO7/gowebsocket", ["BuildProxy", "New"]) + } + + override DataFlow::Node getRequestUrl() { result = this.getArgument(0) } + } +} + +/** + * A message written to a WebSocket, considered as a flow sink for reflected XSS. + */ +class WebSocketReaderAsSource extends UntrustedFlowSource::Range { + WebSocketReaderAsSource() { + exists(WebSocketReader r | this = r.getAnOutput().getNode(r.getACall())) + } +} + +/** + * A function or a method which reads a message from a WebSocket connection. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `WebSocketReader::Range` instead. + */ +class WebSocketReader extends Function { + WebSocketReader::Range self; + + WebSocketReader() { this = self } + + /** Gets an output of this function containing data that is read from a WebSocket connection. */ + FunctionOutput getAnOutput() { result = self.getAnOutput() } +} + +/** Provides classes for working with messages read from a WebSocket. */ +module WebSocketReader { + /** + * A function or a method which reads a message from a WebSocket connection + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `WebSocketReader` instead. + */ + abstract class Range extends Function { + /** Gets an output of this function containing data that is read from a WebSocket connection. */ + abstract FunctionOutput getAnOutput(); + } + + /** + * The `Codec.Receive` method of the `golang.org/x/net/websocket` package. + */ + private class GolangXNetCodecRecv extends Range, Method { + GolangXNetCodecRecv() { + // func (cd Codec) Receive(ws *Conn, v interface{}) (err error) + this.hasQualifiedName("golang.org/x/net/websocket", "Codec", "Receive") + } + + override FunctionOutput getAnOutput() { result.isParameter(1) } + } + + /** + * The `Conn.Read` method of the `golang.org/x/net/websocket` package. + */ + private class GolangXNetConnRead extends Range, Method { + GolangXNetConnRead() { + // func (ws *Conn) Read(msg []byte) (n int, err error) + this.hasQualifiedName("golang.org/x/net/websocket", "Conn", "Read") + } + + override FunctionOutput getAnOutput() { result.isParameter(0) } + } + + /** + * The `Conn.Read` method of the `nhooyr.io/websocket` package. + */ + private class NhooyrWebSocketRead extends Range, Method { + NhooyrWebSocketRead() { + // func (c *Conn) Read(ctx context.Context) (MessageType, []byte, error) + this.hasQualifiedName("nhooyr.io/websocket", "Conn", "Read") + } + + override FunctionOutput getAnOutput() { result.isResult(1) } + } + + /** + * The `Conn.Reader` method of the `nhooyr.io/websocket` package. + */ + private class NhooyrWebSocketReader extends Range, Method { + NhooyrWebSocketReader() { + // func (c *Conn) Reader(ctx context.Context) (MessageType, io.Reader, error) + this.hasQualifiedName("nhooyr.io/websocket", "Conn", "Reader") + } + + override FunctionOutput getAnOutput() { result.isResult(1) } + } + + /** + * The `ReadFrame` function of the `github.com/gobwas/ws` package. + */ + private class GobwasWsReadFrame extends Range { + GobwasWsReadFrame() { + // func ReadFrame(r io.Reader) (f Frame, err error) + this.hasQualifiedName("github.com/gobwas/ws", "ReadFrame") + } + + override FunctionOutput getAnOutput() { result.isResult(0) } + } + + /** + * The `ReadHeader` function of the `github.com/gobwas/ws` package. + */ + private class GobwasWsReadHeader extends Range { + GobwasWsReadHeader() { + // func ReadHeader(r io.Reader) (h Header, err error) + this.hasQualifiedName("github.com/gobwas/ws", "ReadHeader") + } + + override FunctionOutput getAnOutput() { result.isResult(0) } + } + + /** + * The `ReadJson` function of the `github.com/gorilla/websocket` package. + */ + private class GorillaWebSocketReadJson extends Range { + GorillaWebSocketReadJson() { + // func ReadJSON(c *Conn, v interface{}) error + this.hasQualifiedName("github.com/gorilla/websocket", "ReadJSON") + } + + override FunctionOutput getAnOutput() { result.isParameter(1) } + } + + /** + * The `Conn.ReadJson` method of the `github.com/gorilla/websocket` package. + */ + private class GorillaWebSocketConnReadJson extends Range, Method { + GorillaWebSocketConnReadJson() { + // func (c *Conn) ReadJSON(v interface{}) error + this.hasQualifiedName("github.com/gorilla/websocket", "Conn", "ReadJSON") + } + + override FunctionOutput getAnOutput() { result.isParameter(0) } + } + + /** + * The `Conn.ReadMessage` method of the `github.com/gorilla/websocket` package. + */ + private class GorillaWebSocketReadMessage extends Range, Method { + GorillaWebSocketReadMessage() { + // func (c *Conn) ReadMessage() (messageType int, p []byte, err error) + this.hasQualifiedName("github.com/gorilla/websocket", "Conn", "ReadMessage") + } + + override FunctionOutput getAnOutput() { result.isResult(1) } + } +} diff --git a/ql/src/semmle/go/security/AllocationSizeOverflowCustomizations.qll b/ql/src/semmle/go/security/AllocationSizeOverflowCustomizations.qll index fe2572ac5d0..0dbc26bd70e 100644 --- a/ql/src/semmle/go/security/AllocationSizeOverflowCustomizations.qll +++ b/ql/src/semmle/go/security/AllocationSizeOverflowCustomizations.qll @@ -51,9 +51,7 @@ module AllocationSizeOverflow { exists(MarshalingFunction marshal, DataFlow::CallNode call | call = marshal.getACall() and // rule out cases where we can tell that the result will always be small - not forall(FunctionInput inp | inp = marshal.getAnInput() | - isSmall(inp.getNode(call).asExpr()) - ) and + exists(FunctionInput inp | inp = marshal.getAnInput() | isBig(inp.getNode(call).asExpr())) and this = marshal.getOutput().getNode(call) ) } @@ -152,26 +150,17 @@ module AllocationSizeOverflow { exists(StructType st | st = t | forall(Field f | f = st.getField(_) | isSmallType(f.getType()))) } - /** Holds if `e` is an expression whose values are likely to marshal to relatively small blobs. */ - private predicate isSmall(Expr e) { - isSmallType(e.getType()) - or - e.isConst() + /** Holds if `e` is an expression whose values might marshal to relatively large blobs. */ + private predicate isBig(Expr e) { + not isSmallType(e.getType()) and + not e.isConst() or exists(KeyValueExpr kv | kv = e | - isSmall(kv.getKey()) and - isSmall(kv.getValue()) + isBig(kv.getKey()) or + isBig(kv.getValue()) ) or - isSmallCompositeLit(e, 0) - } - - /** Holds if elements `n` and above of `lit` are small. */ - private predicate isSmallCompositeLit(CompositeLit lit, int n) { - n = lit.getNumElement() - or - isSmall(lit.getElement(n)) and - isSmallCompositeLit(lit, n + 1) + isBig(e.(CompositeLit).getAnElement()) } /** diff --git a/ql/src/semmle/go/security/OpenUrlRedirectCustomizations.qll b/ql/src/semmle/go/security/OpenUrlRedirectCustomizations.qll index 7c4b0854daf..df6785a578e 100644 --- a/ql/src/semmle/go/security/OpenUrlRedirectCustomizations.qll +++ b/ql/src/semmle/go/security/OpenUrlRedirectCustomizations.qll @@ -7,6 +7,7 @@ import go import UrlConcatenation import SafeUrlFlowCustomizations +import semmle.go.dataflow.BarrierGuardUtil /** * Provides extension points for customizing the taint-tracking configuration for reasoning about @@ -57,7 +58,6 @@ module OpenUrlRedirect { | methName = "Cookie" or methName = "Cookies" or - methName = "FormValue" or methName = "MultipartReader" or methName = "PostFormValues" or methName = "Referer" or @@ -104,62 +104,22 @@ module OpenUrlRedirect { /** * A call to a function called `isLocalUrl`, `isValidRedirect`, or similar, which is - * considered a barrier for purposes of URL redirection. + * considered a barrier guard for sanitizing untrusted URLs. */ - class RedirectCheckBarrierGuard extends BarrierGuard, DataFlow::CallNode { - RedirectCheckBarrierGuard() { - this.getCalleeName().regexpMatch("(?i)(is_?)?(local_?url|valid_?redir(ect)?)") - } - - override predicate checks(Expr e, boolean outcome) { - // `isLocalUrl(e)` is a barrier for `e` if it evaluates to `true` - getAnArgument().asExpr() = e and - outcome = true - } - } + class RedirectCheckBarrierGuardAsBarrierGuard extends RedirectCheckBarrierGuard, BarrierGuard { } /** - * A check against a constant value, considered a barrier for redirection. - */ - class EqualityTestGuard extends BarrierGuard, DataFlow::EqualityTestNode { - DataFlow::Node url; - - EqualityTestGuard() { - exists(this.getAnOperand().getStringValue()) and - ( - url = this.getAnOperand() - or - exists(DataFlow::MethodCallNode mc | mc = this.getAnOperand() | - mc.getTarget().getName() = "Hostname" and - url = mc.getReceiver() - ) - ) - } - - override predicate checks(Expr e, boolean outcome) { - e = url.asExpr() and this.eq(outcome, _, _) - } - } - - /** - * A call to a regexp match function, considered as a barrier guard for unvalidated URLs. + * A call to a regexp match function, considered as a barrier guard for sanitizing untrusted URLs. * * This is overapproximate: we do not attempt to reason about the correctness of the regexp. */ - class RegexpCheck extends BarrierGuard { - RegexpMatchFunction matchfn; - DataFlow::CallNode call; + class RegexpCheckAsBarrierGuard extends RegexpCheck, BarrierGuard { } - RegexpCheck() { - matchfn.getACall() = call and - this = matchfn.getResult().getNode(call).getASuccessor*() - } - - override predicate checks(Expr e, boolean branch) { - e = matchfn.getValue().getNode(call).asExpr() and - (branch = false or branch = true) - } - } + /** + * A check against a constant value or the `Hostname` function, + * considered a barrier guard for url flow. + */ + class UrlCheckAsBarrierGuard extends UrlCheck, BarrierGuard { } } /** A sink for an open redirect, considered as a sink for safe URL flow. */ diff --git a/ql/src/semmle/go/security/ReflectedXssCustomizations.qll b/ql/src/semmle/go/security/ReflectedXssCustomizations.qll index 631c18780a7..22df9c97083 100644 --- a/ql/src/semmle/go/security/ReflectedXssCustomizations.qll +++ b/ql/src/semmle/go/security/ReflectedXssCustomizations.qll @@ -59,7 +59,7 @@ module ReflectedXss { not htmlTypeSpecified(body) and ( exists(HTTP::HeaderWrite hw | hw = body.getResponseWriter().getAHeaderWrite() | - hw.definesHeader("content-type", _) + hw.getName().getStringValue().toLowerCase() = "content-type" ) or exists(DataFlow::CallNode call | call.getTarget().hasQualifiedName("fmt", "Fprintf") | @@ -69,6 +69,14 @@ module ReflectedXss { // - '%', which could be a format string. call.getArgument(1).getStringValue().regexpMatch("^[^<%].*") ) + or + exists(DataFlow::Node pred | body = pred.getASuccessor*() | + // data starting with a character other than `<` cannot cause an HTML content type to be detected. + pred.getStringValue().regexpMatch("^[^<].*") + or + // json data cannot begin with `<` + exists(EncodingJson::MarshalFunction mf | pred = mf.getOutput().getNode(mf.getACall())) + ) ) } @@ -95,4 +103,15 @@ module ReflectedXss { ) } } + + /** + * A check against a constant value, considered a barrier for reflected XSS. + */ + class EqualityTestGuard extends SanitizerGuard, DataFlow::EqualityTestNode { + override predicate checks(Expr e, boolean outcome) { + this.getAnOperand().isConst() and + e = this.getAnOperand().asExpr() and + outcome = this.getPolarity() + } + } } diff --git a/ql/src/semmle/go/security/RequestForgeryCustomizations.qll b/ql/src/semmle/go/security/RequestForgeryCustomizations.qll index aecd5e077fb..19a0852d2a7 100644 --- a/ql/src/semmle/go/security/RequestForgeryCustomizations.qll +++ b/ql/src/semmle/go/security/RequestForgeryCustomizations.qll @@ -5,6 +5,7 @@ import go import UrlConcatenation import SafeUrlFlowCustomizations +import semmle.go.dataflow.BarrierGuardUtil /** Provides classes and predicates for the request forgery query. */ module RequestForgery { @@ -52,6 +53,19 @@ module RequestForgery { override string getKind() { result = "URL" } } + /** + * The URL of a WebSocket request, viewed as a sink for request forgery. + */ + class WebSocketCallAsSink extends Sink { + WebSocketRequestCall request; + + WebSocketCallAsSink() { this = request.getRequestUrl() } + + override DataFlow::Node getARequest() { result = request } + + override string getKind() { result = "WebSocket URL" } + } + /** * A value that is the result of prepending a string that prevents any value from controlling the * host of a URL. @@ -59,6 +73,29 @@ module RequestForgery { private class HostnameSanitizer extends SanitizerEdge { HostnameSanitizer() { hostnameSanitizingPrefixEdge(this, _) } } + + /** + * A call to a function called `isLocalUrl`, `isValidRedirect`, or similar, which is + * considered a barrier guard. + */ + class RedirectCheckBarrierGuardAsBarrierGuard extends RedirectCheckBarrierGuard, SanitizerGuard { + } + + /** + * A call to a regexp match function, considered as a barrier guard for sanitizing untrusted URLs. + * + * This is overapproximate: we do not attempt to reason about the correctness of the regexp. + */ + class RegexpCheckAsBarrierGuard extends RegexpCheck, SanitizerGuard { } + + /** + * An equality check comparing a data-flow node against a constant string, considered as + * a barrier guard for sanitizing untrusted URLs. + * + * Additionally, a check comparing `url.Hostname()` against a constant string is also + * considered a barrier guard for `url`. + */ + class UrlCheckAsBarrierGuard extends UrlCheck, SanitizerGuard { } } /** A sink for request forgery, considered as a sink for safe URL flow. */ diff --git a/ql/src/semmle/go/security/SqlInjection.qll b/ql/src/semmle/go/security/SqlInjection.qll index 70df0b4822f..82ea9a2a232 100644 --- a/ql/src/semmle/go/security/SqlInjection.qll +++ b/ql/src/semmle/go/security/SqlInjection.qll @@ -23,6 +23,10 @@ module SqlInjection { override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + NoSQL::isAdditionalMongoTaintStep(pred, succ) + } + override predicate isSanitizer(DataFlow::Node node) { super.isSanitizer(node) or node instanceof Sanitizer diff --git a/ql/src/semmle/go/security/SqlInjectionCustomizations.qll b/ql/src/semmle/go/security/SqlInjectionCustomizations.qll index b34c34954d8..006c308ed4f 100644 --- a/ql/src/semmle/go/security/SqlInjectionCustomizations.qll +++ b/ql/src/semmle/go/security/SqlInjectionCustomizations.qll @@ -39,4 +39,9 @@ module SqlInjection { class SqlQueryAsSink extends Sink { SqlQueryAsSink() { this instanceof SQL::QueryString } } + + /** A NoSQL query, considered as a taint sink for SQL injection. */ + class NoSqlQueryAsSink extends Sink { + NoSqlQueryAsSink() { this instanceof NoSQL::Query } + } } diff --git a/ql/test/consistency/UnexpectedFrontendErrors.expected b/ql/test/consistency/UnexpectedFrontendErrors.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ql/test/consistency/UnexpectedFrontendErrors.ql b/ql/test/consistency/UnexpectedFrontendErrors.ql new file mode 100644 index 00000000000..f75e2c6bf21 --- /dev/null +++ b/ql/test/consistency/UnexpectedFrontendErrors.ql @@ -0,0 +1,16 @@ +/** + * @name Unexpected frontend error + * @description This query produces a list of all errors produced by the Go frontend + * during extraction, except for those occurring in files annotated with + * "// codeql test: expect frontend errors". + * @id go/unexpected-frontend-error + */ + +import go + +from Error e +where + not exists(Comment c | c.getFile() = e.getFile() | + c.getText().trim() = "codeql test: expect frontend errors" + ) +select e diff --git a/ql/test/consistency/test.go b/ql/test/consistency/test.go new file mode 100644 index 00000000000..ca40bd0f601 --- /dev/null +++ b/ql/test/consistency/test.go @@ -0,0 +1,7 @@ +package main + +// Example file with a syntax error to demonstrate use of "expect frontend errors" directive + +// codeql test: expect frontend errors + +This is not a valid Go program diff --git a/ql/test/experimental/CWE-640/EmailInjection.expected b/ql/test/experimental/CWE-640/EmailInjection.expected new file mode 100644 index 00000000000..37a3af5ae75 --- /dev/null +++ b/ql/test/experimental/CWE-640/EmailInjection.expected @@ -0,0 +1,43 @@ +edges +| email.go:24:10:24:17 | selection of Header : Header | email.go:27:56:27:67 | type conversion | +| email.go:34:21:34:31 | call to Referer : string | email.go:36:57:36:78 | type conversion | +| email.go:42:21:42:31 | call to Referer : string | email.go:45:3:45:7 | definition of write | +| email.go:51:21:51:31 | call to Referer : string | email.go:57:46:57:59 | untrustedInput | +| email.go:51:21:51:31 | call to Referer : string | email.go:58:52:58:65 | untrustedInput | +| email.go:63:21:63:31 | call to Referer : string | email.go:68:16:68:22 | content | +| email.go:73:21:73:31 | call to Referer : string | email.go:81:50:81:56 | content | +| email.go:73:21:73:31 | call to Referer : string | email.go:81:59:81:65 | content | +| email.go:73:21:73:31 | call to Referer : string | email.go:82:16:82:22 | content | +| email.go:87:21:87:31 | call to Referer : string | email.go:94:37:94:50 | untrustedInput | +| email.go:87:21:87:31 | call to Referer : string | email.go:98:16:98:23 | content2 | +nodes +| email.go:24:10:24:17 | selection of Header : Header | semmle.label | selection of Header : Header | +| email.go:27:56:27:67 | type conversion | semmle.label | type conversion | +| email.go:34:21:34:31 | call to Referer : string | semmle.label | call to Referer : string | +| email.go:36:57:36:78 | type conversion | semmle.label | type conversion | +| email.go:42:21:42:31 | call to Referer : string | semmle.label | call to Referer : string | +| email.go:45:3:45:7 | definition of write | semmle.label | definition of write | +| email.go:51:21:51:31 | call to Referer : string | semmle.label | call to Referer : string | +| email.go:57:46:57:59 | untrustedInput | semmle.label | untrustedInput | +| email.go:58:52:58:65 | untrustedInput | semmle.label | untrustedInput | +| email.go:63:21:63:31 | call to Referer : string | semmle.label | call to Referer : string | +| email.go:68:16:68:22 | content | semmle.label | content | +| email.go:73:21:73:31 | call to Referer : string | semmle.label | call to Referer : string | +| email.go:81:50:81:56 | content | semmle.label | content | +| email.go:81:59:81:65 | content | semmle.label | content | +| email.go:82:16:82:22 | content | semmle.label | content | +| email.go:87:21:87:31 | call to Referer : string | semmle.label | call to Referer : string | +| email.go:94:37:94:50 | untrustedInput | semmle.label | untrustedInput | +| email.go:98:16:98:23 | content2 | semmle.label | content2 | +#select +| email.go:27:56:27:67 | type conversion | email.go:24:10:24:17 | selection of Header : Header | email.go:27:56:27:67 | type conversion | Email content may contain $@. | email.go:24:10:24:17 | selection of Header | untrusted input | +| email.go:36:57:36:78 | type conversion | email.go:34:21:34:31 | call to Referer : string | email.go:36:57:36:78 | type conversion | Email content may contain $@. | email.go:34:21:34:31 | call to Referer | untrusted input | +| email.go:45:3:45:7 | definition of write | email.go:42:21:42:31 | call to Referer : string | email.go:45:3:45:7 | definition of write | Email content may contain $@. | email.go:42:21:42:31 | call to Referer | untrusted input | +| email.go:57:46:57:59 | untrustedInput | email.go:51:21:51:31 | call to Referer : string | email.go:57:46:57:59 | untrustedInput | Email content may contain $@. | email.go:51:21:51:31 | call to Referer | untrusted input | +| email.go:58:52:58:65 | untrustedInput | email.go:51:21:51:31 | call to Referer : string | email.go:58:52:58:65 | untrustedInput | Email content may contain $@. | email.go:51:21:51:31 | call to Referer | untrusted input | +| email.go:68:16:68:22 | content | email.go:63:21:63:31 | call to Referer : string | email.go:68:16:68:22 | content | Email content may contain $@. | email.go:63:21:63:31 | call to Referer | untrusted input | +| email.go:81:50:81:56 | content | email.go:73:21:73:31 | call to Referer : string | email.go:81:50:81:56 | content | Email content may contain $@. | email.go:73:21:73:31 | call to Referer | untrusted input | +| email.go:81:59:81:65 | content | email.go:73:21:73:31 | call to Referer : string | email.go:81:59:81:65 | content | Email content may contain $@. | email.go:73:21:73:31 | call to Referer | untrusted input | +| email.go:82:16:82:22 | content | email.go:73:21:73:31 | call to Referer : string | email.go:82:16:82:22 | content | Email content may contain $@. | email.go:73:21:73:31 | call to Referer | untrusted input | +| email.go:94:37:94:50 | untrustedInput | email.go:87:21:87:31 | call to Referer : string | email.go:94:37:94:50 | untrustedInput | Email content may contain $@. | email.go:87:21:87:31 | call to Referer | untrusted input | +| email.go:98:16:98:23 | content2 | email.go:87:21:87:31 | call to Referer : string | email.go:98:16:98:23 | content2 | Email content may contain $@. | email.go:87:21:87:31 | call to Referer | untrusted input | diff --git a/ql/test/experimental/CWE-640/EmailInjection.qlref b/ql/test/experimental/CWE-640/EmailInjection.qlref new file mode 100644 index 00000000000..a6d8abad1c9 --- /dev/null +++ b/ql/test/experimental/CWE-640/EmailInjection.qlref @@ -0,0 +1 @@ +experimental/CWE-640/EmailInjection.ql \ No newline at end of file diff --git a/ql/test/experimental/CWE-640/email.go b/ql/test/experimental/CWE-640/email.go new file mode 100644 index 00000000000..44a54b59fe7 --- /dev/null +++ b/ql/test/experimental/CWE-640/email.go @@ -0,0 +1,115 @@ +package main + +//go:generate depstubber -vendor github.com/sendgrid/sendgrid-go/helpers/mail "" NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit + +import ( + "io" + "log" + "net/http" + "net/smtp" + + sendgrid "github.com/sendgrid/sendgrid-go/helpers/mail" +) + +// OK +func mailGood(w http.ResponseWriter, r *http.Request) { + host := config["Host"] + token := backend.getUserSecretResetToken(email) + body := "Click to reset password: " + host + "/" + token + smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body)) +} + +// Not OK +func mail(w http.ResponseWriter, r *http.Request) { + host := r.Header.Get("Host") + token := backend.getUserSecretResetToken(email) + body := "Click to reset password: " + host + "/" + token + smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body)) +} + +func main() { + + // Not OK + http.HandleFunc("/ex0", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(untrustedInput)) + + }) + + // Not OK + http.HandleFunc("/ex1", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + s, _ := smtp.Dial("test.test") + write, _ := s.Data() + io.WriteString(write, untrustedInput) + }) + + // Not OK + http.HandleFunc("/ex2", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + from := sendgrid.NewEmail("from", "from@from.com") + to := sendgrid.NewEmail("to", "to@to.com") + subject := "test" + body := "body" + sendgrid.NewSingleEmail(from, subject, to, untrustedInput, body) + sendgrid.NewSingleEmail(from, subject, to, body, untrustedInput) + }) + + // Not OK + http.HandleFunc("/ex3", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + content := sendgrid.NewContent("text/html", untrustedInput) + + v := sendgrid.NewV3Mail() + v.AddContent(content) + }) + + // Not OK + http.HandleFunc("/ex4", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + from := sendgrid.NewEmail("from", "from@from.com") + to := sendgrid.NewEmail("to", "to@to.com") + subject := "test" + + content := sendgrid.NewContent("text/html", untrustedInput) + + v := sendgrid.NewV3MailInit(from, subject, to, content, content) + v.AddContent(content) + }) + + // Not OK + http.HandleFunc("/ex5", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + from := sendgrid.NewEmail("from", "from@from.com") + to := sendgrid.NewEmail("to", "to@to.com") + + content := sendgrid.NewContent("text/html", "test") + + v := sendgrid.NewV3MailInit(from, untrustedInput, to, content, content) + + content2 := sendgrid.NewContent("text/html", untrustedInput) + + v.AddContent(content2) + }) + + log.Println(http.ListenAndServe(":80", nil)) + +} + +// Backend is an empty struct +type Backend struct{} + +func (*Backend) getUserSecretResetToken(email string) string { + return "" +} + +var email = "test@test.com" + +var config map[string]string +var backend = &Backend{} diff --git a/ql/test/experimental/CWE-640/go.mod b/ql/test/experimental/CWE-640/go.mod new file mode 100644 index 00000000000..979044b6bf2 --- /dev/null +++ b/ql/test/experimental/CWE-640/go.mod @@ -0,0 +1,7 @@ +module main + +go 1.14 + +require ( + github.com/sendgrid/sendgrid-go v3.5.0+incompatible +) diff --git a/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE b/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE new file mode 100644 index 00000000000..e5441aa6a0a --- /dev/null +++ b/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2019 Twilio SendGrid, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go b/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go new file mode 100644 index 00000000000..cd1956cecc0 --- /dev/null +++ b/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go @@ -0,0 +1,391 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/sendgrid/sendgrid-go/helpers/mail, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/sendgrid/sendgrid-go/helpers/mail (exports: ; functions: NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit) + +// Package mail is a stub of github.com/sendgrid/sendgrid-go/helpers/mail, generated by depstubber. +package mail + +import () + +type Asm struct { + GroupID int + GroupsToDisplay []int +} + +func (_ *Asm) AddGroupsToDisplay(_ ...int) *Asm { + return nil +} + +func (_ *Asm) SetGroupID(_ int) *Asm { + return nil +} + +type Attachment struct { + Content string + Type string + Name string + Filename string + Disposition string + ContentID string +} + +func (_ *Attachment) SetContent(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetContentID(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetDisposition(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetFilename(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetType(_ string) *Attachment { + return nil +} + +type BccSetting struct { + Enable *bool + Email string +} + +func (_ *BccSetting) SetEmail(_ string) *BccSetting { + return nil +} + +func (_ *BccSetting) SetEnable(_ bool) *BccSetting { + return nil +} + +type ClickTrackingSetting struct { + Enable *bool + EnableText *bool +} + +func (_ *ClickTrackingSetting) SetEnable(_ bool) *ClickTrackingSetting { + return nil +} + +func (_ *ClickTrackingSetting) SetEnableText(_ bool) *ClickTrackingSetting { + return nil +} + +type Content struct { + Type string + Value string +} + +type Email struct { + Name string + Address string +} + +type FooterSetting struct { + Enable *bool + Text string + Html string +} + +func (_ *FooterSetting) SetEnable(_ bool) *FooterSetting { + return nil +} + +func (_ *FooterSetting) SetHTML(_ string) *FooterSetting { + return nil +} + +func (_ *FooterSetting) SetText(_ string) *FooterSetting { + return nil +} + +type GaSetting struct { + Enable *bool + CampaignSource string + CampaignTerm string + CampaignContent string + CampaignName string + CampaignMedium string +} + +func (_ *GaSetting) SetCampaignContent(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignMedium(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignName(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignSource(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignTerm(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetEnable(_ bool) *GaSetting { + return nil +} + +type MailSettings struct { + BCC *BccSetting + BypassListManagement *Setting + Footer *FooterSetting + SandboxMode *Setting + SpamCheckSetting *SpamCheckSetting +} + +func (_ *MailSettings) SetBCC(_ *BccSetting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetBypassListManagement(_ *Setting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetFooter(_ *FooterSetting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetSandboxMode(_ *Setting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetSpamCheckSettings(_ *SpamCheckSetting) *MailSettings { + return nil +} + +func NewContent(_ string, _ string) *Content { + return nil +} + +func NewEmail(_ string, _ string) *Email { + return nil +} + +func NewSingleEmail(_ *Email, _ string, _ *Email, _ string, _ string) *SGMailV3 { + return nil +} + +func NewV3Mail() *SGMailV3 { + return nil +} + +func NewV3MailInit(_ *Email, _ string, _ *Email, _ ...*Content) *SGMailV3 { + return nil +} + +type OpenTrackingSetting struct { + Enable *bool + SubstitutionTag string +} + +func (_ *OpenTrackingSetting) SetEnable(_ bool) *OpenTrackingSetting { + return nil +} + +func (_ *OpenTrackingSetting) SetSubstitutionTag(_ string) *OpenTrackingSetting { + return nil +} + +type Personalization struct { + To []*Email + CC []*Email + BCC []*Email + Subject string + Headers map[string]string + Substitutions map[string]string + CustomArgs map[string]string + DynamicTemplateData map[string]interface{} + Categories []string + SendAt int +} + +func (_ *Personalization) AddBCCs(_ ...*Email) {} + +func (_ *Personalization) AddCCs(_ ...*Email) {} + +func (_ *Personalization) AddTos(_ ...*Email) {} + +func (_ *Personalization) SetCustomArg(_ string, _ string) {} + +func (_ *Personalization) SetDynamicTemplateData(_ string, _ interface{}) {} + +func (_ *Personalization) SetHeader(_ string, _ string) {} + +func (_ *Personalization) SetSendAt(_ int) {} + +func (_ *Personalization) SetSubstitution(_ string, _ string) {} + +type SGMailV3 struct { + From *Email + Subject string + Personalizations []*Personalization + Content []*Content + Attachments []*Attachment + TemplateID string + Sections map[string]string + Headers map[string]string + Categories []string + CustomArgs map[string]string + SendAt int + BatchID string + Asm *Asm + IPPoolID string + MailSettings *MailSettings + TrackingSettings *TrackingSettings + ReplyTo *Email +} + +func (_ *SGMailV3) AddAttachment(_ ...*Attachment) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddCategories(_ ...string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddContent(_ ...*Content) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddPersonalizations(_ ...*Personalization) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddSection(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetASM(_ *Asm) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetBatchID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetCustomArg(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetFrom(_ *Email) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetHeader(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetIPPoolID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetMailSettings(_ *MailSettings) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetReplyTo(_ *Email) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetSendAt(_ int) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetTemplateID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetTrackingSettings(_ *TrackingSettings) *SGMailV3 { + return nil +} + +type SandboxModeSetting struct { + Enable *bool + ForwardSpam *bool + SpamCheck *SpamCheckSetting +} + +type Setting struct { + Enable *bool +} + +type SpamCheckSetting struct { + Enable *bool + SpamThreshold int + PostToURL string +} + +func (_ *SpamCheckSetting) SetEnable(_ bool) *SpamCheckSetting { + return nil +} + +func (_ *SpamCheckSetting) SetPostToURL(_ string) *SpamCheckSetting { + return nil +} + +func (_ *SpamCheckSetting) SetSpamThreshold(_ int) *SpamCheckSetting { + return nil +} + +type SubscriptionTrackingSetting struct { + Enable *bool + Text string + Html string + SubstitutionTag string +} + +func (_ *SubscriptionTrackingSetting) SetEnable(_ bool) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetHTML(_ string) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetSubstitutionTag(_ string) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetText(_ string) *SubscriptionTrackingSetting { + return nil +} + +type TrackingSettings struct { + ClickTracking *ClickTrackingSetting + OpenTracking *OpenTrackingSetting + SubscriptionTracking *SubscriptionTrackingSetting + GoogleAnalytics *GaSetting + BCC *BccSetting + BypassListManagement *Setting + Footer *FooterSetting + SandboxMode *SandboxModeSetting +} + +func (_ *TrackingSettings) SetClickTracking(_ *ClickTrackingSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetGoogleAnalytics(_ *GaSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetOpenTracking(_ *OpenTrackingSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetSubscriptionTracking(_ *SubscriptionTrackingSetting) *TrackingSettings { + return nil +} diff --git a/ql/test/experimental/CWE-640/vendor/modules.txt b/ql/test/experimental/CWE-640/vendor/modules.txt new file mode 100644 index 00000000000..4b7525957df --- /dev/null +++ b/ql/test/experimental/CWE-640/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/sendgrid/sendgrid-go v3.5.0+incompatible +## explicit +github.com/sendgrid/sendgrid-go diff --git a/ql/test/experimental/CWE-681/IncorrectNumericConversion.expected b/ql/test/experimental/CWE-681/IncorrectNumericConversion.expected new file mode 100644 index 00000000000..64aa194012e --- /dev/null +++ b/ql/test/experimental/CWE-681/IncorrectNumericConversion.expected @@ -0,0 +1,83 @@ +edges +| IncorrectNumericConversion.go:26:14:26:28 | call to Atoi : tuple type | IncorrectNumericConversion.go:35:41:35:50 | type conversion | +| IncorrectNumericConversion.go:53:18:53:47 | call to ParseFloat : tuple type | IncorrectNumericConversion.go:57:7:57:19 | type conversion | +| IncorrectNumericConversion.go:60:18:60:47 | call to ParseFloat : tuple type | IncorrectNumericConversion.go:64:7:64:19 | type conversion | +| IncorrectNumericConversion.go:69:18:69:49 | call to ParseInt : tuple type | IncorrectNumericConversion.go:73:7:73:18 | type conversion | +| IncorrectNumericConversion.go:76:18:76:49 | call to ParseInt : tuple type | IncorrectNumericConversion.go:80:7:80:19 | type conversion | +| IncorrectNumericConversion.go:83:18:83:49 | call to ParseInt : tuple type | IncorrectNumericConversion.go:87:7:87:19 | type conversion | +| IncorrectNumericConversion.go:90:18:90:48 | call to ParseInt : tuple type | IncorrectNumericConversion.go:94:7:94:19 | type conversion | +| IncorrectNumericConversion.go:99:18:99:50 | call to ParseUint : tuple type | IncorrectNumericConversion.go:103:7:103:18 | type conversion | +| IncorrectNumericConversion.go:106:18:106:50 | call to ParseUint : tuple type | IncorrectNumericConversion.go:110:7:110:19 | type conversion | +| IncorrectNumericConversion.go:113:18:113:50 | call to ParseUint : tuple type | IncorrectNumericConversion.go:117:7:117:19 | type conversion | +| IncorrectNumericConversion.go:120:18:120:49 | call to ParseUint : tuple type | IncorrectNumericConversion.go:124:7:124:19 | type conversion | +| IncorrectNumericConversion.go:208:18:208:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:212:7:212:18 | type conversion | +| IncorrectNumericConversion.go:215:18:215:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:219:7:219:19 | type conversion | +| IncorrectNumericConversion.go:222:18:222:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:226:7:226:19 | type conversion | +| IncorrectNumericConversion.go:229:18:229:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:233:7:233:19 | type conversion | +| IncorrectNumericConversion.go:236:18:236:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:240:7:240:20 | type conversion | +| IncorrectNumericConversion.go:243:18:243:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:247:7:247:20 | type conversion | +| IncorrectNumericConversion.go:250:18:250:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:254:7:254:21 | type conversion | +| IncorrectNumericConversion.go:257:18:257:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:262:7:262:18 | type conversion | +| IncorrectNumericConversion.go:266:18:266:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:270:7:270:23 | type conversion | +nodes +| IncorrectNumericConversion.go:26:14:26:28 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:35:41:35:50 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:53:18:53:47 | call to ParseFloat : tuple type | semmle.label | call to ParseFloat : tuple type | +| IncorrectNumericConversion.go:57:7:57:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:60:18:60:47 | call to ParseFloat : tuple type | semmle.label | call to ParseFloat : tuple type | +| IncorrectNumericConversion.go:64:7:64:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:69:18:69:49 | call to ParseInt : tuple type | semmle.label | call to ParseInt : tuple type | +| IncorrectNumericConversion.go:73:7:73:18 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:76:18:76:49 | call to ParseInt : tuple type | semmle.label | call to ParseInt : tuple type | +| IncorrectNumericConversion.go:80:7:80:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:83:18:83:49 | call to ParseInt : tuple type | semmle.label | call to ParseInt : tuple type | +| IncorrectNumericConversion.go:87:7:87:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:90:18:90:48 | call to ParseInt : tuple type | semmle.label | call to ParseInt : tuple type | +| IncorrectNumericConversion.go:94:7:94:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:99:18:99:50 | call to ParseUint : tuple type | semmle.label | call to ParseUint : tuple type | +| IncorrectNumericConversion.go:103:7:103:18 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:106:18:106:50 | call to ParseUint : tuple type | semmle.label | call to ParseUint : tuple type | +| IncorrectNumericConversion.go:110:7:110:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:113:18:113:50 | call to ParseUint : tuple type | semmle.label | call to ParseUint : tuple type | +| IncorrectNumericConversion.go:117:7:117:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:120:18:120:49 | call to ParseUint : tuple type | semmle.label | call to ParseUint : tuple type | +| IncorrectNumericConversion.go:124:7:124:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:208:18:208:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:212:7:212:18 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:215:18:215:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:219:7:219:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:222:18:222:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:226:7:226:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:229:18:229:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:233:7:233:19 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:236:18:236:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:240:7:240:20 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:243:18:243:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:247:7:247:20 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:250:18:250:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:254:7:254:21 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:257:18:257:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:262:7:262:18 | type conversion | semmle.label | type conversion | +| IncorrectNumericConversion.go:266:18:266:36 | call to Atoi : tuple type | semmle.label | call to Atoi : tuple type | +| IncorrectNumericConversion.go:270:7:270:23 | type conversion | semmle.label | type conversion | +#select +| IncorrectNumericConversion.go:26:14:26:28 | call to Atoi | IncorrectNumericConversion.go:26:14:26:28 | call to Atoi : tuple type | IncorrectNumericConversion.go:35:41:35:50 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type int32 | +| IncorrectNumericConversion.go:53:18:53:47 | call to ParseFloat | IncorrectNumericConversion.go:53:18:53:47 | call to ParseFloat : tuple type | IncorrectNumericConversion.go:57:7:57:19 | type conversion | Incorrect conversion of a 32-bit number from strconv.ParseFloat result to a lower bit size type int16 | +| IncorrectNumericConversion.go:60:18:60:47 | call to ParseFloat | IncorrectNumericConversion.go:60:18:60:47 | call to ParseFloat : tuple type | IncorrectNumericConversion.go:64:7:64:19 | type conversion | Incorrect conversion of a 64-bit number from strconv.ParseFloat result to a lower bit size type int32 | +| IncorrectNumericConversion.go:69:18:69:49 | call to ParseInt | IncorrectNumericConversion.go:69:18:69:49 | call to ParseInt : tuple type | IncorrectNumericConversion.go:73:7:73:18 | type conversion | Incorrect conversion of a 16-bit number from strconv.ParseInt result to a lower bit size type int8 | +| IncorrectNumericConversion.go:76:18:76:49 | call to ParseInt | IncorrectNumericConversion.go:76:18:76:49 | call to ParseInt : tuple type | IncorrectNumericConversion.go:80:7:80:19 | type conversion | Incorrect conversion of a 32-bit number from strconv.ParseInt result to a lower bit size type int16 | +| IncorrectNumericConversion.go:83:18:83:49 | call to ParseInt | IncorrectNumericConversion.go:83:18:83:49 | call to ParseInt : tuple type | IncorrectNumericConversion.go:87:7:87:19 | type conversion | Incorrect conversion of a 64-bit number from strconv.ParseInt result to a lower bit size type int32 | +| IncorrectNumericConversion.go:90:18:90:48 | call to ParseInt | IncorrectNumericConversion.go:90:18:90:48 | call to ParseInt : tuple type | IncorrectNumericConversion.go:94:7:94:19 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.ParseInt result to a lower bit size type int32 | +| IncorrectNumericConversion.go:99:18:99:50 | call to ParseUint | IncorrectNumericConversion.go:99:18:99:50 | call to ParseUint : tuple type | IncorrectNumericConversion.go:103:7:103:18 | type conversion | Incorrect conversion of a 16-bit number from strconv.ParseUint result to a lower bit size type int8 | +| IncorrectNumericConversion.go:106:18:106:50 | call to ParseUint | IncorrectNumericConversion.go:106:18:106:50 | call to ParseUint : tuple type | IncorrectNumericConversion.go:110:7:110:19 | type conversion | Incorrect conversion of a 32-bit number from strconv.ParseUint result to a lower bit size type int16 | +| IncorrectNumericConversion.go:113:18:113:50 | call to ParseUint | IncorrectNumericConversion.go:113:18:113:50 | call to ParseUint : tuple type | IncorrectNumericConversion.go:117:7:117:19 | type conversion | Incorrect conversion of a 64-bit number from strconv.ParseUint result to a lower bit size type int32 | +| IncorrectNumericConversion.go:120:18:120:49 | call to ParseUint | IncorrectNumericConversion.go:120:18:120:49 | call to ParseUint : tuple type | IncorrectNumericConversion.go:124:7:124:19 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.ParseUint result to a lower bit size type int32 | +| IncorrectNumericConversion.go:208:18:208:36 | call to Atoi | IncorrectNumericConversion.go:208:18:208:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:212:7:212:18 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type int8 | +| IncorrectNumericConversion.go:215:18:215:36 | call to Atoi | IncorrectNumericConversion.go:215:18:215:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:219:7:219:19 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type int16 | +| IncorrectNumericConversion.go:222:18:222:36 | call to Atoi | IncorrectNumericConversion.go:222:18:222:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:226:7:226:19 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type int32 | +| IncorrectNumericConversion.go:229:18:229:36 | call to Atoi | IncorrectNumericConversion.go:229:18:229:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:233:7:233:19 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type uint8 | +| IncorrectNumericConversion.go:236:18:236:36 | call to Atoi | IncorrectNumericConversion.go:236:18:236:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:240:7:240:20 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type uint16 | +| IncorrectNumericConversion.go:243:18:243:36 | call to Atoi | IncorrectNumericConversion.go:243:18:243:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:247:7:247:20 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type uint32 | +| IncorrectNumericConversion.go:250:18:250:36 | call to Atoi | IncorrectNumericConversion.go:250:18:250:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:254:7:254:21 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type float32 | +| IncorrectNumericConversion.go:257:18:257:36 | call to Atoi | IncorrectNumericConversion.go:257:18:257:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:262:7:262:18 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type uint8 | +| IncorrectNumericConversion.go:266:18:266:36 | call to Atoi | IncorrectNumericConversion.go:266:18:266:36 | call to Atoi : tuple type | IncorrectNumericConversion.go:270:7:270:23 | type conversion | Incorrect conversion of a (arch-dependent)-bit number from strconv.Atoi result to a lower bit size type int16 | diff --git a/ql/test/experimental/CWE-681/IncorrectNumericConversion.go b/ql/test/experimental/CWE-681/IncorrectNumericConversion.go new file mode 100644 index 00000000000..cdabda91a4a --- /dev/null +++ b/ql/test/experimental/CWE-681/IncorrectNumericConversion.go @@ -0,0 +1,355 @@ +package main + +import ( + "math" + "strconv" +) + +func main() { + +} + +type Something struct { +} +type Config struct { +} +type Registry struct { +} + +func LookupTarget(conf *Config, num int32) (int32, error) { + return 567, nil +} +func LookupNumberByName(reg *Registry, name string) (int32, error) { + return 567, nil +} +func lab(s string) (*Something, error) { + num, err := strconv.Atoi(s) + + if err != nil { + number, err := LookupNumberByName(&Registry{}, s) + if err != nil { + return nil, err + } + num = int(number) + } + target, err := LookupTarget(&Config{}, int32(num)) + if err != nil { + return nil, err + } + + // convert the resolved target number back to a string + + s = strconv.Itoa(int(target)) + + return nil, nil +} + +const CustomMaxInt16 = 1<<15 - 1 + +type CustomInt int16 + +func badParseFloat() { + { + parsed, err := strconv.ParseFloat("1.32", 32) + if err != nil { + panic(err) + } + _ = int16(parsed) + } + { + parsed, err := strconv.ParseFloat("1.32", 64) + if err != nil { + panic(err) + } + _ = int32(parsed) + } +} +func badParseInt() { + { + parsed, err := strconv.ParseInt("3456", 10, 16) + if err != nil { + panic(err) + } + _ = int8(parsed) + } + { + parsed, err := strconv.ParseInt("3456", 10, 32) + if err != nil { + panic(err) + } + _ = int16(parsed) + } + { + parsed, err := strconv.ParseInt("3456", 10, 64) + if err != nil { + panic(err) + } + _ = int32(parsed) + } + { + parsed, err := strconv.ParseInt("3456", 10, 0) + if err != nil { + panic(err) + } + _ = int32(parsed) + } +} +func badParseUint() { + { + parsed, err := strconv.ParseUint("3456", 10, 16) + if err != nil { + panic(err) + } + _ = int8(parsed) + } + { + parsed, err := strconv.ParseUint("3456", 10, 32) + if err != nil { + panic(err) + } + _ = int16(parsed) + } + { + parsed, err := strconv.ParseUint("3456", 10, 64) + if err != nil { + panic(err) + } + _ = int32(parsed) + } + { + parsed, err := strconv.ParseUint("3456", 10, 0) + if err != nil { + panic(err) + } + _ = int32(parsed) + } +} + +func goodParseFloat() { + { + parsed, err := strconv.ParseFloat("1.32", 32) + if err != nil { + panic(err) + } + _ = int32(parsed) + } + { + parsed, err := strconv.ParseFloat("1.32", 64) + if err != nil { + panic(err) + } + _ = int64(parsed) + } +} +func goodParseInt() { + { + parsed, err := strconv.ParseInt("3456", 10, 16) + if err != nil { + panic(err) + } + _ = int16(parsed) + } + { + parsed, err := strconv.ParseInt("3456", 10, 32) + if err != nil { + panic(err) + } + _ = int32(parsed) + } + { + parsed, err := strconv.ParseInt("3456", 10, 64) + if err != nil { + panic(err) + } + _ = int64(parsed) + } + { + parsed, err := strconv.ParseInt("3456", 10, 0) + if err != nil { + panic(err) + } + _ = int64(parsed) + } +} +func goodParseUint() { + { + parsed, err := strconv.ParseUint("3456", 10, 16) + if err != nil { + panic(err) + } + _ = int16(parsed) + } + { + parsed, err := strconv.ParseUint("3456", 10, 32) + if err != nil { + panic(err) + } + _ = int32(parsed) + } + { + parsed, err := strconv.ParseUint("3456", 10, 64) + if err != nil { + panic(err) + } + _ = int64(parsed) + } + { + parsed, err := strconv.ParseUint("3456", 10, 0) + if err != nil { + panic(err) + } + _ = int64(parsed) + } +} + +// these should be caught: +func upperBoundIsNOTChecked(input string) { + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + _ = int8(parsed) + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + _ = int16(parsed) + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + _ = int32(parsed) + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + _ = uint8(parsed) + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + _ = uint16(parsed) + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + _ = uint32(parsed) + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + _ = float32(parsed) + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + // NOTE: byte is uint8 + _ = byte(parsed) + } + { + // using custom type: + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + _ = CustomInt(parsed) + } + +} + +// these should NOT be caught: +func upperBoundIsChecked(input string) { + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + if parsed < math.MaxInt8 { + _ = int8(parsed) + } + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + if parsed < math.MaxInt16 { + _ = int16(parsed) + } + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + if parsed > 0 { + _ = int32(parsed) + } + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + if parsed < math.MaxInt32 { + _ = int32(parsed) + } + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + if parsed < math.MaxUint8 { + _ = uint8(parsed) + } + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + if parsed < math.MaxUint16 { + _ = uint16(parsed) + } + } + { + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + if parsed < math.MaxUint8 { + _ = byte(parsed) + } + } + { // multiple `and` conditions + parsed, err := strconv.Atoi(input) + if err == nil && 1 == 1 && parsed < math.MaxInt8 { + _ = int8(parsed) + } + } + { // custom maxInt16 + parsed, err := strconv.Atoi(input) + if err != nil { + panic(err) + } + if parsed < CustomMaxInt16 { + _ = int16(parsed) + } + } +} diff --git a/ql/test/experimental/CWE-681/IncorrectNumericConversion.qlref b/ql/test/experimental/CWE-681/IncorrectNumericConversion.qlref new file mode 100644 index 00000000000..884cf918f1c --- /dev/null +++ b/ql/test/experimental/CWE-681/IncorrectNumericConversion.qlref @@ -0,0 +1 @@ +experimental/CWE-681/IncorrectNumericConversion.ql diff --git a/ql/test/experimental/CWE-807/SensitiveConditionBypass.expected b/ql/test/experimental/CWE-807/SensitiveConditionBypass.expected new file mode 100644 index 00000000000..a9dbfa8f426 --- /dev/null +++ b/ql/test/experimental/CWE-807/SensitiveConditionBypass.expected @@ -0,0 +1,4 @@ +| SensitiveConditionBypassBad.go:7:5:7:39 | ...!=... | This sensitive comparision check can potentially be bypassed. | +| condition.go:16:5:16:34 | ...!=... | This sensitive comparision check can potentially be bypassed. | +| condition.go:25:5:25:35 | ...!=... | This sensitive comparision check can potentially be bypassed. | +| condition.go:34:5:34:35 | ...!=... | This sensitive comparision check can potentially be bypassed. | diff --git a/ql/test/experimental/CWE-807/SensitiveConditionBypass.qlref b/ql/test/experimental/CWE-807/SensitiveConditionBypass.qlref new file mode 100644 index 00000000000..da2ab35074a --- /dev/null +++ b/ql/test/experimental/CWE-807/SensitiveConditionBypass.qlref @@ -0,0 +1 @@ +experimental/CWE-807/SensitiveConditionBypass.ql diff --git a/ql/test/experimental/CWE-807/SensitiveConditionBypassBad.go b/ql/test/experimental/CWE-807/SensitiveConditionBypassBad.go new file mode 100644 index 00000000000..bf8e70f88b7 --- /dev/null +++ b/ql/test/experimental/CWE-807/SensitiveConditionBypassBad.go @@ -0,0 +1,10 @@ +package main + +import "net/http" + +func example(w http.ResponseWriter, r *http.Request) { + test2 := "test" + if r.Header.Get("X-Password") != test2 { + login() + } +} diff --git a/ql/test/experimental/CWE-807/condition.go b/ql/test/experimental/CWE-807/condition.go new file mode 100644 index 00000000000..ecd6b0a9f2a --- /dev/null +++ b/ql/test/experimental/CWE-807/condition.go @@ -0,0 +1,89 @@ +package main + +import ( + "io" + "net/http" +) + +func use(xs ...interface{}) {} +func t(xs ...interface{}) string { return "sadsad" } +func login(xs ...interface{}) {} + +const test = "localhost" + +// Should alert as authkey is sensitive +func ex1(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != test { + authkey := "randomDatta" + io.WriteString(w, authkey) + } +} + +// Should alert as authkey is sensitive +func ex2(w http.ResponseWriter, r *http.Request) { + test2 := "test" + if r.Header.Get("Origin") != test2 { + authkey := "randomDatta2" + io.WriteString(w, authkey) + } +} + +// Should alert as login() is sensitive +func ex3(w http.ResponseWriter, r *http.Request) { + test2 := "test" + if r.Header.Get("Origin") != test2 { + login() + } +} + +// no alert as we can't say if the rhs resolves to a fixed pattern everytime. +func ex4(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != t()+r.Header.Get("Origin") { + login() + } +} + +// No alert as use is not sensitive +func ex5(w http.ResponseWriter, r *http.Request) { + test2 := "test" + if r.Header.Get("Origin") != test2 { + use() + } +} + +// Should not alert as test is against empty string +func ex6(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != "" { + login() + } +} + +// Should not alert as test is against uri path +func ex7(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != "/asd/" { + login() + } +} + +// Should not alert as test is against uri path +func ex8(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != "/asd/a" { + login() + } +} + +// Should not alert as test is against uri path +func ex9(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != "/asd" { + login() + } +} + +// Should not alert as test is against uri path +func ex10(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != "asd/" { + login() + } +} + +func main() {} diff --git a/ql/test/experimental/CWE-840/ConditionalBypass.expected b/ql/test/experimental/CWE-840/ConditionalBypass.expected new file mode 100644 index 00000000000..c57a25bddff --- /dev/null +++ b/ql/test/experimental/CWE-840/ConditionalBypass.expected @@ -0,0 +1,3 @@ +| ConditionalBypassBad.go:9:5:9:46 | ...!=... | This comparison compares user-controlled values from $@ and $@, and hence can be bypassed. | ConditionalBypassBad.go:9:5:9:12 | selection of Header : Header | here | ConditionalBypassBad.go:9:41:9:46 | selection of Host : string | here | +| condition.go:9:5:9:46 | ...!=... | This comparison compares user-controlled values from $@ and $@, and hence can be bypassed. | condition.go:9:5:9:12 | selection of Header : Header | here | condition.go:9:41:9:46 | selection of Host : string | here | +| condition.go:16:5:16:62 | ...!=... | This comparison compares user-controlled values from $@ and $@, and hence can be bypassed. | condition.go:16:5:16:12 | selection of Header : Header | here | condition.go:16:41:16:48 | selection of Header : Header | here | diff --git a/ql/test/experimental/CWE-840/ConditionalBypass.qlref b/ql/test/experimental/CWE-840/ConditionalBypass.qlref new file mode 100644 index 00000000000..6d167616055 --- /dev/null +++ b/ql/test/experimental/CWE-840/ConditionalBypass.qlref @@ -0,0 +1 @@ +experimental/CWE-840/ConditionalBypass.ql diff --git a/ql/test/experimental/CWE-840/ConditionalBypassBad.go b/ql/test/experimental/CWE-840/ConditionalBypassBad.go new file mode 100644 index 00000000000..b788dee2009 --- /dev/null +++ b/ql/test/experimental/CWE-840/ConditionalBypassBad.go @@ -0,0 +1,12 @@ +package main + +import ( + "net/http" +) + +func exampleHandlerBad(w http.ResponseWriter, r *http.Request) { + // BAD: the Origin and Host headers are user controlled + if r.Header.Get("Origin") != "http://"+r.Host { + //do something + } +} diff --git a/ql/test/experimental/CWE-840/ConditionalBypassGood.go b/ql/test/experimental/CWE-840/ConditionalBypassGood.go new file mode 100644 index 00000000000..635d16d1f8f --- /dev/null +++ b/ql/test/experimental/CWE-840/ConditionalBypassGood.go @@ -0,0 +1,12 @@ +package main + +import ( + "net/http" +) + +func exampleHandlerGood(w http.ResponseWriter, r *http.Request) { + // GOOD: the configuration is not user controlled + if r.Header.Get("Origin") != config.get("Host") { + //do something + } +} diff --git a/ql/test/experimental/CWE-840/condition.go b/ql/test/experimental/CWE-840/condition.go new file mode 100644 index 00000000000..7b7b7480c10 --- /dev/null +++ b/ql/test/experimental/CWE-840/condition.go @@ -0,0 +1,26 @@ +package main + +import ( + "net/http" +) + +// BAD: taken from https://www.gorillatoolkit.org/pkg/websocket +func ex1(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != "http://"+r.Host { + //do something + } +} + +// BAD: both operands are from remote sources +func ex2(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != "http://"+r.Header.Get("Header") { + //do something + } +} + +// GOOD +func ex3(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Origin") != "http://"+"test" { + //do something + } +} diff --git a/ql/test/experimental/CWE-840/util.go b/ql/test/experimental/CWE-840/util.go new file mode 100644 index 00000000000..9e7a9a27f20 --- /dev/null +++ b/ql/test/experimental/CWE-840/util.go @@ -0,0 +1,9 @@ +package main + +type Config struct{} + +func (_ Config) get(s string) string { + return "" +} + +var config = Config{} diff --git a/ql/test/experimental/frameworks/Gin/Gin.expected b/ql/test/experimental/frameworks/Gin/Gin.expected new file mode 100644 index 00000000000..b30b4861d87 --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/Gin.expected @@ -0,0 +1,63 @@ +| Gin.go:23:10:23:29 | call to GetHeader | +| Gin.go:27:10:27:30 | call to QueryArray | +| Gin.go:31:10:31:25 | call to Query | +| Gin.go:35:10:35:33 | call to PostFormArray | +| Gin.go:39:10:39:28 | call to PostForm | +| Gin.go:43:10:43:25 | call to Param | +| Gin.go:47:10:47:34 | call to GetStringSlice | +| Gin.go:51:10:51:29 | call to GetString | +| Gin.go:55:3:55:28 | ... := ...[0] | +| Gin.go:59:10:59:23 | call to ClientIP | +| Gin.go:63:10:63:26 | call to ContentType | +| Gin.go:67:3:67:29 | ... := ...[0] | +| Gin.go:71:3:71:36 | ... := ...[0] | +| Gin.go:75:3:75:31 | ... := ...[0] | +| Gin.go:79:3:79:39 | ... := ...[0] | +| Gin.go:83:3:83:34 | ... := ...[0] | +| Gin.go:87:10:87:52 | call to DefaultPostForm | +| Gin.go:91:10:91:49 | call to DefaultQuery | +| Gin.go:95:3:95:37 | ... := ...[0] | +| Gin.go:99:3:99:34 | ... := ...[0] | +| Gin.go:103:10:103:32 | call to GetStringMap | +| Gin.go:107:10:107:38 | call to GetStringMapString | +| Gin.go:111:10:111:43 | call to GetStringMapStringSlice | +| Gin.go:115:10:115:31 | call to PostFormMap | +| Gin.go:119:10:119:28 | call to QueryMap | +| Gin.go:123:10:123:23 | call to FullPath | +| Gin.go:129:10:129:21 | selection of Accepted | +| Gin.go:133:10:133:19 | selection of Params | +| Gin.go:134:7:134:9 | val | +| Gin.go:134:7:134:12 | index expression | +| Gin.go:134:7:134:18 | selection of Value | +| Gin.go:139:10:139:19 | selection of Params | +| Gin.go:139:10:139:22 | index expression | +| Gin.go:140:7:140:9 | val | +| Gin.go:140:7:140:15 | selection of Value | +| Gin.go:143:10:143:19 | selection of Params | +| Gin.go:143:10:143:34 | call to ByName | +| Gin.go:147:3:147:34 | ... := ...[0] | +| Gin.go:147:13:147:22 | selection of Params | +| Gin.go:153:12:153:21 | selection of Params | +| Gin.go:153:12:153:24 | index expression | +| Gin.go:154:10:154:14 | param | +| Gin.go:154:10:154:18 | selection of Key | +| Gin.go:155:10:155:14 | param | +| Gin.go:155:10:155:20 | selection of Value | +| Gin.go:163:16:163:22 | &... | +| Gin.go:168:15:168:21 | &... | +| Gin.go:173:16:173:22 | &... | +| Gin.go:178:15:178:21 | &... | +| Gin.go:183:17:183:23 | &... | +| Gin.go:188:20:188:26 | &... | +| Gin.go:193:16:193:22 | &... | +| Gin.go:198:12:198:18 | &... | +| Gin.go:203:18:203:24 | &... | +| Gin.go:208:26:208:32 | &... | +| Gin.go:213:22:213:28 | &... | +| Gin.go:218:23:218:29 | &... | +| Gin.go:223:21:223:27 | &... | +| Gin.go:228:22:228:28 | &... | +| Gin.go:233:21:233:27 | &... | +| Gin.go:238:22:238:28 | &... | +| Gin.go:243:18:243:24 | &... | +| Gin.go:248:24:248:30 | &... | diff --git a/ql/test/experimental/frameworks/Gin/Gin.go b/ql/test/experimental/frameworks/Gin/Gin.go new file mode 100644 index 00000000000..774ce47e822 --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/Gin.go @@ -0,0 +1,251 @@ +package main + +//go:generate depstubber -vendor github.com/gin-gonic/gin Context +//go:generate depstubber -vendor github.com/gin-gonic/gin/binding "" YAML + +import ( + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" +) + +func main() {} + +type Person struct { + Name string `form:"name"` + Address string `form:"address"` +} + +func use(val string) {} + +// gin +func ginHandler(ctx *gin.Context) { + { + val := ctx.GetHeader("key") + use(val) + } + { + val := ctx.QueryArray("key") + use(val[0]) + } + { + val := ctx.Query("key") + use(val) + } + { + val := ctx.PostFormArray("key") + use(val[0]) + } + { + val := ctx.PostForm("key") + use(val) + } + { + val := ctx.Param("key") + use(val) + } + { + val := ctx.GetStringSlice("key") + use(val[0]) + } + { + val := ctx.GetString("key") + use(val) + } + { + val, _ := ctx.GetRawData() + use(string(val)) + } + { + val := ctx.ClientIP() + use(val) + } + { + val := ctx.ContentType() + use(val) + } + { + val, _ := ctx.Cookie("key") + use(val) + } + { + val, _ := ctx.GetQueryArray("key") + use(val[0]) + } + { + val, _ := ctx.GetQuery("key") + use(val) + } + { + val, _ := ctx.GetPostFormArray("key") + use(val[0]) + } + { + val, _ := ctx.GetPostForm("key") + use(val) + } + { + val := ctx.DefaultPostForm("key", "default-value") + use(val) + } + { + val := ctx.DefaultQuery("key", "default-value") + use(val) + } + { + val, _ := ctx.GetPostFormMap("key") + use(val["a"]) + } + { + val, _ := ctx.GetQueryMap("key") + use(val["a"]) + } + { + val := ctx.GetStringMap("key") + use(val["a"].(string)) + } + { + val := ctx.GetStringMapString("key") + use(val["a"]) + } + { + val := ctx.GetStringMapStringSlice("key") + use(val["a"][0]) + } + { + val := ctx.PostFormMap("key") + use(val["a"]) + } + { + val := ctx.QueryMap("key") + use(val["a"]) + } + { + val := ctx.FullPath() + use(val) + } + + // fields: + { + val := ctx.Accepted + use(val[0]) + } + { + val := ctx.Params + use(val[0].Value) + } + + // Params: + { + val := ctx.Params[0] + use(val.Value) + } + { + val := ctx.Params.ByName("name") + use(val) + } + { + val, _ := ctx.Params.Get("name") + use(val) + } + + // Param: + { + param := ctx.Params[0] + key := param.Key + val := param.Value + use(key) + use(val) + } + + // bind: + { + var person Person + ctx.BindYAML(&person) + use(person.Name) + } + { + var person Person + ctx.BindXML(&person) + use(person.Name) + } + { + var person Person + ctx.BindWith(&person, binding.YAML) + use(person.Name) + } + { + var person Person + ctx.BindUri(&person) + use(person.Name) + } + { + var person Person + ctx.BindQuery(&person) + use(person.Name) + } + { + var person Person + ctx.MustBindWith(&person, binding.YAML) + use(person.Name) + } + { + var person Person + ctx.BindJSON(&person) + use(person.Name) + } + { + var person Person + ctx.Bind(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBind(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindBodyWith(&person, binding.YAML) + use(person.Name) + } + { + var person Person + ctx.ShouldBindJSON(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindQuery(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindUri(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindWith(&person, binding.YAML) + use(person.Name) + } + { + var person Person + ctx.ShouldBindXML(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindYAML(&person) + use(person.Name) + } + { + var person Person + ctx.BindHeader(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindHeader(&person) + use(person.Name) + } +} diff --git a/ql/test/experimental/frameworks/Gin/Gin.ql b/ql/test/experimental/frameworks/Gin/Gin.ql new file mode 100644 index 00000000000..91f3324f23a --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/Gin.ql @@ -0,0 +1,4 @@ +import go +import experimental.frameworks.Gin + +select any(UntrustedFlowSource src) diff --git a/ql/test/experimental/frameworks/Gin/go.mod b/ql/test/experimental/frameworks/Gin/go.mod new file mode 100644 index 00000000000..d6312810ebf --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/go.mod @@ -0,0 +1,5 @@ +module example.com/m + +go 1.14 + +require github.com/gin-gonic/gin v1.6.2 diff --git a/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/LICENSE b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/LICENSE new file mode 100644 index 00000000000..1ff7f370605 --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Manuel Martínez-Almeida + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/binding/stub.go b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/binding/stub.go new file mode 100644 index 00000000000..43fd634edcd --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/binding/stub.go @@ -0,0 +1,12 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gin-gonic/gin/binding, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gin-gonic/gin/binding (exports: ; functions: YAML) + +// Package binding is a stub of github.com/gin-gonic/gin/binding, generated by depstubber. +package binding + +import () + +var YAML interface{} = nil diff --git a/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/stub.go b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/stub.go new file mode 100644 index 00000000000..eb68095e53e --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/stub.go @@ -0,0 +1,500 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gin-gonic/gin, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gin-gonic/gin (exports: Context; functions: ) + +// Package gin is a stub of github.com/gin-gonic/gin, generated by depstubber. +package gin + +import ( + bufio "bufio" + io "io" + multipart "mime/multipart" + net "net" + http "net/http" + sync "sync" + time "time" +) + +type Context struct { + Request *http.Request + Writer ResponseWriter + Params Params + KeysMutex *sync.RWMutex + Keys map[string]interface{} + Errors interface{} + Accepted []string +} + +func (_ *Context) Abort() {} + +func (_ *Context) AbortWithError(_ int, _ interface { + Error() string +}) *Error { + return nil +} + +func (_ *Context) AbortWithStatus(_ int) {} + +func (_ *Context) AbortWithStatusJSON(_ int, _ interface{}) {} + +func (_ *Context) AsciiJSON(_ int, _ interface{}) {} + +func (_ *Context) Bind(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindHeader(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindJSON(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindQuery(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindUri(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindWith(_ interface{}, _ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindXML(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindYAML(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ClientIP() string { + return "" +} + +func (_ *Context) ContentType() string { + return "" +} + +func (_ *Context) Cookie(_ string) (string, interface { + Error() string +}) { + return "", nil +} + +func (_ *Context) Copy() *Context { + return nil +} + +func (_ *Context) Data(_ int, _ string, _ []uint8) {} + +func (_ *Context) DataFromReader(_ int, _ int64, _ string, _ io.Reader, _ map[string]string) {} + +func (_ *Context) Deadline() (time.Time, bool) { + return time.Time{}, false +} + +func (_ *Context) DefaultPostForm(_ string, _ string) string { + return "" +} + +func (_ *Context) DefaultQuery(_ string, _ string) string { + return "" +} + +func (_ *Context) Done() <-chan struct{} { + return nil +} + +func (_ *Context) Err() interface { + Error() string +} { + return nil +} + +func (_ *Context) Error(_ interface { + Error() string +}) *Error { + return nil +} + +func (_ *Context) File(_ string) {} + +func (_ *Context) FileAttachment(_ string, _ string) {} + +func (_ *Context) FileFromFS(_ string, _ http.FileSystem) {} + +func (_ *Context) FormFile(_ string) (*multipart.FileHeader, interface { + Error() string +}) { + return nil, nil +} + +func (_ *Context) FullPath() string { + return "" +} + +func (_ *Context) Get(_ string) (interface{}, bool) { + return nil, false +} + +func (_ *Context) GetBool(_ string) bool { + return false +} + +func (_ *Context) GetDuration(_ string) time.Duration { + return 0 +} + +func (_ *Context) GetFloat64(_ string) float64 { + return 0 +} + +func (_ *Context) GetHeader(_ string) string { + return "" +} + +func (_ *Context) GetInt(_ string) int { + return 0 +} + +func (_ *Context) GetInt64(_ string) int64 { + return 0 +} + +func (_ *Context) GetPostForm(_ string) (string, bool) { + return "", false +} + +func (_ *Context) GetPostFormArray(_ string) ([]string, bool) { + return nil, false +} + +func (_ *Context) GetPostFormMap(_ string) (map[string]string, bool) { + return nil, false +} + +func (_ *Context) GetQuery(_ string) (string, bool) { + return "", false +} + +func (_ *Context) GetQueryArray(_ string) ([]string, bool) { + return nil, false +} + +func (_ *Context) GetQueryMap(_ string) (map[string]string, bool) { + return nil, false +} + +func (_ *Context) GetRawData() ([]uint8, interface { + Error() string +}) { + return nil, nil +} + +func (_ *Context) GetString(_ string) string { + return "" +} + +func (_ *Context) GetStringMap(_ string) map[string]interface{} { + return nil +} + +func (_ *Context) GetStringMapString(_ string) map[string]string { + return nil +} + +func (_ *Context) GetStringMapStringSlice(_ string) map[string][]string { + return nil +} + +func (_ *Context) GetStringSlice(_ string) []string { + return nil +} + +func (_ *Context) GetTime(_ string) time.Time { + return time.Time{} +} + +func (_ *Context) HTML(_ int, _ string, _ interface{}) {} + +func (_ *Context) Handler() HandlerFunc { + return nil +} + +func (_ *Context) HandlerName() string { + return "" +} + +func (_ *Context) HandlerNames() []string { + return nil +} + +func (_ *Context) Header(_ string, _ string) {} + +func (_ *Context) IndentedJSON(_ int, _ interface{}) {} + +func (_ *Context) IsAborted() bool { + return false +} + +func (_ *Context) IsWebsocket() bool { + return false +} + +func (_ *Context) JSON(_ int, _ interface{}) {} + +func (_ *Context) JSONP(_ int, _ interface{}) {} + +func (_ *Context) MultipartForm() (*multipart.Form, interface { + Error() string +}) { + return nil, nil +} + +func (_ *Context) MustBindWith(_ interface{}, _ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) MustGet(_ string) interface{} { + return nil +} + +func (_ *Context) Negotiate(_ int, _ Negotiate) {} + +func (_ *Context) NegotiateFormat(_ ...string) string { + return "" +} + +func (_ *Context) Next() {} + +func (_ *Context) Param(_ string) string { + return "" +} + +func (_ *Context) PostForm(_ string) string { + return "" +} + +func (_ *Context) PostFormArray(_ string) []string { + return nil +} + +func (_ *Context) PostFormMap(_ string) map[string]string { + return nil +} + +func (_ *Context) ProtoBuf(_ int, _ interface{}) {} + +func (_ *Context) PureJSON(_ int, _ interface{}) {} + +func (_ *Context) Query(_ string) string { + return "" +} + +func (_ *Context) QueryArray(_ string) []string { + return nil +} + +func (_ *Context) QueryMap(_ string) map[string]string { + return nil +} + +func (_ *Context) Redirect(_ int, _ string) {} + +func (_ *Context) Render(_ int, _ interface{}) {} + +func (_ *Context) SSEvent(_ string, _ interface{}) {} + +func (_ *Context) SaveUploadedFile(_ *multipart.FileHeader, _ string) interface { + Error() string +} { + return nil +} + +func (_ *Context) SecureJSON(_ int, _ interface{}) {} + +func (_ *Context) Set(_ string, _ interface{}) {} + +func (_ *Context) SetAccepted(_ ...string) {} + +func (_ *Context) SetCookie(_ string, _ string, _ int, _ string, _ string, _ bool, _ bool) {} + +func (_ *Context) SetSameSite(_ http.SameSite) {} + +func (_ *Context) ShouldBind(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindBodyWith(_ interface{}, _ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindHeader(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindJSON(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindQuery(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindUri(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindWith(_ interface{}, _ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindXML(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindYAML(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) Status(_ int) {} + +func (_ *Context) Stream(_ func(io.Writer) bool) bool { + return false +} + +func (_ *Context) String(_ int, _ string, _ ...interface{}) {} + +func (_ *Context) Value(_ interface{}) interface{} { + return nil +} + +func (_ *Context) XML(_ int, _ interface{}) {} + +func (_ *Context) YAML(_ int, _ interface{}) {} + +type Error struct { + Err interface { + Error() string + } + Type ErrorType + Meta interface{} +} + +func (_ Error) Error() string { + return "" +} + +func (_ *Error) IsType(_ ErrorType) bool { + return false +} + +func (_ *Error) JSON() interface{} { + return nil +} + +func (_ *Error) MarshalJSON() ([]uint8, interface { + Error() string +}) { + return nil, nil +} + +func (_ *Error) SetMeta(_ interface{}) *Error { + return nil +} + +func (_ *Error) SetType(_ ErrorType) *Error { + return nil +} + +type ErrorType uint64 + +type HandlerFunc func(*Context) + +type Negotiate struct { + Offered []string + HTMLName string + HTMLData interface{} + JSONData interface{} + XMLData interface{} + YAMLData interface{} + Data interface{} +} + +type Param struct { + Key string + Value string +} + +type Params []Param + +func (_ Params) ByName(_ string) string { + return "" +} + +func (_ Params) Get(_ string) (string, bool) { + return "", false +} + +type ResponseWriter interface { + CloseNotify() <-chan bool + Flush() + Header() http.Header + Hijack() (net.Conn, *bufio.ReadWriter, interface { + Error() string + }) + Pusher() http.Pusher + Size() int + Status() int + Write(_ []uint8) (int, interface { + Error() string + }) + WriteHeader(_ int) + WriteHeaderNow() + WriteString(_ string) (int, interface { + Error() string + }) + Written() bool +} diff --git a/ql/test/experimental/frameworks/Gin/vendor/modules.txt b/ql/test/experimental/frameworks/Gin/vendor/modules.txt new file mode 100644 index 00000000000..5f2816316f4 --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/gin-gonic/gin v1.6.2 +## explicit +github.com/gin-gonic/gin diff --git a/ql/test/library-tests/semmle/go/Packages/package.expected b/ql/test/library-tests/semmle/go/Packages/package.expected new file mode 100644 index 00000000000..256819c6151 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Packages/package.expected @@ -0,0 +1,2 @@ +| PackageName/test | +| PackageName/v2/test | diff --git a/ql/test/library-tests/semmle/go/Packages/package.ql b/ql/test/library-tests/semmle/go/Packages/package.ql new file mode 100644 index 00000000000..ff083b52aa7 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Packages/package.ql @@ -0,0 +1,18 @@ +import go + +from string path +where + ( + path = "PackageName/v2/test" or // OK + path = "PackageName/test" or // OK + path = "PackageName//v//test" or // NOT OK + path = "PackageName//v/test" or // NOT OK + path = "PackageName/v//test" or // NOT OK + path = "PackageName/v/asd/v2/test" or // NOT OK + path = "PackageName/v/test" or // NOT OK + path = "PackageName//v2//test" or // NOT OK + path = "PackageName//v2/test" or // NOT OK + path = "PackageName/v2//test" // NOT OK + ) and + path = package("PackageName", "test") +select path diff --git a/ql/test/library-tests/semmle/go/Scopes/DeclaredEntity.expected b/ql/test/library-tests/semmle/go/Scopes/DeclaredEntity.expected index aa235d0b23f..8fa90233151 100644 --- a/ql/test/library-tests/semmle/go/Scopes/DeclaredEntity.expected +++ b/ql/test/library-tests/semmle/go/Scopes/DeclaredEntity.expected @@ -23,3 +23,5 @@ | types.go:33:16:33:20 | meth1 | types.go:33:16:33:20 | meth1 | | types.go:33:22:33:22 | a | types.go:33:22:33:22 | a | | types.go:37:16:37:20 | meth2 | types.go:37:16:37:20 | meth2 | +| types.go:41:6:41:27 | iHaveARedeclaredMethod | types.go:41:6:41:27 | iHaveARedeclaredMethod | +| types.go:43:2:43:5 | meth | types.go:43:2:43:5 | meth | diff --git a/ql/test/library-tests/semmle/go/Scopes/EntityReference.expected b/ql/test/library-tests/semmle/go/Scopes/EntityReference.expected index 97fa0236baf..d78211a4c66 100644 --- a/ql/test/library-tests/semmle/go/Scopes/EntityReference.expected +++ b/ql/test/library-tests/semmle/go/Scopes/EntityReference.expected @@ -13,6 +13,7 @@ | file://:0:0:0:0 | int | | types.go:27:25:27:27 | int | | file://:0:0:0:0 | int | | types.go:33:24:33:26 | int | | file://:0:0:0:0 | int | | types.go:37:24:37:26 | int | +| file://:0:0:0:0 | int | | types.go:43:9:43:11 | int | | main.go:5:6:5:6 | t | main.go@5:6:5:6 | main.go:5:6:5:6 | t | | main.go:5:6:5:6 | t | main.go@5:6:5:6 | main.go:13:13:13:13 | t | | main.go:5:6:5:6 | t | main.go@5:6:5:6 | main.go:17:29:17:29 | t | @@ -42,6 +43,7 @@ | main.go:23:16:23:19 | bump | main.go@23:16:23:19 | main.go:23:16:23:19 | bump | | types.go:3:6:3:17 | iHaveAMethod | types.go@3:6:3:17 | main.go:17:12:17:23 | iHaveAMethod | | types.go:3:6:3:17 | iHaveAMethod | types.go@3:6:3:17 | types.go:3:6:3:17 | iHaveAMethod | +| types.go:3:6:3:17 | iHaveAMethod | types.go@3:6:3:17 | types.go:42:2:42:13 | iHaveAMethod | | types.go:4:2:4:5 | meth | types.go@4:2:4:5 | main.go:18:2:18:7 | selection of meth | | types.go:4:2:4:5 | meth | types.go@4:2:4:5 | main.go:18:4:18:7 | meth | | types.go:4:2:4:5 | meth | types.go@4:2:4:5 | types.go:4:2:4:5 | meth | @@ -65,3 +67,5 @@ | types.go:33:22:33:22 | a | types.go@33:22:33:22 | types.go:33:22:33:22 | a | | types.go:33:22:33:22 | a | types.go@33:22:33:22 | types.go:34:9:34:9 | a | | types.go:37:16:37:20 | meth2 | types.go@37:16:37:20 | types.go:37:16:37:20 | meth2 | +| types.go:41:6:41:27 | iHaveARedeclaredMethod | types.go@41:6:41:27 | types.go:41:6:41:27 | iHaveARedeclaredMethod | +| types.go:43:2:43:5 | meth | types.go@43:2:43:5 | types.go:43:2:43:5 | meth | diff --git a/ql/test/library-tests/semmle/go/Scopes/EntityType.expected b/ql/test/library-tests/semmle/go/Scopes/EntityType.expected index 8c7eac514b1..d61da59604e 100644 --- a/ql/test/library-tests/semmle/go/Scopes/EntityType.expected +++ b/ql/test/library-tests/semmle/go/Scopes/EntityType.expected @@ -23,3 +23,5 @@ | types.go:33:16:33:20 | meth1 | func(int) bool | | types.go:33:22:33:22 | a | int | | types.go:37:16:37:20 | meth2 | func() int | +| types.go:41:6:41:27 | iHaveARedeclaredMethod | iHaveARedeclaredMethod | +| types.go:43:2:43:5 | meth | func() int | diff --git a/ql/test/library-tests/semmle/go/Scopes/MethodImplements.expected b/ql/test/library-tests/semmle/go/Scopes/MethodImplements.expected index b23488d0571..8edc2f2a211 100644 --- a/ql/test/library-tests/semmle/go/Scopes/MethodImplements.expected +++ b/ql/test/library-tests/semmle/go/Scopes/MethodImplements.expected @@ -1,10 +1,16 @@ +| * starImpl | meth1 | * starImpl | meth1 | +| * starImpl | meth1 | meth1Iface | meth1 | +| * starImpl | meth1 | twoMethods | meth1 | +| * t | bump | * t | bump | +| * t | meth | * t | meth | +| * t | meth | iHaveAMethod | meth | +| * t | meth | iHaveARedeclaredMethod | meth | | iHaveAMethod | meth | iHaveAMethod | meth | +| iHaveARedeclaredMethod | meth | iHaveARedeclaredMethod | meth | | meth1Iface | meth1 | meth1Iface | meth1 | -| meth1Iface | meth1 | twoMethods | meth1 | -| pointer type | meth | iHaveAMethod | meth | -| pointer type | meth1 | meth1Iface | meth1 | -| pointer type | meth1 | twoMethods | meth1 | +| notImpl | meth1 | notImpl | meth1 | +| notImpl | meth2 | notImpl | meth2 | +| starImpl | meth2 | starImpl | meth2 | | starImpl | meth2 | twoMethods | meth2 | -| twoMethods | meth1 | meth1Iface | meth1 | | twoMethods | meth1 | twoMethods | meth1 | | twoMethods | meth2 | twoMethods | meth2 | diff --git a/ql/test/library-tests/semmle/go/Scopes/MethodImplements.ql b/ql/test/library-tests/semmle/go/Scopes/MethodImplements.ql index 7def17705e0..eefc289dde5 100644 --- a/ql/test/library-tests/semmle/go/Scopes/MethodImplements.ql +++ b/ql/test/library-tests/semmle/go/Scopes/MethodImplements.ql @@ -2,4 +2,4 @@ import go from Method m, Method im where m.implements(im) and m.getPackage().getName() = "main" -select m.getReceiverType(), m.getName(), im.getReceiverType(), im.getName() +select m.getReceiverType().pp(), m.getName(), im.getReceiverType().pp(), im.getName() diff --git a/ql/test/library-tests/semmle/go/Scopes/MethodImplementsName.expected b/ql/test/library-tests/semmle/go/Scopes/MethodImplementsName.expected index 2d5931a28a0..021111a44f3 100644 --- a/ql/test/library-tests/semmle/go/Scopes/MethodImplementsName.expected +++ b/ql/test/library-tests/semmle/go/Scopes/MethodImplementsName.expected @@ -1,15 +1,20 @@ +| * starImpl | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | meth1Iface | meth1 | +| * starImpl | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | starImpl | meth1 | +| * starImpl | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethods | meth1 | +| * starImpl | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethodsEmbedded | meth1 | +| * t | bump | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | t | bump | +| * t | meth | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | iHaveAMethod | meth | +| * t | meth | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | iHaveARedeclaredMethod | meth | +| * t | meth | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | t | meth | | iHaveAMethod | meth | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | iHaveAMethod | meth | +| iHaveARedeclaredMethod | meth | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | iHaveARedeclaredMethod | meth | | meth1Iface | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | meth1Iface | meth1 | -| meth1Iface | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethods | meth1 | | meth1Iface | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethodsEmbedded | meth1 | -| pointer type | meth | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | iHaveAMethod | meth | -| pointer type | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | meth1Iface | meth1 | -| pointer type | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethods | meth1 | -| pointer type | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethodsEmbedded | meth1 | +| notImpl | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | notImpl | meth1 | +| notImpl | meth2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | notImpl | meth2 | +| starImpl | meth2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | starImpl | meth2 | | starImpl | meth2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethods | meth2 | | starImpl | meth2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethodsEmbedded | meth2 | -| twoMethods | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | meth1Iface | meth1 | | twoMethods | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethods | meth1 | -| twoMethods | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethodsEmbedded | meth1 | | twoMethods | meth2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethods | meth2 | | twoMethods | meth2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes | twoMethodsEmbedded | meth2 | diff --git a/ql/test/library-tests/semmle/go/Scopes/MethodImplementsName.ql b/ql/test/library-tests/semmle/go/Scopes/MethodImplementsName.ql index 6b688522c2d..d8ad9ee4122 100644 --- a/ql/test/library-tests/semmle/go/Scopes/MethodImplementsName.ql +++ b/ql/test/library-tests/semmle/go/Scopes/MethodImplementsName.ql @@ -4,4 +4,4 @@ from Method m, string pkg, string tp, string name where m.implements(pkg, tp, name) and m.hasQualifiedName("github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes", _, _) -select m.getReceiverType(), m.getName(), pkg, tp, name +select m.getReceiverType().pp(), m.getName(), pkg, tp, name diff --git a/ql/test/library-tests/semmle/go/Scopes/Methods.expected b/ql/test/library-tests/semmle/go/Scopes/Methods.expected index 5afe9843562..215d6308509 100644 --- a/ql/test/library-tests/semmle/go/Scopes/Methods.expected +++ b/ql/test/library-tests/semmle/go/Scopes/Methods.expected @@ -10,3 +10,4 @@ | types.go:27:17:27:21 | meth2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes.starImpl.meth2 | file://:0:0:0:0 | | starImpl | | types.go:33:16:33:20 | meth1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes.notImpl.meth1 | file://:0:0:0:0 | | notImpl | | types.go:37:16:37:20 | meth2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes.notImpl.meth2 | file://:0:0:0:0 | | notImpl | +| types.go:43:2:43:5 | meth | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Scopes.iHaveARedeclaredMethod.meth | file://:0:0:0:0 | | iHaveARedeclaredMethod | diff --git a/ql/test/library-tests/semmle/go/Scopes/TypeImplements.expected b/ql/test/library-tests/semmle/go/Scopes/TypeImplements.expected index d58feba2c03..d6e1ee64252 100644 --- a/ql/test/library-tests/semmle/go/Scopes/TypeImplements.expected +++ b/ql/test/library-tests/semmle/go/Scopes/TypeImplements.expected @@ -2,12 +2,17 @@ | * starImpl | twoMethods | | * starImpl | twoMethodsEmbedded | | * t | iHaveAMethod | +| * t | iHaveARedeclaredMethod | | iHaveAMethod | iHaveAMethod | +| iHaveAMethod | iHaveARedeclaredMethod | +| iHaveARedeclaredMethod | iHaveAMethod | +| iHaveARedeclaredMethod | iHaveARedeclaredMethod | | interface { meth1 func() bool } | meth1Iface | | interface { meth1 func() bool; meth2 func() int } | meth1Iface | | interface { meth1 func() bool; meth2 func() int } | twoMethods | | interface { meth1 func() bool; meth2 func() int } | twoMethodsEmbedded | | interface { meth func() int } | iHaveAMethod | +| interface { meth func() int } | iHaveARedeclaredMethod | | meth1Iface | meth1Iface | | twoMethods | meth1Iface | | twoMethods | twoMethods | diff --git a/ql/test/library-tests/semmle/go/Scopes/types.go b/ql/test/library-tests/semmle/go/Scopes/types.go index 5486d0faa74..49f640da6bb 100644 --- a/ql/test/library-tests/semmle/go/Scopes/types.go +++ b/ql/test/library-tests/semmle/go/Scopes/types.go @@ -37,3 +37,8 @@ func (notImpl) meth1(a int) bool { func (notImpl) meth2() int { return -42 } + +type iHaveARedeclaredMethod interface { + iHaveAMethod + meth() int +} diff --git a/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected index 48855348e90..cc10eecd92b 100644 --- a/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected +++ b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected @@ -8,4 +8,4 @@ | pkg1/tst.go:33:1:35:1 | function declaration | 0 | | pkg1/tst.go:37:1:37:26 | function declaration | 1 | | pkg1/tst.go:39:1:57:1 | function declaration | 2 | -| unknownFunction.go:8:1:12:1 | function declaration | 0 | +| unknownFunction.go:10:1:14:1 | function declaration | 0 | diff --git a/ql/test/library-tests/semmle/go/Types/SignatureType_getNumResult.expected b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumResult.expected index a90412830d7..4a50841a812 100644 --- a/ql/test/library-tests/semmle/go/Types/SignatureType_getNumResult.expected +++ b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumResult.expected @@ -8,4 +8,4 @@ | pkg1/tst.go:33:1:35:1 | function declaration | 1 | | pkg1/tst.go:37:1:37:26 | function declaration | 0 | | pkg1/tst.go:39:1:57:1 | function declaration | 0 | -| unknownFunction.go:8:1:12:1 | function declaration | 0 | +| unknownFunction.go:10:1:14:1 | function declaration | 0 | diff --git a/ql/test/library-tests/semmle/go/Types/notype.expected b/ql/test/library-tests/semmle/go/Types/notype.expected index 669165864d4..ded6be7bc59 100644 --- a/ql/test/library-tests/semmle/go/Types/notype.expected +++ b/ql/test/library-tests/semmle/go/Types/notype.expected @@ -1,3 +1,3 @@ -| unknownFunction.go:9:7:9:21 | unknownFunction | invalid type | -| unknownFunction.go:9:7:9:23 | call to unknownFunction | invalid type | -| unknownFunction.go:10:7:10:15 | ...+... | invalid type | +| unknownFunction.go:11:7:11:21 | unknownFunction | invalid type | +| unknownFunction.go:11:7:11:23 | call to unknownFunction | invalid type | +| unknownFunction.go:12:7:12:15 | ...+... | invalid type | diff --git a/ql/test/library-tests/semmle/go/Types/unknownFunction.go b/ql/test/library-tests/semmle/go/Types/unknownFunction.go index 5af16d4b13f..1eda1dba942 100644 --- a/ql/test/library-tests/semmle/go/Types/unknownFunction.go +++ b/ql/test/library-tests/semmle/go/Types/unknownFunction.go @@ -1,7 +1,9 @@ package main // This file tests type inference for expressions referencing undeclared entities. -// It is therefore expected to produce extractor warnings. +// It is therefore expected to expected frontend errors. + +// codeql test: expect frontend errors import "fmt" diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getEntryNode.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getEntryNode.expected index 316a5f82f61..540156fb0fc 100644 --- a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getEntryNode.expected +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getEntryNode.expected @@ -2,6 +2,8 @@ | parameter 0 | main.go:53:2:53:22 | call to op2 | main.go:53:6:53:8 | "-" | | parameter 0 | main.go:55:2:55:27 | call to Printf | main.go:55:13:55:20 | "%d, %d" | | parameter 0 | main.go:57:2:57:27 | call to Printf | main.go:57:13:57:20 | "%d, %d" | +| parameter 0 | reset.go:12:2:12:21 | call to Reset | reset.go:12:15:12:20 | source | +| parameter 0 | tst.go:10:2:10:29 | call to ReadFrom | tst.go:10:23:10:28 | reader | | parameter 1 | main.go:51:2:51:14 | call to op | main.go:51:10:51:10 | 1 | | parameter 1 | main.go:53:2:53:22 | call to op2 | main.go:53:11:53:11 | 2 | | parameter 1 | main.go:55:2:55:27 | call to Printf | main.go:55:23:55:23 | x | @@ -11,3 +13,7 @@ | parameter 2 | main.go:55:2:55:27 | call to Printf | main.go:55:26:55:26 | y | | parameter 2 | main.go:57:2:57:27 | call to Printf | main.go:57:26:57:26 | y | | receiver | main.go:53:14:53:21 | call to bump | main.go:53:14:53:14 | c | +| receiver | reset.go:12:2:12:21 | call to Reset | reset.go:12:2:12:7 | reader | +| receiver | tst.go:10:2:10:29 | call to ReadFrom | tst.go:10:2:10:12 | bytesBuffer | +| result | tst.go:9:17:9:33 | call to new | tst.go:9:2:9:12 | definition of bytesBuffer | +| result 0 | tst.go:9:17:9:33 | call to new | tst.go:9:2:9:12 | definition of bytesBuffer | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected index 92e681a7b01..3f4fdfccbc2 100644 --- a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected @@ -1,6 +1,8 @@ | parameter 0 | main.go:5:1:11:1 | function declaration | main.go:5:9:5:10 | definition of op | | parameter 0 | main.go:13:1:20:1 | function declaration | main.go:13:10:13:11 | definition of op | | parameter 0 | main.go:40:1:48:1 | function declaration | main.go:40:12:40:12 | definition of b | +| parameter 0 | reset.go:8:1:16:1 | function declaration | reset.go:8:27:8:27 | definition of r | +| parameter 0 | tst.go:8:1:11:1 | function declaration | tst.go:8:12:8:17 | definition of reader | | parameter 1 | main.go:5:1:11:1 | function declaration | main.go:5:20:5:20 | definition of x | | parameter 1 | main.go:13:1:20:1 | function declaration | main.go:13:21:13:21 | definition of x | | parameter 2 | main.go:5:1:11:1 | function declaration | main.go:5:27:5:27 | definition of y | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.expected new file mode 100644 index 00000000000..be1c46b3437 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.expected @@ -0,0 +1,2 @@ +| file://:0:0:0:0 | ReadFrom | tst.go:10:23:10:28 | reader | tst.go:9:2:9:12 | definition of bytesBuffer | +| file://:0:0:0:0 | Reset | reset.go:12:15:12:20 | source | reset.go:11:6:11:11 | definition of reader | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.ql b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.ql new file mode 100644 index 00000000000..276239350d7 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionModelStep.ql @@ -0,0 +1,21 @@ +import go + +class BytesReadFrom extends TaintTracking::FunctionModel, Method { + BytesReadFrom() { this.hasQualifiedName("bytes", "Buffer", "ReadFrom") } + + override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { + inp.isParameter(0) and outp.isReceiver() + } +} + +class ReaderReset extends TaintTracking::FunctionModel, Method { + ReaderReset() { this.hasQualifiedName("bufio", "Reader", "Reset") } + + override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { + inp.isParameter(0) and outp.isReceiver() + } +} + +from Function fn, DataFlow::Node pred, DataFlow::Node succ +where TaintTracking::functionModelStep(fn, pred, succ) +select fn, pred, succ diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getEntryNode.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getEntryNode.expected index a9bde481fa3..1745841994e 100644 --- a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getEntryNode.expected +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getEntryNode.expected @@ -4,6 +4,7 @@ | result | main.go:13:1:20:1 | function declaration | main.go:15:9:15:13 | ...+... | | result | main.go:13:1:20:1 | function declaration | main.go:17:10:17:14 | ...-... | | result | main.go:26:1:29:1 | function declaration | main.go:28:9:28:15 | selection of count | +| result | reset.go:8:1:16:1 | function declaration | reset.go:15:9:15:12 | sink | | result 0 | main.go:31:1:33:1 | function declaration | main.go:32:9:32:10 | 23 | | result 0 | main.go:35:1:38:1 | function declaration | main.go:35:15:35:15 | zero value for x | | result 0 | main.go:35:1:38:1 | function declaration | main.go:36:13:36:14 | 23 | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getExitNode.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getExitNode.expected index 19c2533a831..e116ab151ef 100644 --- a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getExitNode.expected +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_getExitNode.expected @@ -1,7 +1,17 @@ +| parameter 0 | reset.go:12:2:12:21 | call to Reset | reset.go:9:2:9:7 | definition of source | +| parameter 0 | tst.go:10:2:10:29 | call to ReadFrom | tst.go:8:12:8:17 | definition of reader | +| receiver | main.go:53:14:53:21 | call to bump | main.go:52:2:52:2 | definition of c | +| receiver | reset.go:12:2:12:21 | call to Reset | reset.go:11:6:11:11 | definition of reader | +| receiver | tst.go:10:2:10:29 | call to ReadFrom | tst.go:9:2:9:12 | definition of bytesBuffer | | result | main.go:51:2:51:14 | call to op | main.go:51:2:51:14 | call to op | | result | main.go:53:2:53:22 | call to op2 | main.go:53:2:53:22 | call to op2 | | result | main.go:53:14:53:21 | call to bump | main.go:53:14:53:21 | call to bump | +| result | tst.go:9:17:9:33 | call to new | tst.go:9:17:9:33 | call to new | +| result 0 | main.go:51:2:51:14 | call to op | main.go:51:2:51:14 | call to op | +| result 0 | main.go:53:2:53:22 | call to op2 | main.go:53:2:53:22 | call to op2 | +| result 0 | main.go:53:14:53:21 | call to bump | main.go:53:14:53:21 | call to bump | | result 0 | main.go:54:10:54:15 | call to test | main.go:54:2:54:15 | ... := ...[0] | | result 0 | main.go:56:9:56:15 | call to test2 | main.go:56:2:56:15 | ... = ...[0] | +| result 0 | tst.go:9:17:9:33 | call to new | tst.go:9:17:9:33 | call to new | | result 1 | main.go:54:10:54:15 | call to test | main.go:54:2:54:15 | ... := ...[1] | | result 1 | main.go:56:9:56:15 | call to test2 | main.go:56:2:56:15 | ... = ...[1] | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult.expected new file mode 100644 index 00000000000..7f903e4efed --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult.expected @@ -0,0 +1,4 @@ +| main.go:51:2:51:14 | call to op | main.go:51:2:51:14 | call to op | result | +| main.go:53:2:53:22 | call to op2 | main.go:53:2:53:22 | call to op2 | result | +| main.go:53:14:53:21 | call to bump | main.go:53:14:53:21 | call to bump | result | +| tst.go:9:17:9:33 | call to new | tst.go:9:17:9:33 | call to new | result | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult.ql b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult.ql new file mode 100644 index 00000000000..c39c80da64e --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult.ql @@ -0,0 +1,5 @@ +import go + +from FunctionOutput outp, DataFlow::CallNode c, DataFlow::Node nodeTo +where outp.isResult() and nodeTo = outp.getNode(c) +select c, nodeTo, outp diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult_int.expected b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult_int.expected new file mode 100644 index 00000000000..06c5efaea97 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult_int.expected @@ -0,0 +1,12 @@ +| main.go:51:2:51:14 | call to op | main.go:51:2:51:14 | call to op | 0 | result | +| main.go:51:2:51:14 | call to op | main.go:51:2:51:14 | call to op | 0 | result 0 | +| main.go:53:2:53:22 | call to op2 | main.go:53:2:53:22 | call to op2 | 0 | result | +| main.go:53:2:53:22 | call to op2 | main.go:53:2:53:22 | call to op2 | 0 | result 0 | +| main.go:53:14:53:21 | call to bump | main.go:53:14:53:21 | call to bump | 0 | result | +| main.go:53:14:53:21 | call to bump | main.go:53:14:53:21 | call to bump | 0 | result 0 | +| main.go:54:10:54:15 | call to test | main.go:54:2:54:15 | ... := ...[0] | 0 | result 0 | +| main.go:54:10:54:15 | call to test | main.go:54:2:54:15 | ... := ...[1] | 1 | result 1 | +| main.go:56:9:56:15 | call to test2 | main.go:56:2:56:15 | ... = ...[0] | 0 | result 0 | +| main.go:56:9:56:15 | call to test2 | main.go:56:2:56:15 | ... = ...[1] | 1 | result 1 | +| tst.go:9:17:9:33 | call to new | tst.go:9:17:9:33 | call to new | 0 | result | +| tst.go:9:17:9:33 | call to new | tst.go:9:17:9:33 | call to new | 0 | result 0 | diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult_int.ql b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult_int.ql new file mode 100644 index 00000000000..8d5e142cfbb --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionOutput_isResult_int.ql @@ -0,0 +1,5 @@ +import go + +from FunctionOutput outp, int i, DataFlow::CallNode c, DataFlow::Node nodeTo +where outp.isResult(i) and nodeTo = outp.getNode(c) +select c, nodeTo, i, outp diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/reset.go b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/reset.go new file mode 100644 index 00000000000..d135d77d51a --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/reset.go @@ -0,0 +1,16 @@ +package main + +import ( + "bufio" + "io" +) + +func bufioReaderResetTest(r io.Reader) bufio.Reader { + source := r + + var reader bufio.Reader + reader.Reset(source) + sink := reader + + return sink +} diff --git a/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/tst.go b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/tst.go new file mode 100644 index 00000000000..e36e3cf15d2 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/tst.go @@ -0,0 +1,11 @@ +package main + +import ( + "bytes" + "io" +) + +func test4(reader io.Reader) { + bytesBuffer := new(bytes.Buffer) + bytesBuffer.ReadFrom(reader) +} diff --git a/ql/test/library-tests/semmle/go/dataflow/Nodes/BinaryOperationNodes.expected b/ql/test/library-tests/semmle/go/dataflow/Nodes/BinaryOperationNodes.expected index dbc7327f186..9d996bdd020 100644 --- a/ql/test/library-tests/semmle/go/dataflow/Nodes/BinaryOperationNodes.expected +++ b/ql/test/library-tests/semmle/go/dataflow/Nodes/BinaryOperationNodes.expected @@ -1,3 +1,4 @@ | main.go:7:14:7:24 | ...+... | + | main.go:7:14:7:14 | x | main.go:7:19:7:23 | ...+... | | main.go:7:19:7:23 | ...+... | + | main.go:7:19:7:19 | y | main.go:7:23:7:23 | z | -| main.go:15:2:15:13 | ... += ... | + | main.go:15:2:15:6 | index expression | main.go:15:11:15:13 | "!" | +| main.go:10:14:10:18 | ...+... | + | main.go:10:14:10:14 | x | main.go:10:18:10:18 | y | +| main.go:17:2:17:13 | ... += ... | + | main.go:17:2:17:6 | index expression | main.go:17:11:17:13 | "!" | diff --git a/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode.expected b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode.expected index 5a3836d14a5..cbd18610584 100644 --- a/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode.expected +++ b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode.expected @@ -1,4 +1,6 @@ | main.go:7:2:7:25 | call to Println | | main.go:8:5:8:7 | call to f | -| main.go:12:8:12:24 | call to make | -| main.go:14:2:14:26 | call to Println | +| main.go:9:9:9:14 | call to test | +| main.go:10:2:10:19 | call to Println | +| main.go:14:8:14:24 | call to make | +| main.go:16:2:16:26 | call to Println | diff --git a/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getArgument.expected b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getArgument.expected index 9d2c1e18ecc..fc391bafcff 100644 --- a/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getArgument.expected +++ b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getArgument.expected @@ -1,5 +1,6 @@ | main.go:7:2:7:25 | call to Println | 0 | main.go:7:14:7:24 | ...+... | -| main.go:12:8:12:24 | call to make | 0 | main.go:12:23:12:23 | 1 | -| main.go:14:2:14:26 | call to Println | 0 | main.go:14:14:14:15 | ss | -| main.go:14:2:14:26 | call to Println | 1 | main.go:14:18:14:18 | 0 | -| main.go:14:2:14:26 | call to Println | 2 | main.go:14:21:14:25 | index expression | +| main.go:10:2:10:19 | call to Println | 0 | main.go:10:14:10:18 | ...+... | +| main.go:14:8:14:24 | call to make | 0 | main.go:14:23:14:23 | 1 | +| main.go:16:2:16:26 | call to Println | 0 | main.go:16:14:16:15 | ss | +| main.go:16:2:16:26 | call to Println | 1 | main.go:16:18:16:18 | 0 | +| main.go:16:2:16:26 | call to Println | 2 | main.go:16:21:16:25 | index expression | diff --git a/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult.expected b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult.expected new file mode 100644 index 00000000000..d9aa7ade978 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult.expected @@ -0,0 +1 @@ +| main.go:14:8:14:24 | call to make | main.go:14:8:14:24 | call to make | diff --git a/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult.ql b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult.ql new file mode 100644 index 00000000000..5d6c0ff9043 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult.ql @@ -0,0 +1,5 @@ +import go + +from DataFlow::CallNode c, DataFlow::Node outp +where outp = c.getResult() +select c, outp diff --git a/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult_int.expected b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult_int.expected new file mode 100644 index 00000000000..6c9465eeaf4 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult_int.expected @@ -0,0 +1,3 @@ +| main.go:9:9:9:14 | call to test | 0 | main.go:9:2:9:14 | ... = ...[0] | +| main.go:9:9:9:14 | call to test | 1 | main.go:9:2:9:14 | ... = ...[1] | +| main.go:14:8:14:24 | call to make | 0 | main.go:14:8:14:24 | call to make | diff --git a/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult_int.ql b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult_int.ql new file mode 100644 index 00000000000..b0124d3c56b --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/Nodes/CallNode_getResult_int.ql @@ -0,0 +1,5 @@ +import go + +from DataFlow::CallNode c, int i, DataFlow::Node outp +where outp = c.getResult(i) +select c, i, outp diff --git a/ql/test/library-tests/semmle/go/dataflow/Nodes/main.go b/ql/test/library-tests/semmle/go/dataflow/Nodes/main.go index f576f2cd3f3..1fb3466820c 100644 --- a/ql/test/library-tests/semmle/go/dataflow/Nodes/main.go +++ b/ql/test/library-tests/semmle/go/dataflow/Nodes/main.go @@ -6,6 +6,8 @@ func main() { x, y, z := 1, 2, 3 fmt.Println(x + (y + z)) go f() + x, y = test() + fmt.Println(x + y) } func f() { @@ -14,3 +16,7 @@ func f() { fmt.Println(ss, 0, ss[0]) ss[0] += "!" } + +func test() (int, int) { + return 23, 42 +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/EmailData.expected b/ql/test/library-tests/semmle/go/frameworks/Email/EmailData.expected new file mode 100644 index 00000000000..99b33b4a780 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/EmailData.expected @@ -0,0 +1,9 @@ +| mail.go:15:73:15:94 | type conversion | +| mail.go:18:19:18:23 | definition of write | +| mail.go:26:49:26:52 | text | +| mail.go:26:76:26:79 | text | +| mail.go:27:20:27:23 | text | +| mail.go:31:33:31:39 | content | +| mail.go:36:52:36:55 | text | +| mail.go:36:79:36:86 | content2 | +| mail.go:37:20:37:27 | content3 | diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/EmailData.ql b/ql/test/library-tests/semmle/go/frameworks/Email/EmailData.ql new file mode 100644 index 00000000000..c67ce8753e1 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/EmailData.ql @@ -0,0 +1,4 @@ +import go + +from EmailData f +select f diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/go.mod b/ql/test/library-tests/semmle/go/frameworks/Email/go.mod new file mode 100644 index 00000000000..eb193e9905e --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/go.mod @@ -0,0 +1,5 @@ +module main + +go 1.14 + +require github.com/sendgrid/sendgrid-go v3.5.0+incompatible diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/mail.go b/ql/test/library-tests/semmle/go/frameworks/Email/mail.go new file mode 100644 index 00000000000..fe5565c2305 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/mail.go @@ -0,0 +1,38 @@ +package main + +//go:generate depstubber -vendor github.com/sendgrid/sendgrid-go/helpers/mail "" NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit + +import ( + "io" + "net/smtp" + + sendgrid "github.com/sendgrid/sendgrid-go/helpers/mail" +) + +func main() { + untrustedInput := "test" + + smtp.SendMail("test.test", nil, "from@from.com", nil /* email data */, []byte(untrustedInput)) + + s, _ := smtp.Dial("test.test") + /* email data */ write, _ := s.Data() + + io.WriteString(write, untrustedInput) + + from := sendgrid.NewEmail("from", "from@from.com") + to := sendgrid.NewEmail("to", "to@to.com") + text := "sub" + + sendgrid.NewSingleEmail(from /* email data */, text, to /* email data */, text, + /* email data */ text) + + content := sendgrid.NewContent("text/html", text) + v := sendgrid.NewV3Mail() + v.AddContent( /* email data */ content) + + content2 := sendgrid.NewContent("text/html", text) + content3 := sendgrid.NewContent("text/html", text) + + v = sendgrid.NewV3MailInit(from /* email data */, text, to /* email data */, content2, + /* email data */ content3) +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/LICENSE b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/LICENSE new file mode 100644 index 00000000000..e5441aa6a0a --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2019 Twilio SendGrid, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go new file mode 100644 index 00000000000..cd1956cecc0 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go @@ -0,0 +1,391 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/sendgrid/sendgrid-go/helpers/mail, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/sendgrid/sendgrid-go/helpers/mail (exports: ; functions: NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit) + +// Package mail is a stub of github.com/sendgrid/sendgrid-go/helpers/mail, generated by depstubber. +package mail + +import () + +type Asm struct { + GroupID int + GroupsToDisplay []int +} + +func (_ *Asm) AddGroupsToDisplay(_ ...int) *Asm { + return nil +} + +func (_ *Asm) SetGroupID(_ int) *Asm { + return nil +} + +type Attachment struct { + Content string + Type string + Name string + Filename string + Disposition string + ContentID string +} + +func (_ *Attachment) SetContent(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetContentID(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetDisposition(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetFilename(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetType(_ string) *Attachment { + return nil +} + +type BccSetting struct { + Enable *bool + Email string +} + +func (_ *BccSetting) SetEmail(_ string) *BccSetting { + return nil +} + +func (_ *BccSetting) SetEnable(_ bool) *BccSetting { + return nil +} + +type ClickTrackingSetting struct { + Enable *bool + EnableText *bool +} + +func (_ *ClickTrackingSetting) SetEnable(_ bool) *ClickTrackingSetting { + return nil +} + +func (_ *ClickTrackingSetting) SetEnableText(_ bool) *ClickTrackingSetting { + return nil +} + +type Content struct { + Type string + Value string +} + +type Email struct { + Name string + Address string +} + +type FooterSetting struct { + Enable *bool + Text string + Html string +} + +func (_ *FooterSetting) SetEnable(_ bool) *FooterSetting { + return nil +} + +func (_ *FooterSetting) SetHTML(_ string) *FooterSetting { + return nil +} + +func (_ *FooterSetting) SetText(_ string) *FooterSetting { + return nil +} + +type GaSetting struct { + Enable *bool + CampaignSource string + CampaignTerm string + CampaignContent string + CampaignName string + CampaignMedium string +} + +func (_ *GaSetting) SetCampaignContent(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignMedium(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignName(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignSource(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignTerm(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetEnable(_ bool) *GaSetting { + return nil +} + +type MailSettings struct { + BCC *BccSetting + BypassListManagement *Setting + Footer *FooterSetting + SandboxMode *Setting + SpamCheckSetting *SpamCheckSetting +} + +func (_ *MailSettings) SetBCC(_ *BccSetting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetBypassListManagement(_ *Setting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetFooter(_ *FooterSetting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetSandboxMode(_ *Setting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetSpamCheckSettings(_ *SpamCheckSetting) *MailSettings { + return nil +} + +func NewContent(_ string, _ string) *Content { + return nil +} + +func NewEmail(_ string, _ string) *Email { + return nil +} + +func NewSingleEmail(_ *Email, _ string, _ *Email, _ string, _ string) *SGMailV3 { + return nil +} + +func NewV3Mail() *SGMailV3 { + return nil +} + +func NewV3MailInit(_ *Email, _ string, _ *Email, _ ...*Content) *SGMailV3 { + return nil +} + +type OpenTrackingSetting struct { + Enable *bool + SubstitutionTag string +} + +func (_ *OpenTrackingSetting) SetEnable(_ bool) *OpenTrackingSetting { + return nil +} + +func (_ *OpenTrackingSetting) SetSubstitutionTag(_ string) *OpenTrackingSetting { + return nil +} + +type Personalization struct { + To []*Email + CC []*Email + BCC []*Email + Subject string + Headers map[string]string + Substitutions map[string]string + CustomArgs map[string]string + DynamicTemplateData map[string]interface{} + Categories []string + SendAt int +} + +func (_ *Personalization) AddBCCs(_ ...*Email) {} + +func (_ *Personalization) AddCCs(_ ...*Email) {} + +func (_ *Personalization) AddTos(_ ...*Email) {} + +func (_ *Personalization) SetCustomArg(_ string, _ string) {} + +func (_ *Personalization) SetDynamicTemplateData(_ string, _ interface{}) {} + +func (_ *Personalization) SetHeader(_ string, _ string) {} + +func (_ *Personalization) SetSendAt(_ int) {} + +func (_ *Personalization) SetSubstitution(_ string, _ string) {} + +type SGMailV3 struct { + From *Email + Subject string + Personalizations []*Personalization + Content []*Content + Attachments []*Attachment + TemplateID string + Sections map[string]string + Headers map[string]string + Categories []string + CustomArgs map[string]string + SendAt int + BatchID string + Asm *Asm + IPPoolID string + MailSettings *MailSettings + TrackingSettings *TrackingSettings + ReplyTo *Email +} + +func (_ *SGMailV3) AddAttachment(_ ...*Attachment) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddCategories(_ ...string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddContent(_ ...*Content) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddPersonalizations(_ ...*Personalization) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddSection(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetASM(_ *Asm) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetBatchID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetCustomArg(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetFrom(_ *Email) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetHeader(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetIPPoolID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetMailSettings(_ *MailSettings) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetReplyTo(_ *Email) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetSendAt(_ int) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetTemplateID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetTrackingSettings(_ *TrackingSettings) *SGMailV3 { + return nil +} + +type SandboxModeSetting struct { + Enable *bool + ForwardSpam *bool + SpamCheck *SpamCheckSetting +} + +type Setting struct { + Enable *bool +} + +type SpamCheckSetting struct { + Enable *bool + SpamThreshold int + PostToURL string +} + +func (_ *SpamCheckSetting) SetEnable(_ bool) *SpamCheckSetting { + return nil +} + +func (_ *SpamCheckSetting) SetPostToURL(_ string) *SpamCheckSetting { + return nil +} + +func (_ *SpamCheckSetting) SetSpamThreshold(_ int) *SpamCheckSetting { + return nil +} + +type SubscriptionTrackingSetting struct { + Enable *bool + Text string + Html string + SubstitutionTag string +} + +func (_ *SubscriptionTrackingSetting) SetEnable(_ bool) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetHTML(_ string) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetSubstitutionTag(_ string) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetText(_ string) *SubscriptionTrackingSetting { + return nil +} + +type TrackingSettings struct { + ClickTracking *ClickTrackingSetting + OpenTracking *OpenTrackingSetting + SubscriptionTracking *SubscriptionTrackingSetting + GoogleAnalytics *GaSetting + BCC *BccSetting + BypassListManagement *Setting + Footer *FooterSetting + SandboxMode *SandboxModeSetting +} + +func (_ *TrackingSettings) SetClickTracking(_ *ClickTrackingSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetGoogleAnalytics(_ *GaSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetOpenTracking(_ *OpenTrackingSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetSubscriptionTracking(_ *SubscriptionTrackingSetting) *TrackingSettings { + return nil +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/modules.txt new file mode 100644 index 00000000000..4b7525957df --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/sendgrid/sendgrid-go v3.5.0+incompatible +## explicit +github.com/sendgrid/sendgrid-go diff --git a/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.expected b/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.expected new file mode 100644 index 00000000000..45fd77beda2 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.expected @@ -0,0 +1,2 @@ +| main.go:15:2:15:25 | call to Redirect | main.go:14:12:14:14 | ctx | +| main.go:19:2:19:25 | call to Redirect | main.go:18:13:18:15 | ctx | diff --git a/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.ql b/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.ql new file mode 100644 index 00000000000..813ad53f2e0 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.ql @@ -0,0 +1,4 @@ +import go + +from HTTP::Redirect redir +select redir, redir.getResponseWriter() diff --git a/ql/test/library-tests/semmle/go/frameworks/Macaron/go.mod b/ql/test/library-tests/semmle/go/frameworks/Macaron/go.mod new file mode 100644 index 00000000000..157e3b7b1c6 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Macaron/go.mod @@ -0,0 +1,5 @@ +module codeql-go-tests/frameworks/macaron + +go 1.14 + +require gopkg.in/macaron.v1 v1.3.5 diff --git a/ql/test/library-tests/semmle/go/frameworks/Macaron/main.go b/ql/test/library-tests/semmle/go/frameworks/Macaron/main.go new file mode 100644 index 00000000000..f47ec3c7074 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Macaron/main.go @@ -0,0 +1,23 @@ +package main + +//go:generate depstubber -vendor gopkg.in/macaron.v1 Context + +import ( + "gopkg.in/macaron.v1" +) + +type EmbeddedContext struct { + *macaron.Context + foo string +} + +func redir(ctx *macaron.Context) { + ctx.Redirect("/example") +} + +func redir1(ctx *EmbeddedContext) { + ctx.Redirect("/example") +} + +func main() { +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/gopkg.in/macaron.v1/LICENSE b/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/gopkg.in/macaron.v1/LICENSE new file mode 100644 index 00000000000..c8a16eb2eb9 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/gopkg.in/macaron.v1/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2014 The Macaron Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/gopkg.in/macaron.v1/stub.go b/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/gopkg.in/macaron.v1/stub.go new file mode 100644 index 00000000000..cac6e12385a --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/gopkg.in/macaron.v1/stub.go @@ -0,0 +1,580 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for gopkg.in/macaron.v1, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: gopkg.in/macaron.v1 (exports: Context; functions: ) + +// Package macaron is a stub of gopkg.in/macaron.v1, generated by depstubber. +package macaron + +import ( + context "context" + io "io" + multipart "mime/multipart" + http "net/http" + reflect "reflect" +) + +type BeforeFunc func(ResponseWriter) + +type ComboRouter struct{} + +func (_ *ComboRouter) Delete(_ ...Handler) *ComboRouter { + return nil +} + +func (_ *ComboRouter) Get(_ ...Handler) *ComboRouter { + return nil +} + +func (_ *ComboRouter) Head(_ ...Handler) *ComboRouter { + return nil +} + +func (_ *ComboRouter) Name(_ string) {} + +func (_ *ComboRouter) Options(_ ...Handler) *ComboRouter { + return nil +} + +func (_ *ComboRouter) Patch(_ ...Handler) *ComboRouter { + return nil +} + +func (_ *ComboRouter) Post(_ ...Handler) *ComboRouter { + return nil +} + +func (_ *ComboRouter) Put(_ ...Handler) *ComboRouter { + return nil +} + +type Context struct { + Injector interface{} + Router *Router + Req Request + Resp ResponseWriter + Render Render + Locale Locale + Data map[string]interface{} +} + +func (_ Context) Any(_ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) Apply(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ Context) Combo(_ string, _ ...Handler) *ComboRouter { + return nil +} + +func (_ Context) Delete(_ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) Error(_ int, _ ...string) {} + +func (_ Context) Get(_ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) GetVal(_ reflect.Type) reflect.Value { + return reflect.Value{} +} + +func (_ Context) Group(_ string, _ func(), _ ...Handler) {} + +func (_ Context) HTMLBytes(_ string, _ interface{}, _ ...HTMLOptions) ([]uint8, interface { + Error() string +}) { + return nil, nil +} + +func (_ Context) HTMLSetBytes(_ string, _ string, _ interface{}, _ ...HTMLOptions) ([]uint8, interface { + Error() string +}) { + return nil, nil +} + +func (_ Context) HTMLSetString(_ string, _ string, _ interface{}, _ ...HTMLOptions) (string, interface { + Error() string +}) { + return "", nil +} + +func (_ Context) HTMLString(_ string, _ interface{}, _ ...HTMLOptions) (string, interface { + Error() string +}) { + return "", nil +} + +func (_ Context) Handle(_ string, _ string, _ []Handler) *Route { + return nil +} + +func (_ Context) HasTemplateSet(_ string) bool { + return false +} + +func (_ Context) Head(_ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) Header() http.Header { + return nil +} + +func (_ Context) InternalServerError(_ ...Handler) {} + +func (_ Context) Invoke(_ interface{}) ([]reflect.Value, interface { + Error() string +}) { + return nil, nil +} + +func (_ Context) JSON(_ int, _ interface{}) {} + +func (_ Context) JSONString(_ interface{}) (string, interface { + Error() string +}) { + return "", nil +} + +func (_ Context) Language() string { + return "" +} + +func (_ Context) Map(_ interface{}) interface{} { + return nil +} + +func (_ Context) MapTo(_ interface{}, _ interface{}) interface{} { + return nil +} + +func (_ Context) NotFound(_ ...Handler) {} + +func (_ Context) Options(_ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) Patch(_ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) PlainText(_ int, _ []uint8) {} + +func (_ Context) Post(_ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) Put(_ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) RawData(_ int, _ []uint8) {} + +func (_ Context) Route(_ string, _ string, _ ...Handler) *Route { + return nil +} + +func (_ Context) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {} + +func (_ Context) Set(_ reflect.Type, _ reflect.Value) interface{} { + return nil +} + +func (_ Context) SetAutoHead(_ bool) {} + +func (_ Context) SetHandlerWrapper(_ func(Handler) Handler) {} + +func (_ Context) SetParent(_ interface{}) {} + +func (_ Context) SetResponseWriter(_ http.ResponseWriter) {} + +func (_ Context) SetTemplatePath(_ string, _ string) {} + +func (_ Context) Status(_ int) {} + +func (_ Context) Tr(_ string, _ ...interface{}) string { + return "" +} + +func (_ Context) URLFor(_ string, _ ...string) string { + return "" +} + +func (_ Context) Write(_ []uint8) (int, interface { + Error() string +}) { + return 0, nil +} + +func (_ Context) WriteHeader(_ int) {} + +func (_ Context) XML(_ int, _ interface{}) {} + +func (_ *Context) ChangeStaticPath(_ string, _ string) {} + +func (_ *Context) GetCookie(_ string) string { + return "" +} + +func (_ *Context) GetCookieFloat64(_ string) float64 { + return 0 +} + +func (_ *Context) GetCookieInt(_ string) int { + return 0 +} + +func (_ *Context) GetCookieInt64(_ string) int64 { + return 0 +} + +func (_ *Context) GetFile(_ string) (multipart.File, *multipart.FileHeader, interface { + Error() string +}) { + return nil, nil, nil +} + +func (_ *Context) GetSecureCookie(_ string) (string, bool) { + return "", false +} + +func (_ *Context) GetSuperSecureCookie(_ string, _ string) (string, bool) { + return "", false +} + +func (_ *Context) HTML(_ int, _ string, _ ...interface{}) {} + +func (_ *Context) HTMLSet(_ int, _ string, _ string, _ ...interface{}) {} + +func (_ *Context) Next() {} + +func (_ *Context) Params(_ string) string { + return "" +} + +func (_ *Context) ParamsEscape(_ string) string { + return "" +} + +func (_ *Context) ParamsFloat64(_ string) float64 { + return 0 +} + +func (_ *Context) ParamsInt(_ string) int { + return 0 +} + +func (_ *Context) ParamsInt64(_ string) int64 { + return 0 +} + +func (_ *Context) Query(_ string) string { + return "" +} + +func (_ *Context) QueryBool(_ string) bool { + return false +} + +func (_ *Context) QueryEscape(_ string) string { + return "" +} + +func (_ *Context) QueryFloat64(_ string) float64 { + return 0 +} + +func (_ *Context) QueryInt(_ string) int { + return 0 +} + +func (_ *Context) QueryInt64(_ string) int64 { + return 0 +} + +func (_ *Context) QueryStrings(_ string) []string { + return nil +} + +func (_ *Context) QueryTrim(_ string) string { + return "" +} + +func (_ *Context) Redirect(_ string, _ ...int) {} + +func (_ *Context) RemoteAddr() string { + return "" +} + +func (_ *Context) ReplaceAllParams(_ Params) {} + +func (_ *Context) SaveToFile(_ string, _ string) interface { + Error() string +} { + return nil +} + +func (_ *Context) ServeContent(_ string, _ io.ReadSeeker, _ ...interface{}) {} + +func (_ *Context) ServeFile(_ string, _ ...string) {} + +func (_ *Context) ServeFileContent(_ string, _ ...string) {} + +func (_ *Context) SetCookie(_ string, _ string, _ ...interface{}) {} + +func (_ *Context) SetParams(_ string, _ string) {} + +func (_ *Context) SetSecureCookie(_ string, _ string, _ ...interface{}) {} + +func (_ *Context) SetSuperSecureCookie(_ string, _ string, _ string, _ ...interface{}) {} + +func (_ *Context) Written() bool { + return false +} + +type HTMLOptions struct { + Layout string +} + +type Handler interface{} + +type Locale interface { + Language() string + Tr(_ string, _ ...interface{}) string +} + +type Params map[string]string + +type Render interface { + Error(_ int, _ ...string) + HTML(_ int, _ string, _ interface{}, _ ...HTMLOptions) + HTMLBytes(_ string, _ interface{}, _ ...HTMLOptions) ([]uint8, interface { + Error() string + }) + HTMLSet(_ int, _ string, _ string, _ interface{}, _ ...HTMLOptions) + HTMLSetBytes(_ string, _ string, _ interface{}, _ ...HTMLOptions) ([]uint8, interface { + Error() string + }) + HTMLSetString(_ string, _ string, _ interface{}, _ ...HTMLOptions) (string, interface { + Error() string + }) + HTMLString(_ string, _ interface{}, _ ...HTMLOptions) (string, interface { + Error() string + }) + HasTemplateSet(_ string) bool + Header() http.Header + JSON(_ int, _ interface{}) + JSONString(_ interface{}) (string, interface { + Error() string + }) + PlainText(_ int, _ []uint8) + RawData(_ int, _ []uint8) + SetResponseWriter(_ http.ResponseWriter) + SetTemplatePath(_ string, _ string) + Status(_ int) + Write(_ []uint8) (int, interface { + Error() string + }) + WriteHeader(_ int) + XML(_ int, _ interface{}) +} + +type Request struct { + Request *http.Request +} + +func (_ Request) AddCookie(_ *http.Cookie) {} + +func (_ Request) BasicAuth() (string, string, bool) { + return "", "", false +} + +func (_ Request) Clone(_ context.Context) *http.Request { + return nil +} + +func (_ Request) Context() context.Context { + return nil +} + +func (_ Request) Cookie(_ string) (*http.Cookie, interface { + Error() string +}) { + return nil, nil +} + +func (_ Request) Cookies() []*http.Cookie { + return nil +} + +func (_ Request) FormFile(_ string) (multipart.File, *multipart.FileHeader, interface { + Error() string +}) { + return nil, nil, nil +} + +func (_ Request) FormValue(_ string) string { + return "" +} + +func (_ Request) MultipartReader() (*multipart.Reader, interface { + Error() string +}) { + return nil, nil +} + +func (_ Request) ParseForm() interface { + Error() string +} { + return nil +} + +func (_ Request) ParseMultipartForm(_ int64) interface { + Error() string +} { + return nil +} + +func (_ Request) PostFormValue(_ string) string { + return "" +} + +func (_ Request) ProtoAtLeast(_ int, _ int) bool { + return false +} + +func (_ Request) Referer() string { + return "" +} + +func (_ Request) SetBasicAuth(_ string, _ string) {} + +func (_ Request) UserAgent() string { + return "" +} + +func (_ Request) WithContext(_ context.Context) *http.Request { + return nil +} + +func (_ Request) Write(_ io.Writer) interface { + Error() string +} { + return nil +} + +func (_ Request) WriteProxy(_ io.Writer) interface { + Error() string +} { + return nil +} + +func (_ *Request) Body() *RequestBody { + return nil +} + +type RequestBody struct{} + +func (_ *RequestBody) Bytes() ([]uint8, interface { + Error() string +}) { + return nil, nil +} + +func (_ *RequestBody) ReadCloser() io.ReadCloser { + return nil +} + +func (_ *RequestBody) String() (string, interface { + Error() string +}) { + return "", nil +} + +type ResponseWriter interface { + Before(_ BeforeFunc) + Flush() + Header() http.Header + Size() int + Status() int + Write(_ []uint8) (int, interface { + Error() string + }) + WriteHeader(_ int) + Written() bool +} + +type Route struct{} + +func (_ *Route) Name(_ string) {} + +type Router struct{} + +func (_ *Router) Any(_ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) Combo(_ string, _ ...Handler) *ComboRouter { + return nil +} + +func (_ *Router) Delete(_ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) Get(_ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) Group(_ string, _ func(), _ ...Handler) {} + +func (_ *Router) Handle(_ string, _ string, _ []Handler) *Route { + return nil +} + +func (_ *Router) Head(_ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) InternalServerError(_ ...Handler) {} + +func (_ *Router) NotFound(_ ...Handler) {} + +func (_ *Router) Options(_ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) Patch(_ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) Post(_ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) Put(_ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) Route(_ string, _ string, _ ...Handler) *Route { + return nil +} + +func (_ *Router) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {} + +func (_ *Router) SetAutoHead(_ bool) {} + +func (_ *Router) SetHandlerWrapper(_ func(Handler) Handler) {} + +func (_ *Router) URLFor(_ string, _ ...string) string { + return "" +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/modules.txt new file mode 100644 index 00000000000..58e7025ec1a --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Macaron/vendor/modules.txt @@ -0,0 +1,3 @@ +# gopkg.in/macaron.v1 v1.3.5 +## explicit +gopkg.in/macaron.v1 diff --git a/ql/test/library-tests/semmle/go/frameworks/Mux/UntrustedFlowSources.expected b/ql/test/library-tests/semmle/go/frameworks/Mux/UntrustedFlowSources.expected new file mode 100644 index 00000000000..84776eef575 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Mux/UntrustedFlowSources.expected @@ -0,0 +1,2 @@ +| mux.go:15:10:15:20 | call to Vars | +| mux.go:21:13:21:23 | call to Vars | diff --git a/ql/test/library-tests/semmle/go/frameworks/Mux/UntrustedFlowSources.ql b/ql/test/library-tests/semmle/go/frameworks/Mux/UntrustedFlowSources.ql new file mode 100644 index 00000000000..0715d64f8e2 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Mux/UntrustedFlowSources.ql @@ -0,0 +1,3 @@ +import go + +select any(UntrustedFlowSource ufs) diff --git a/ql/test/library-tests/semmle/go/frameworks/Mux/go.mod b/ql/test/library-tests/semmle/go/frameworks/Mux/go.mod new file mode 100644 index 00000000000..c173488c7c7 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Mux/go.mod @@ -0,0 +1,5 @@ +module codeql-go-tests/frameworks/Mux + +go 1.14 + +require github.com/gorilla/mux v1.7.4 diff --git a/ql/test/library-tests/semmle/go/frameworks/Mux/mux.go b/ql/test/library-tests/semmle/go/frameworks/Mux/mux.go new file mode 100644 index 00000000000..e12ce28daa3 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Mux/mux.go @@ -0,0 +1,37 @@ +package main + +//go:generate depstubber -vendor github.com/gorilla/mux "" Vars,NewRouter + +import ( + "fmt" + "log" + "net/http" + "os/exec" + + "github.com/gorilla/mux" +) + +func ArticlesHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "Category: %v\n", vars["category"]) +} + +func CmdHandler(w http.ResponseWriter, r *http.Request) { + cmdName := mux.Vars(r)["cmd"] + + cmd := exec.Command(cmdName) + stdoutStderr, err := cmd.CombinedOutput() + if err != nil { + log.Print(err) + } + fmt.Fprintf(w, "%s\n", stdoutStderr) +} + +func main() { + r := mux.NewRouter() + r.HandleFunc("/run/{cmd}", CmdHandler) + r.HandleFunc("/articles/{category}", ArticlesHandler) + http.Handle("/", r) + log.Fatal(http.ListenAndServe(":8090", nil)) +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/github.com/gorilla/mux/LICENSE b/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/github.com/gorilla/mux/LICENSE new file mode 100644 index 00000000000..6903df6386e --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/github.com/gorilla/mux/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/github.com/gorilla/mux/stub.go b/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/github.com/gorilla/mux/stub.go new file mode 100644 index 00000000000..62510300b2d --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/github.com/gorilla/mux/stub.go @@ -0,0 +1,252 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gorilla/mux, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gorilla/mux (exports: ; functions: Vars,NewRouter) + +// Package mux is a stub of github.com/gorilla/mux, generated by depstubber. +package mux + +import ( + http "net/http" + url "net/url" +) + +type BuildVarsFunc func(map[string]string) map[string]string + +type MatcherFunc func(*http.Request, *RouteMatch) bool + +func (_ MatcherFunc) Match(_ *http.Request, _ *RouteMatch) bool { + return false +} + +type MiddlewareFunc func(http.Handler) http.Handler + +func (_ MiddlewareFunc) Middleware(_ http.Handler) http.Handler { + return nil +} + +func NewRouter() *Router { + return nil +} + +type Route struct{} + +func (_ *Route) BuildOnly() *Route { + return nil +} + +func (_ *Route) BuildVarsFunc(_ BuildVarsFunc) *Route { + return nil +} + +func (_ *Route) GetError() error { + return nil +} + +func (_ *Route) GetHandler() http.Handler { + return nil +} + +func (_ *Route) GetHostTemplate() (string, error) { + return "", nil +} + +func (_ *Route) GetMethods() ([]string, error) { + return nil, nil +} + +func (_ *Route) GetName() string { + return "" +} + +func (_ *Route) GetPathRegexp() (string, error) { + return "", nil +} + +func (_ *Route) GetPathTemplate() (string, error) { + return "", nil +} + +func (_ *Route) GetQueriesRegexp() ([]string, error) { + return nil, nil +} + +func (_ *Route) GetQueriesTemplates() ([]string, error) { + return nil, nil +} + +func (_ *Route) Handler(_ http.Handler) *Route { + return nil +} + +func (_ *Route) HandlerFunc(_ func(http.ResponseWriter, *http.Request)) *Route { + return nil +} + +func (_ *Route) Headers(_ ...string) *Route { + return nil +} + +func (_ *Route) HeadersRegexp(_ ...string) *Route { + return nil +} + +func (_ *Route) Host(_ string) *Route { + return nil +} + +func (_ *Route) Match(_ *http.Request, _ *RouteMatch) bool { + return false +} + +func (_ *Route) MatcherFunc(_ MatcherFunc) *Route { + return nil +} + +func (_ *Route) Methods(_ ...string) *Route { + return nil +} + +func (_ *Route) Name(_ string) *Route { + return nil +} + +func (_ *Route) Path(_ string) *Route { + return nil +} + +func (_ *Route) PathPrefix(_ string) *Route { + return nil +} + +func (_ *Route) Queries(_ ...string) *Route { + return nil +} + +func (_ *Route) Schemes(_ ...string) *Route { + return nil +} + +func (_ *Route) SkipClean() bool { + return false +} + +func (_ *Route) Subrouter() *Router { + return nil +} + +func (_ *Route) URL(_ ...string) (*url.URL, error) { + return nil, nil +} + +func (_ *Route) URLHost(_ ...string) (*url.URL, error) { + return nil, nil +} + +func (_ *Route) URLPath(_ ...string) (*url.URL, error) { + return nil, nil +} + +type RouteMatch struct { + Route *Route + Handler http.Handler + Vars map[string]string + MatchErr error +} + +type Router struct { + NotFoundHandler http.Handler + MethodNotAllowedHandler http.Handler + KeepContext bool +} + +func (_ *Router) BuildVarsFunc(_ BuildVarsFunc) *Route { + return nil +} + +func (_ *Router) Get(_ string) *Route { + return nil +} + +func (_ *Router) GetRoute(_ string) *Route { + return nil +} + +func (_ *Router) Handle(_ string, _ http.Handler) *Route { + return nil +} + +func (_ *Router) HandleFunc(_ string, _ func(http.ResponseWriter, *http.Request)) *Route { + return nil +} + +func (_ *Router) Headers(_ ...string) *Route { + return nil +} + +func (_ *Router) Host(_ string) *Route { + return nil +} + +func (_ *Router) Match(_ *http.Request, _ *RouteMatch) bool { + return false +} + +func (_ *Router) MatcherFunc(_ MatcherFunc) *Route { + return nil +} + +func (_ *Router) Methods(_ ...string) *Route { + return nil +} + +func (_ *Router) Name(_ string) *Route { + return nil +} + +func (_ *Router) NewRoute() *Route { + return nil +} + +func (_ *Router) Path(_ string) *Route { + return nil +} + +func (_ *Router) PathPrefix(_ string) *Route { + return nil +} + +func (_ *Router) Queries(_ ...string) *Route { + return nil +} + +func (_ *Router) Schemes(_ ...string) *Route { + return nil +} + +func (_ *Router) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {} + +func (_ *Router) SkipClean(_ bool) *Router { + return nil +} + +func (_ *Router) StrictSlash(_ bool) *Router { + return nil +} + +func (_ *Router) Use(_ ...MiddlewareFunc) {} + +func (_ *Router) UseEncodedPath() *Router { + return nil +} + +func (_ *Router) Walk(_ WalkFunc) error { + return nil +} + +func Vars(_ *http.Request) map[string]string { + return nil +} + +type WalkFunc func(*Route, *Router, []*Route) error diff --git a/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/modules.txt new file mode 100644 index 00000000000..d96be1fa71b --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Mux/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/gorilla/mux v1.7.4 +## explicit +github.com/gorilla/mux diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/Query.expected b/ql/test/library-tests/semmle/go/frameworks/NoSQL/Query.expected new file mode 100644 index 00000000000..109ad99673b --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/Query.expected @@ -0,0 +1,14 @@ +| main.go:24:22:24:29 | pipeline | +| main.go:27:27:27:32 | filter | +| main.go:29:23:29:28 | filter | +| main.go:30:22:30:27 | filter | +| main.go:32:32:32:37 | filter | +| main.go:35:17:35:22 | filter | +| main.go:36:20:36:25 | filter | +| main.go:37:29:37:34 | filter | +| main.go:38:30:38:35 | filter | +| main.go:39:29:39:34 | filter | +| main.go:45:23:45:28 | filter | +| main.go:47:23:47:28 | filter | +| main.go:48:22:48:27 | filter | +| main.go:49:18:49:25 | pipeline | diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/Query.ql b/ql/test/library-tests/semmle/go/frameworks/NoSQL/Query.ql new file mode 100644 index 00000000000..c5a89c0b8ef --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/Query.ql @@ -0,0 +1,3 @@ +import go + +select any(NoSQL::Query q) diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/go.mod b/ql/test/library-tests/semmle/go/frameworks/NoSQL/go.mod new file mode 100644 index 00000000000..6cd131e192f --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/go.mod @@ -0,0 +1,5 @@ +module main + +go 1.14 + +require go.mongodb.org/mongo-driver v1.3.2 diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/main.go b/ql/test/library-tests/semmle/go/frameworks/NoSQL/main.go new file mode 100644 index 00000000000..a437eef781b --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/main.go @@ -0,0 +1,52 @@ +package main + +//go:generate depstubber -vendor go.mongodb.org/mongo-driver/bson/primitive D +//go:generate depstubber -vendor go.mongodb.org/mongo-driver/mongo Collection,Pipeline + +import ( + "context" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +func test(coll *mongo.Collection, filter interface{}, models []mongo.WriteModel, ctx context.Context) { + + fieldName := "test" + document := filter + documents := []interface{}{ + document, + bson.D{{"name", "Bob"}}, + } + matchStage := bson.D{{"$match", filter}} + pipeline := mongo.Pipeline{matchStage} + + coll.Aggregate(ctx, pipeline, nil) + coll.BulkWrite(ctx, models, nil) + coll.Clone(nil) + coll.CountDocuments(ctx, filter, nil) + coll.Database() + coll.DeleteMany(ctx, filter, nil) + coll.DeleteOne(ctx, filter, nil) + + coll.Distinct(ctx, fieldName, filter) + coll.Drop(ctx) + coll.EstimatedDocumentCount(ctx, nil) + coll.Find(ctx, filter, nil) + coll.FindOne(ctx, filter, nil) + coll.FindOneAndDelete(ctx, filter, nil) + coll.FindOneAndReplace(ctx, filter, nil) + coll.FindOneAndUpdate(ctx, filter, nil) + coll.Indexes() + coll.InsertMany(ctx, documents) + coll.InsertOne(ctx, document, nil) + coll.Name() + replacement := bson.D{{"location", "NYC"}} + coll.ReplaceOne(ctx, filter, replacement) + update := bson.D{{"$inc", bson.D{{"age", 1}}}} + coll.UpdateMany(ctx, filter, update) + coll.UpdateOne(ctx, filter, update) + coll.Watch(ctx, pipeline) +} + +func main() {} diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/LICENSE b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/bson/primitive/stub.go b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/bson/primitive/stub.go new file mode 100644 index 00000000000..6f07aaff4ee --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/bson/primitive/stub.go @@ -0,0 +1,23 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go.mongodb.org/mongo-driver/bson/primitive, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go.mongodb.org/mongo-driver/bson/primitive (exports: D; functions: ) + +// Package primitive is a stub of go.mongodb.org/mongo-driver/bson/primitive, generated by depstubber. +package primitive + +import () + +type D []E + +func (_ D) Map() M { + return nil +} + +type E struct { + Key string + Value interface{} +} + +type M map[string]interface{} diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/bson/stub.go b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/bson/stub.go new file mode 100644 index 00000000000..de80f55501f --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/bson/stub.go @@ -0,0 +1,5 @@ +package bson + +import "go.mongodb.org/mongo-driver/bson/primitive" + +type D = primitive.D diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/mongo/stub.go b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/mongo/stub.go new file mode 100644 index 00000000000..be1ed14fbaf --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/go.mongodb.org/mongo-driver/mongo/stub.go @@ -0,0 +1,389 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go.mongodb.org/mongo-driver/mongo, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go.mongodb.org/mongo-driver/mongo (exports: Collection,Pipeline; functions: ) + +// Package mongo is a stub of go.mongodb.org/mongo-driver/mongo, generated by depstubber. +package mongo + +import ( + context "context" + time "time" +) + +type BulkWriteResult struct { + InsertedCount int64 + MatchedCount int64 + ModifiedCount int64 + DeletedCount int64 + UpsertedCount int64 + UpsertedIDs map[int64]interface{} +} + +type ChangeStream struct { + Current interface{} +} + +func (_ *ChangeStream) Close(_ context.Context) error { + return nil +} + +func (_ *ChangeStream) Decode(_ interface{}) error { + return nil +} + +func (_ *ChangeStream) Err() error { + return nil +} + +func (_ *ChangeStream) ID() int64 { + return 0 +} + +func (_ *ChangeStream) Next(_ context.Context) bool { + return false +} + +func (_ *ChangeStream) ResumeToken() interface{} { + return nil +} + +func (_ *ChangeStream) TryNext(_ context.Context) bool { + return false +} + +type Client struct{} + +func (_ *Client) Connect(_ context.Context) error { + return nil +} + +func (_ *Client) Database(_ string, _ ...interface{}) *Database { + return nil +} + +func (_ *Client) Disconnect(_ context.Context) error { + return nil +} + +func (_ *Client) ListDatabaseNames(_ context.Context, _ interface{}, _ ...interface{}) ([]string, error) { + return nil, nil +} + +func (_ *Client) ListDatabases(_ context.Context, _ interface{}, _ ...interface{}) (ListDatabasesResult, error) { + return ListDatabasesResult{}, nil +} + +func (_ *Client) NumberSessionsInProgress() int { + return 0 +} + +func (_ *Client) Ping(_ context.Context, _ interface{}) error { + return nil +} + +func (_ *Client) StartSession(_ ...interface{}) (Session, error) { + return nil, nil +} + +func (_ *Client) UseSession(_ context.Context, _ func(SessionContext) error) error { + return nil +} + +func (_ *Client) UseSessionWithOptions(_ context.Context, _ interface{}, _ func(SessionContext) error) error { + return nil +} + +func (_ *Client) Watch(_ context.Context, _ interface{}, _ ...interface{}) (*ChangeStream, error) { + return nil, nil +} + +type Collection struct{} + +func (_ *Collection) Aggregate(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Collection) BulkWrite(_ context.Context, _ []WriteModel, _ ...interface{}) (*BulkWriteResult, error) { + return nil, nil +} + +func (_ *Collection) Clone(_ ...interface{}) (*Collection, error) { + return nil, nil +} + +func (_ *Collection) CountDocuments(_ context.Context, _ interface{}, _ ...interface{}) (int64, error) { + return 0, nil +} + +func (_ *Collection) Database() *Database { + return nil +} + +func (_ *Collection) DeleteMany(_ context.Context, _ interface{}, _ ...interface{}) (*DeleteResult, error) { + return nil, nil +} + +func (_ *Collection) DeleteOne(_ context.Context, _ interface{}, _ ...interface{}) (*DeleteResult, error) { + return nil, nil +} + +func (_ *Collection) Distinct(_ context.Context, _ string, _ interface{}, _ ...interface{}) ([]interface{}, error) { + return nil, nil +} + +func (_ *Collection) Drop(_ context.Context) error { + return nil +} + +func (_ *Collection) EstimatedDocumentCount(_ context.Context, _ ...interface{}) (int64, error) { + return 0, nil +} + +func (_ *Collection) Find(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Collection) FindOne(_ context.Context, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Collection) FindOneAndDelete(_ context.Context, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Collection) FindOneAndReplace(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Collection) FindOneAndUpdate(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Collection) Indexes() IndexView { + return IndexView{} +} + +func (_ *Collection) InsertMany(_ context.Context, _ []interface{}, _ ...interface{}) (*InsertManyResult, error) { + return nil, nil +} + +func (_ *Collection) InsertOne(_ context.Context, _ interface{}, _ ...interface{}) (*InsertOneResult, error) { + return nil, nil +} + +func (_ *Collection) Name() string { + return "" +} + +func (_ *Collection) ReplaceOne(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (*UpdateResult, error) { + return nil, nil +} + +func (_ *Collection) UpdateMany(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (*UpdateResult, error) { + return nil, nil +} + +func (_ *Collection) UpdateOne(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (*UpdateResult, error) { + return nil, nil +} + +func (_ *Collection) Watch(_ context.Context, _ interface{}, _ ...interface{}) (*ChangeStream, error) { + return nil, nil +} + +type Cursor struct { + Current interface{} +} + +func (_ *Cursor) All(_ context.Context, _ interface{}) error { + return nil +} + +func (_ *Cursor) Close(_ context.Context) error { + return nil +} + +func (_ *Cursor) Decode(_ interface{}) error { + return nil +} + +func (_ *Cursor) Err() error { + return nil +} + +func (_ *Cursor) ID() int64 { + return 0 +} + +func (_ *Cursor) Next(_ context.Context) bool { + return false +} + +func (_ *Cursor) TryNext(_ context.Context) bool { + return false +} + +type Database struct{} + +func (_ *Database) Aggregate(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Database) Client() *Client { + return nil +} + +func (_ *Database) Collection(_ string, _ ...interface{}) *Collection { + return nil +} + +func (_ *Database) Drop(_ context.Context) error { + return nil +} + +func (_ *Database) ListCollectionNames(_ context.Context, _ interface{}, _ ...interface{}) ([]string, error) { + return nil, nil +} + +func (_ *Database) ListCollections(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Database) Name() string { + return "" +} + +func (_ *Database) ReadConcern() interface{} { + return nil +} + +func (_ *Database) ReadPreference() interface{} { + return nil +} + +func (_ *Database) RunCommand(_ context.Context, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Database) RunCommandCursor(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Database) Watch(_ context.Context, _ interface{}, _ ...interface{}) (*ChangeStream, error) { + return nil, nil +} + +func (_ *Database) WriteConcern() interface{} { + return nil +} + +type DatabaseSpecification struct { + Name string + SizeOnDisk int64 + Empty bool +} + +type DeleteResult struct { + DeletedCount int64 +} + +type IndexModel struct { + Keys interface{} + Options interface{} +} + +type IndexView struct{} + +func (_ IndexView) CreateMany(_ context.Context, _ []IndexModel, _ ...interface{}) ([]string, error) { + return nil, nil +} + +func (_ IndexView) CreateOne(_ context.Context, _ IndexModel, _ ...interface{}) (string, error) { + return "", nil +} + +func (_ IndexView) DropAll(_ context.Context, _ ...interface{}) (interface{}, error) { + return nil, nil +} + +func (_ IndexView) DropOne(_ context.Context, _ string, _ ...interface{}) (interface{}, error) { + return nil, nil +} + +func (_ IndexView) List(_ context.Context, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +type InsertManyResult struct { + InsertedIDs []interface{} +} + +type InsertOneResult struct { + InsertedID interface{} +} + +type ListDatabasesResult struct { + Databases []DatabaseSpecification + TotalSize int64 +} + +type Pipeline []interface{} + +type Session interface { + AbortTransaction(_ context.Context) error + AdvanceClusterTime(_ interface{}) error + AdvanceOperationTime(_ interface{}) error + Client() *Client + ClusterTime() interface{} + CommitTransaction(_ context.Context) error + EndSession(_ context.Context) + OperationTime() interface{} + StartTransaction(_ ...interface{}) error + WithTransaction(_ context.Context, _ func(SessionContext) (interface{}, error), _ ...interface{}) (interface{}, error) +} + +type SessionContext interface { + AbortTransaction(_ context.Context) error + AdvanceClusterTime(_ interface{}) error + AdvanceOperationTime(_ interface{}) error + Client() *Client + ClusterTime() interface{} + CommitTransaction(_ context.Context) error + Deadline() (time.Time, bool) + Done() <-chan struct{} + EndSession(_ context.Context) + Err() error + OperationTime() interface{} + StartTransaction(_ ...interface{}) error + Value(_ interface{}) interface{} + WithTransaction(_ context.Context, _ func(SessionContext) (interface{}, error), _ ...interface{}) (interface{}, error) +} + +type SingleResult struct{} + +func (_ *SingleResult) Decode(_ interface{}) error { + return nil +} + +func (_ *SingleResult) DecodeBytes() (interface{}, error) { + return nil, nil +} + +func (_ *SingleResult) Err() error { + return nil +} + +type UpdateResult struct { + MatchedCount int64 + ModifiedCount int64 + UpsertedCount int64 + UpsertedID interface{} +} + +func (_ *UpdateResult) UnmarshalBSON(_ []byte) error { + return nil +} + +type WriteModel interface{} diff --git a/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/modules.txt new file mode 100644 index 00000000000..bcea2f371ea --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/modules.txt @@ -0,0 +1,3 @@ +# go.mongodb.org/mongo-driver v1.3.2 +## explicit +go.mongodb.org/mongo-driver diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/go.mod b/ql/test/library-tests/semmle/go/frameworks/SQL/go.mod index 14ee2e225ee..23bb420b262 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SQL/go.mod +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/go.mod @@ -4,11 +4,6 @@ go 1.13 require ( github.com/Masterminds/squirrel v1.1.0 - github.com/github/depstubber v0.0.0-20200414033246-a63ca77a1581 // indirect github.com/go-pg/pg v8.0.6+incompatible github.com/go-pg/pg/v9 v9.1.3 - github.com/go-sql-driver/mysql v1.5.0 // indirect - github.com/lib/pq v1.3.0 // indirect - github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect - golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect ) diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/Masterminds/squirrel/stub.go b/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/Masterminds/squirrel/stub.go index 19d94461f3a..fc639e9e209 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/Masterminds/squirrel/stub.go +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/Masterminds/squirrel/stub.go @@ -13,12 +13,8 @@ import ( ) type BaseRunner interface { - Exec(_ string, _ ...interface{}) (sql.Result, interface { - Error() string - }) - Query(_ string, _ ...interface{}) (*sql.Rows, interface { - Error() string - }) + Exec(_ string, _ ...interface{}) (sql.Result, error) + Query(_ string, _ ...interface{}) (*sql.Rows, error) } func Expr(_ string, _ ...interface{}) interface{} { @@ -26,15 +22,11 @@ func Expr(_ string, _ ...interface{}) interface{} { } type PlaceholderFormat interface { - ReplacePlaceholders(_ string) (string, interface { - Error() string - }) + ReplacePlaceholders(_ string) (string, error) } type RowScanner interface { - Scan(_ ...interface{}) interface { - Error() string - } + Scan(_ ...interface{}) error } func Select(_ ...string) SelectBuilder { @@ -55,15 +47,11 @@ func (_ SelectBuilder) Distinct() SelectBuilder { return SelectBuilder{} } -func (_ SelectBuilder) Exec() (sql.Result, interface { - Error() string -}) { +func (_ SelectBuilder) Exec() (sql.Result, error) { return nil, nil } -func (_ SelectBuilder) ExecContext(_ context.Context) (sql.Result, interface { - Error() string -}) { +func (_ SelectBuilder) ExecContext(_ context.Context) (sql.Result, error) { return nil, nil } @@ -123,15 +111,11 @@ func (_ SelectBuilder) Prefix(_ string, _ ...interface{}) SelectBuilder { return SelectBuilder{} } -func (_ SelectBuilder) Query() (*sql.Rows, interface { - Error() string -}) { +func (_ SelectBuilder) Query() (*sql.Rows, error) { return nil, nil } -func (_ SelectBuilder) QueryContext(_ context.Context) (*sql.Rows, interface { - Error() string -}) { +func (_ SelectBuilder) QueryContext(_ context.Context) (*sql.Rows, error) { return nil, nil } @@ -155,15 +139,11 @@ func (_ SelectBuilder) RunWith(_ BaseRunner) SelectBuilder { return SelectBuilder{} } -func (_ SelectBuilder) Scan(_ ...interface{}) interface { - Error() string -} { +func (_ SelectBuilder) Scan(_ ...interface{}) error { return nil } -func (_ SelectBuilder) ScanContext(_ context.Context, _ ...interface{}) interface { - Error() string -} { +func (_ SelectBuilder) ScanContext(_ context.Context, _ ...interface{}) error { return nil } @@ -171,9 +151,7 @@ func (_ SelectBuilder) Suffix(_ string, _ ...interface{}) SelectBuilder { return SelectBuilder{} } -func (_ SelectBuilder) ToSql() (string, []interface{}, interface { - Error() string -}) { +func (_ SelectBuilder) ToSql() (string, []interface{}, error) { return "", nil, nil } diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/orm/stub.go b/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/orm/stub.go index 15d5f583e6a..b4cedc671bf 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/orm/stub.go +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/orm/stub.go @@ -14,9 +14,7 @@ import ( ) type ColumnScanner interface { - ScanColumn(_ int, _ string, _ interface{}, _ int) interface { - Error() string - } + ScanColumn(_ int, _ string, _ interface{}, _ int) error } type CreateTableOptions struct { @@ -28,54 +26,24 @@ type CreateTableOptions struct { type DB interface { Context() context.Context - CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - Delete(_ interface{}) interface { - Error() string - } - Exec(_ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - ExecOne(_ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - ForceDelete(_ interface{}) interface { - Error() string - } - FormatQuery(_ []uint8, _ string, _ ...interface{}) []uint8 - Insert(_ ...interface{}) interface { - Error() string - } + CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (Result, error) + CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (Result, error) + Delete(_ interface{}) error + Exec(_ interface{}, _ ...interface{}) (Result, error) + ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (Result, error) + ExecOne(_ interface{}, _ ...interface{}) (Result, error) + ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (Result, error) + ForceDelete(_ interface{}) error + FormatQuery(_ []byte, _ string, _ ...interface{}) []byte + Insert(_ ...interface{}) error Model(_ ...interface{}) *Query ModelContext(_ context.Context, _ ...interface{}) *Query - Query(_ interface{}, _ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (Result, interface { - Error() string - }) - Select(_ interface{}) interface { - Error() string - } - Update(_ interface{}) interface { - Error() string - } + Query(_ interface{}, _ interface{}, _ ...interface{}) (Result, error) + QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (Result, error) + QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (Result, error) + QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (Result, error) + Select(_ interface{}) error + Update(_ interface{}) error } type DropTableOptions struct { @@ -96,7 +64,7 @@ type Field struct { OnUpdate string } -func (_ *Field) AppendValue(_ []uint8, _ reflect.Value, _ int) []uint8 { +func (_ *Field) AppendValue(_ []byte, _ reflect.Value, _ int) []byte { return nil } @@ -104,7 +72,7 @@ func (_ *Field) Copy() *Field { return nil } -func (_ *Field) HasFlag(_ uint8) bool { +func (_ *Field) HasFlag(_ byte) bool { return false } @@ -116,13 +84,11 @@ func (_ *Field) OmitZero() bool { return false } -func (_ *Field) ScanValue(_ reflect.Value, _ interface{}, _ int) interface { - Error() string -} { +func (_ *Field) ScanValue(_ reflect.Value, _ interface{}, _ int) error { return nil } -func (_ *Field) SetFlag(_ uint8) {} +func (_ *Field) SetFlag(_ byte) {} func (_ *Field) Value(_ reflect.Value) reflect.Value { return reflect.Value{} @@ -132,7 +98,7 @@ type Method struct { Index int } -func (_ *Method) AppendValue(_ []uint8, _ reflect.Value, _ int) []uint8 { +func (_ *Method) AppendValue(_ []byte, _ reflect.Value, _ int) []byte { return nil } @@ -145,55 +111,31 @@ func (_ *Method) Value(_ reflect.Value) reflect.Value { } type Model interface { - AddModel(_ ColumnScanner) interface { - Error() string - } - AfterDelete(_ context.Context, _ DB) interface { - Error() string - } - AfterInsert(_ context.Context, _ DB) interface { - Error() string - } - AfterQuery(_ context.Context, _ DB) interface { - Error() string - } - AfterSelect(_ context.Context, _ DB) interface { - Error() string - } - AfterUpdate(_ context.Context, _ DB) interface { - Error() string - } - BeforeDelete(_ context.Context, _ DB) interface { - Error() string - } - BeforeInsert(_ context.Context, _ DB) interface { - Error() string - } - BeforeSelectQuery(_ context.Context, _ DB, _ *Query) (*Query, interface { - Error() string - }) - BeforeUpdate(_ context.Context, _ DB) interface { - Error() string - } - Init() interface { - Error() string - } + AddModel(_ ColumnScanner) error + AfterDelete(_ context.Context, _ DB) error + AfterInsert(_ context.Context, _ DB) error + AfterQuery(_ context.Context, _ DB) error + AfterSelect(_ context.Context, _ DB) error + AfterUpdate(_ context.Context, _ DB) error + BeforeDelete(_ context.Context, _ DB) error + BeforeInsert(_ context.Context, _ DB) error + BeforeSelectQuery(_ context.Context, _ DB, _ *Query) (*Query, error) + BeforeUpdate(_ context.Context, _ DB) error + Init() error NewModel() ColumnScanner } -func Q(_ string, _ ...interface{}) *interface{} { +func Q(_ string, _ ...interface{}) interface{} { return nil } type Query struct{} -func (_ *Query) AppendFormat(_ []uint8, _ QueryFormatter) []uint8 { +func (_ *Query) AppendFormat(_ []byte, _ QueryFormatter) []byte { return nil } -func (_ *Query) Apply(_ func(*Query) (*Query, interface { - Error() string -})) *Query { +func (_ *Query) Apply(_ func(*Query) (*Query, error)) *Query { return nil } @@ -213,33 +155,23 @@ func (_ *Query) Copy() *Query { return nil } -func (_ *Query) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (Result, error) { return nil, nil } -func (_ *Query) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (Result, error) { return nil, nil } -func (_ *Query) Count() (int, interface { - Error() string -}) { +func (_ *Query) Count() (int, error) { return 0, nil } -func (_ *Query) CountEstimate(_ int) (int, interface { - Error() string -}) { +func (_ *Query) CountEstimate(_ int) (int, error) { return 0, nil } -func (_ *Query) CreateTable(_ *CreateTableOptions) interface { - Error() string -} { +func (_ *Query) CreateTable(_ *CreateTableOptions) error { return nil } @@ -247,9 +179,7 @@ func (_ *Query) DB(_ DB) *Query { return nil } -func (_ *Query) Delete(_ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) Delete(_ ...interface{}) (Result, error) { return nil, nil } @@ -257,9 +187,7 @@ func (_ *Query) Deleted() *Query { return nil } -func (_ *Query) DropTable(_ *DropTableOptions) interface { - Error() string -} { +func (_ *Query) DropTable(_ *DropTableOptions) error { return nil } @@ -267,27 +195,19 @@ func (_ *Query) ExcludeColumn(_ ...string) *Query { return nil } -func (_ *Query) Exec(_ interface{}, _ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) Exec(_ interface{}, _ ...interface{}) (Result, error) { return nil, nil } -func (_ *Query) ExecOne(_ interface{}, _ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) ExecOne(_ interface{}, _ ...interface{}) (Result, error) { return nil, nil } -func (_ *Query) Exists() (bool, interface { - Error() string -}) { +func (_ *Query) Exists() (bool, error) { return false, nil } -func (_ *Query) First() interface { - Error() string -} { +func (_ *Query) First() error { return nil } @@ -295,19 +215,15 @@ func (_ *Query) For(_ string, _ ...interface{}) *Query { return nil } -func (_ *Query) ForEach(_ interface{}) interface { - Error() string -} { +func (_ *Query) ForEach(_ interface{}) error { return nil } -func (_ *Query) ForceDelete(_ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) ForceDelete(_ ...interface{}) (Result, error) { return nil, nil } -func (_ *Query) FormatQuery(_ []uint8, _ string, _ ...interface{}) []uint8 { +func (_ *Query) FormatQuery(_ []byte, _ string, _ ...interface{}) []byte { return nil } @@ -331,9 +247,7 @@ func (_ *Query) Having(_ string, _ ...interface{}) *Query { return nil } -func (_ *Query) Insert(_ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) Insert(_ ...interface{}) (Result, error) { return nil, nil } @@ -349,9 +263,7 @@ func (_ *Query) JoinOnOr(_ string, _ ...interface{}) *Query { return nil } -func (_ *Query) Last() interface { - Error() string -} { +func (_ *Query) Last() error { return nil } @@ -383,21 +295,15 @@ func (_ *Query) OrderExpr(_ string, _ ...interface{}) *Query { return nil } -func (_ *Query) Query(_ interface{}, _ interface{}, _ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) Query(_ interface{}, _ interface{}, _ ...interface{}) (Result, error) { return nil, nil } -func (_ *Query) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (Result, error) { return nil, nil } -func (_ *Query) Relation(_ string, _ ...func(*Query) (*Query, interface { - Error() string -})) *Query { +func (_ *Query) Relation(_ string, _ ...func(*Query) (*Query, error)) *Query { return nil } @@ -405,27 +311,19 @@ func (_ *Query) Returning(_ string, _ ...interface{}) *Query { return nil } -func (_ *Query) Select(_ ...interface{}) interface { - Error() string -} { +func (_ *Query) Select(_ ...interface{}) error { return nil } -func (_ *Query) SelectAndCount(_ ...interface{}) (int, interface { - Error() string -}) { +func (_ *Query) SelectAndCount(_ ...interface{}) (int, error) { return 0, nil } -func (_ *Query) SelectAndCountEstimate(_ int, _ ...interface{}) (int, interface { - Error() string -}) { +func (_ *Query) SelectAndCountEstimate(_ int, _ ...interface{}) (int, error) { return 0, nil } -func (_ *Query) SelectOrInsert(_ ...interface{}) (bool, interface { - Error() string -}) { +func (_ *Query) SelectOrInsert(_ ...interface{}) (bool, error) { return false, nil } @@ -441,15 +339,11 @@ func (_ *Query) TableExpr(_ string, _ ...interface{}) *Query { return nil } -func (_ *Query) Update(_ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) Update(_ ...interface{}) (Result, error) { return nil, nil } -func (_ *Query) UpdateNotNull(_ ...interface{}) (Result, interface { - Error() string -}) { +func (_ *Query) UpdateNotNull(_ ...interface{}) (Result, error) { return nil, nil } @@ -461,9 +355,7 @@ func (_ *Query) Where(_ string, _ ...interface{}) *Query { return nil } -func (_ *Query) WhereGroup(_ func(*Query) (*Query, interface { - Error() string -})) *Query { +func (_ *Query) WhereGroup(_ func(*Query) (*Query, error)) *Query { return nil } @@ -479,9 +371,7 @@ func (_ *Query) WhereOr(_ string, _ ...interface{}) *Query { return nil } -func (_ *Query) WhereOrGroup(_ func(*Query) (*Query, interface { - Error() string -})) *Query { +func (_ *Query) WhereOrGroup(_ func(*Query) (*Query, error)) *Query { return nil } @@ -502,7 +392,7 @@ func (_ *Query) WrapWith(_ string) *Query { } type QueryFormatter interface { - FormatQuery(_ []uint8, _ string, _ ...interface{}) []uint8 + FormatQuery(_ []byte, _ string, _ ...interface{}) []byte } type Relation struct { @@ -549,13 +439,11 @@ type Table struct { func (_ *Table) AddField(_ *Field) {} -func (_ *Table) AppendParam(_ []uint8, _ reflect.Value, _ string) ([]uint8, bool) { +func (_ *Table) AppendParam(_ []byte, _ reflect.Value, _ string) ([]byte, bool) { return nil, false } -func (_ *Table) GetField(_ string) (*Field, interface { - Error() string -}) { +func (_ *Table) GetField(_ string) (*Field, error) { return nil, nil } @@ -576,48 +464,24 @@ func (_ *Table) String() string { } type TableModel interface { - AddJoin(_ interface{}) *interface{} - AddModel(_ ColumnScanner) interface { - Error() string - } - AfterDelete(_ context.Context, _ DB) interface { - Error() string - } - AfterInsert(_ context.Context, _ DB) interface { - Error() string - } - AfterQuery(_ context.Context, _ DB) interface { - Error() string - } - AfterSelect(_ context.Context, _ DB) interface { - Error() string - } - AfterUpdate(_ context.Context, _ DB) interface { - Error() string - } - AppendParam(_ []uint8, _ QueryFormatter, _ string) ([]uint8, bool) - BeforeDelete(_ context.Context, _ DB) interface { - Error() string - } - BeforeInsert(_ context.Context, _ DB) interface { - Error() string - } - BeforeSelectQuery(_ context.Context, _ DB, _ *Query) (*Query, interface { - Error() string - }) - BeforeUpdate(_ context.Context, _ DB) interface { - Error() string - } - GetJoin(_ string) *interface{} + AddJoin(_ interface{}) interface{} + AddModel(_ ColumnScanner) error + AfterDelete(_ context.Context, _ DB) error + AfterInsert(_ context.Context, _ DB) error + AfterQuery(_ context.Context, _ DB) error + AfterSelect(_ context.Context, _ DB) error + AfterUpdate(_ context.Context, _ DB) error + AppendParam(_ []byte, _ QueryFormatter, _ string) ([]byte, bool) + BeforeDelete(_ context.Context, _ DB) error + BeforeInsert(_ context.Context, _ DB) error + BeforeSelectQuery(_ context.Context, _ DB, _ *Query) (*Query, error) + BeforeUpdate(_ context.Context, _ DB) error + GetJoin(_ string) interface{} GetJoins() []interface{} Index() []int - Init() interface { - Error() string - } + Init() error IsNil() bool - Join(_ string, _ func(*Query) (*Query, interface { - Error() string - })) *interface{} + Join(_ string, _ func(*Query) (*Query, error)) interface{} Kind() reflect.Kind Mount(_ reflect.Value) NewModel() ColumnScanner diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/stub.go b/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/stub.go index 0832aac36c4..d92b1486ef0 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/stub.go +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/stub.go @@ -19,105 +19,75 @@ type Conn struct{} func (_ Conn) AddQueryHook(_ QueryHook) {} -func (_ Conn) Begin() (*Tx, interface { - Error() string -}) { +func (_ Conn) Begin() (*Tx, error) { return nil, nil } -func (_ Conn) Close() interface { - Error() string -} { +func (_ Conn) Close() error { return nil } -func (_ Conn) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) CreateComposite(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ Conn) CreateComposite(_ interface{}, _ interface{}) error { return nil } -func (_ Conn) CreateTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ Conn) CreateTable(_ interface{}, _ interface{}) error { return nil } -func (_ Conn) Delete(_ interface{}) interface { - Error() string -} { +func (_ Conn) Delete(_ interface{}) error { return nil } -func (_ Conn) DropComposite(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ Conn) DropComposite(_ interface{}, _ interface{}) error { return nil } -func (_ Conn) DropTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ Conn) DropTable(_ interface{}, _ interface{}) error { return nil } -func (_ Conn) Exec(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) Exec(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) ExecOne(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) ExecOne(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) ForceDelete(_ interface{}) interface { - Error() string -} { +func (_ Conn) ForceDelete(_ interface{}) error { return nil } -func (_ Conn) FormatQuery(_ []uint8, _ string, _ ...interface{}) []uint8 { +func (_ Conn) FormatQuery(_ []byte, _ string, _ ...interface{}) []byte { return nil } -func (_ Conn) Insert(_ ...interface{}) interface { - Error() string -} { +func (_ Conn) Insert(_ ...interface{}) error { return nil } -func (_ Conn) Model(_ ...interface{}) *interface{} { +func (_ Conn) Model(_ ...interface{}) interface{} { return nil } -func (_ Conn) ModelContext(_ context.Context, _ ...interface{}) *interface{} { +func (_ Conn) ModelContext(_ context.Context, _ ...interface{}) interface{} { return nil } @@ -129,53 +99,35 @@ func (_ Conn) PoolStats() *PoolStats { return nil } -func (_ Conn) Prepare(_ string) (*Stmt, interface { - Error() string -}) { +func (_ Conn) Prepare(_ string) (*Stmt, error) { return nil, nil } -func (_ Conn) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) RunInTransaction(_ func(*Tx) interface { - Error() string -}) interface { - Error() string -} { +func (_ Conn) RunInTransaction(_ func(*Tx) error) error { return nil } -func (_ Conn) Select(_ interface{}) interface { - Error() string -} { +func (_ Conn) Select(_ interface{}) error { return nil } -func (_ Conn) Update(_ interface{}) interface { - Error() string -} { +func (_ Conn) Update(_ interface{}) error { return nil } @@ -199,105 +151,75 @@ type DB struct{} func (_ DB) AddQueryHook(_ QueryHook) {} -func (_ DB) Begin() (*Tx, interface { - Error() string -}) { +func (_ DB) Begin() (*Tx, error) { return nil, nil } -func (_ DB) Close() interface { - Error() string -} { +func (_ DB) Close() error { return nil } -func (_ DB) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) CreateComposite(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ DB) CreateComposite(_ interface{}, _ interface{}) error { return nil } -func (_ DB) CreateTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ DB) CreateTable(_ interface{}, _ interface{}) error { return nil } -func (_ DB) Delete(_ interface{}) interface { - Error() string -} { +func (_ DB) Delete(_ interface{}) error { return nil } -func (_ DB) DropComposite(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ DB) DropComposite(_ interface{}, _ interface{}) error { return nil } -func (_ DB) DropTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ DB) DropTable(_ interface{}, _ interface{}) error { return nil } -func (_ DB) Exec(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) Exec(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) ExecOne(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) ExecOne(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) ForceDelete(_ interface{}) interface { - Error() string -} { +func (_ DB) ForceDelete(_ interface{}) error { return nil } -func (_ DB) FormatQuery(_ []uint8, _ string, _ ...interface{}) []uint8 { +func (_ DB) FormatQuery(_ []byte, _ string, _ ...interface{}) []byte { return nil } -func (_ DB) Insert(_ ...interface{}) interface { - Error() string -} { +func (_ DB) Insert(_ ...interface{}) error { return nil } -func (_ DB) Model(_ ...interface{}) *interface{} { +func (_ DB) Model(_ ...interface{}) interface{} { return nil } -func (_ DB) ModelContext(_ context.Context, _ ...interface{}) *interface{} { +func (_ DB) ModelContext(_ context.Context, _ ...interface{}) interface{} { return nil } @@ -309,53 +231,35 @@ func (_ DB) PoolStats() *PoolStats { return nil } -func (_ DB) Prepare(_ string) (*Stmt, interface { - Error() string -}) { +func (_ DB) Prepare(_ string) (*Stmt, error) { return nil, nil } -func (_ DB) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) RunInTransaction(_ func(*Tx) interface { - Error() string -}) interface { - Error() string -} { +func (_ DB) RunInTransaction(_ func(*Tx) error) error { return nil } -func (_ DB) Select(_ interface{}) interface { - Error() string -} { +func (_ DB) Select(_ interface{}) error { return nil } -func (_ DB) Update(_ interface{}) interface { - Error() string -} { +func (_ DB) Update(_ interface{}) error { return nil } @@ -401,27 +305,19 @@ func (_ *Listener) ChannelSize(_ int) <-chan *Notification { return nil } -func (_ *Listener) Close() interface { - Error() string -} { +func (_ *Listener) Close() error { return nil } -func (_ *Listener) Listen(_ ...string) interface { - Error() string -} { +func (_ *Listener) Listen(_ ...string) error { return nil } -func (_ *Listener) Receive() (string, string, interface { - Error() string -}) { +func (_ *Listener) Receive() (string, string, error) { return "", "", nil } -func (_ *Listener) ReceiveTimeout(_ time.Duration) (string, string, interface { - Error() string -}) { +func (_ *Listener) ReceiveTimeout(_ time.Duration) (string, string, error) { return "", "", nil } @@ -435,14 +331,10 @@ type Notification struct { } type Options struct { - Network string - Addr string - Dialer func(string, string) (net.Conn, interface { - Error() string - }) - OnConnect func(*Conn) interface { - Error() string - } + Network string + Addr string + Dialer func(string, string) (net.Conn, error) + OnConnect func(*Conn) error User string Password string Database string @@ -483,21 +375,15 @@ type QueryEvent struct { Params []interface{} Attempt int Result interface{} - Error interface { - Error() string - } - Data map[interface{}]interface{} + Error error + Data map[interface{}]interface{} } -func (_ *QueryEvent) FormattedQuery() (string, interface { - Error() string -}) { +func (_ *QueryEvent) FormattedQuery() (string, error) { return "", nil } -func (_ *QueryEvent) UnformattedQuery() (string, interface { - Error() string -}) { +func (_ *QueryEvent) UnformattedQuery() (string, error) { return "", nil } @@ -508,71 +394,49 @@ type QueryHook interface { type Stmt struct{} -func (_ *Stmt) Close() interface { - Error() string -} { +func (_ *Stmt) Close() error { return nil } -func (_ *Stmt) Exec(_ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) Exec(_ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) ExecContext(_ context.Context, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) ExecContext(_ context.Context, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) ExecOne(_ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) ExecOne(_ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) ExecOneContext(_ context.Context, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) ExecOneContext(_ context.Context, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) Query(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) Query(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) QueryContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) QueryContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) QueryOne(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) QueryOne(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) QueryOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) QueryOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } type Tx struct{} -func (_ *Tx) Begin() (*Tx, interface { - Error() string -}) { +func (_ *Tx) Begin() (*Tx, error) { return nil, nil } -func (_ *Tx) Commit() interface { - Error() string -} { +func (_ *Tx) Commit() error { return nil } @@ -580,131 +444,91 @@ func (_ *Tx) Context() context.Context { return nil } -func (_ *Tx) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) CreateTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ *Tx) CreateTable(_ interface{}, _ interface{}) error { return nil } -func (_ *Tx) Delete(_ interface{}) interface { - Error() string -} { +func (_ *Tx) Delete(_ interface{}) error { return nil } -func (_ *Tx) DropTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ *Tx) DropTable(_ interface{}, _ interface{}) error { return nil } -func (_ *Tx) Exec(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) Exec(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) ExecOne(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) ExecOne(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) ForceDelete(_ interface{}) interface { - Error() string -} { +func (_ *Tx) ForceDelete(_ interface{}) error { return nil } -func (_ *Tx) FormatQuery(_ []uint8, _ string, _ ...interface{}) []uint8 { +func (_ *Tx) FormatQuery(_ []byte, _ string, _ ...interface{}) []byte { return nil } -func (_ *Tx) Insert(_ ...interface{}) interface { - Error() string -} { +func (_ *Tx) Insert(_ ...interface{}) error { return nil } -func (_ *Tx) Model(_ ...interface{}) *interface{} { +func (_ *Tx) Model(_ ...interface{}) interface{} { return nil } -func (_ *Tx) ModelContext(_ context.Context, _ ...interface{}) *interface{} { +func (_ *Tx) ModelContext(_ context.Context, _ ...interface{}) interface{} { return nil } -func (_ *Tx) Prepare(_ string) (*Stmt, interface { - Error() string -}) { +func (_ *Tx) Prepare(_ string) (*Stmt, error) { return nil, nil } -func (_ *Tx) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) Rollback() interface { - Error() string -} { +func (_ *Tx) Rollback() error { return nil } -func (_ *Tx) RunInTransaction(_ func(*Tx) interface { - Error() string -}) interface { - Error() string -} { +func (_ *Tx) RunInTransaction(_ func(*Tx) error) error { return nil } -func (_ *Tx) Select(_ interface{}) interface { - Error() string -} { +func (_ *Tx) Select(_ interface{}) error { return nil } @@ -712,8 +536,6 @@ func (_ *Tx) Stmt(_ *Stmt) *Stmt { return nil } -func (_ *Tx) Update(_ interface{}) interface { - Error() string -} { +func (_ *Tx) Update(_ interface{}) error { return nil } diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/v9/stub.go b/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/v9/stub.go index 3ddcee60a45..58a8442cc7c 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/v9/stub.go +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/vendor/github.com/go-pg/pg/v9/stub.go @@ -19,87 +19,59 @@ type Conn struct{} func (_ Conn) AddQueryHook(_ QueryHook) {} -func (_ Conn) Begin() (*Tx, interface { - Error() string -}) { +func (_ Conn) Begin() (*Tx, error) { return nil, nil } -func (_ Conn) Close() interface { - Error() string -} { +func (_ Conn) Close() error { return nil } -func (_ Conn) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) CreateComposite(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ Conn) CreateComposite(_ interface{}, _ interface{}) error { return nil } -func (_ Conn) CreateTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ Conn) CreateTable(_ interface{}, _ interface{}) error { return nil } -func (_ Conn) Delete(_ interface{}) interface { - Error() string -} { +func (_ Conn) Delete(_ interface{}) error { return nil } -func (_ Conn) DropComposite(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ Conn) DropComposite(_ interface{}, _ interface{}) error { return nil } -func (_ Conn) DropTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ Conn) DropTable(_ interface{}, _ interface{}) error { return nil } -func (_ Conn) Exec(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) Exec(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) ExecOne(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) ExecOne(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) ForceDelete(_ interface{}) interface { - Error() string -} { +func (_ Conn) ForceDelete(_ interface{}) error { return nil } @@ -107,17 +79,15 @@ func (_ Conn) Formatter() interface{} { return nil } -func (_ Conn) Insert(_ ...interface{}) interface { - Error() string -} { +func (_ Conn) Insert(_ ...interface{}) error { return nil } -func (_ Conn) Model(_ ...interface{}) *interface{} { +func (_ Conn) Model(_ ...interface{}) interface{} { return nil } -func (_ Conn) ModelContext(_ context.Context, _ ...interface{}) *interface{} { +func (_ Conn) ModelContext(_ context.Context, _ ...interface{}) interface{} { return nil } @@ -129,53 +99,35 @@ func (_ Conn) PoolStats() *PoolStats { return nil } -func (_ Conn) Prepare(_ string) (*Stmt, interface { - Error() string -}) { +func (_ Conn) Prepare(_ string) (*Stmt, error) { return nil, nil } -func (_ Conn) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ Conn) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ Conn) RunInTransaction(_ func(*Tx) interface { - Error() string -}) interface { - Error() string -} { +func (_ Conn) RunInTransaction(_ func(*Tx) error) error { return nil } -func (_ Conn) Select(_ interface{}) interface { - Error() string -} { +func (_ Conn) Select(_ interface{}) error { return nil } -func (_ Conn) Update(_ interface{}) interface { - Error() string -} { +func (_ Conn) Update(_ interface{}) error { return nil } @@ -199,87 +151,59 @@ type DB struct{} func (_ DB) AddQueryHook(_ QueryHook) {} -func (_ DB) Begin() (*Tx, interface { - Error() string -}) { +func (_ DB) Begin() (*Tx, error) { return nil, nil } -func (_ DB) Close() interface { - Error() string -} { +func (_ DB) Close() error { return nil } -func (_ DB) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) CreateComposite(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ DB) CreateComposite(_ interface{}, _ interface{}) error { return nil } -func (_ DB) CreateTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ DB) CreateTable(_ interface{}, _ interface{}) error { return nil } -func (_ DB) Delete(_ interface{}) interface { - Error() string -} { +func (_ DB) Delete(_ interface{}) error { return nil } -func (_ DB) DropComposite(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ DB) DropComposite(_ interface{}, _ interface{}) error { return nil } -func (_ DB) DropTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ DB) DropTable(_ interface{}, _ interface{}) error { return nil } -func (_ DB) Exec(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) Exec(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) ExecOne(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) ExecOne(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) ForceDelete(_ interface{}) interface { - Error() string -} { +func (_ DB) ForceDelete(_ interface{}) error { return nil } @@ -287,17 +211,15 @@ func (_ DB) Formatter() interface{} { return nil } -func (_ DB) Insert(_ ...interface{}) interface { - Error() string -} { +func (_ DB) Insert(_ ...interface{}) error { return nil } -func (_ DB) Model(_ ...interface{}) *interface{} { +func (_ DB) Model(_ ...interface{}) interface{} { return nil } -func (_ DB) ModelContext(_ context.Context, _ ...interface{}) *interface{} { +func (_ DB) ModelContext(_ context.Context, _ ...interface{}) interface{} { return nil } @@ -309,53 +231,35 @@ func (_ DB) PoolStats() *PoolStats { return nil } -func (_ DB) Prepare(_ string) (*Stmt, interface { - Error() string -}) { +func (_ DB) Prepare(_ string) (*Stmt, error) { return nil, nil } -func (_ DB) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ DB) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ DB) RunInTransaction(_ func(*Tx) interface { - Error() string -}) interface { - Error() string -} { +func (_ DB) RunInTransaction(_ func(*Tx) error) error { return nil } -func (_ DB) Select(_ interface{}) interface { - Error() string -} { +func (_ DB) Select(_ interface{}) error { return nil } -func (_ DB) Update(_ interface{}) interface { - Error() string -} { +func (_ DB) Update(_ interface{}) error { return nil } @@ -401,27 +305,19 @@ func (_ *Listener) ChannelSize(_ int) <-chan *Notification { return nil } -func (_ *Listener) Close() interface { - Error() string -} { +func (_ *Listener) Close() error { return nil } -func (_ *Listener) Listen(_ ...string) interface { - Error() string -} { +func (_ *Listener) Listen(_ ...string) error { return nil } -func (_ *Listener) Receive() (string, string, interface { - Error() string -}) { +func (_ *Listener) Receive() (string, string, error) { return "", "", nil } -func (_ *Listener) ReceiveTimeout(_ time.Duration) (string, string, interface { - Error() string -}) { +func (_ *Listener) ReceiveTimeout(_ time.Duration) (string, string, error) { return "", "", nil } @@ -435,22 +331,18 @@ type Notification struct { } type Options struct { - Network string - Addr string - Dialer func(context.Context, string, string) (net.Conn, interface { - Error() string - }) - User string - Password string - Database string - ApplicationName string - TLSConfig *tls.Config - DialTimeout time.Duration - ReadTimeout time.Duration - WriteTimeout time.Duration - OnConnect func(*Conn) interface { - Error() string - } + Network string + Addr string + Dialer func(context.Context, string, string) (net.Conn, error) + User string + Password string + Database string + ApplicationName string + TLSConfig *tls.Config + DialTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration + OnConnect func(*Conn) error MaxRetries int RetryStatementTimeout bool MinRetryBackoff time.Duration @@ -483,106 +375,72 @@ type QueryEvent struct { Query interface{} Params []interface{} Result interface{} - Err interface { - Error() string - } - Stash map[interface{}]interface{} + Err error + Stash map[interface{}]interface{} } -func (_ *QueryEvent) FormattedQuery() (string, interface { - Error() string -}) { +func (_ *QueryEvent) FormattedQuery() (string, error) { return "", nil } -func (_ *QueryEvent) UnformattedQuery() (string, interface { - Error() string -}) { +func (_ *QueryEvent) UnformattedQuery() (string, error) { return "", nil } type QueryHook interface { - AfterQuery(_ context.Context, _ *QueryEvent) interface { - Error() string - } - BeforeQuery(_ context.Context, _ *QueryEvent) (context.Context, interface { - Error() string - }) + AfterQuery(_ context.Context, _ *QueryEvent) error + BeforeQuery(_ context.Context, _ *QueryEvent) (context.Context, error) } type Stmt struct{} -func (_ *Stmt) Close() interface { - Error() string -} { +func (_ *Stmt) Close() error { return nil } -func (_ *Stmt) Exec(_ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) Exec(_ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) ExecContext(_ context.Context, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) ExecContext(_ context.Context, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) ExecOne(_ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) ExecOne(_ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) ExecOneContext(_ context.Context, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) ExecOneContext(_ context.Context, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) Query(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) Query(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) QueryContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) QueryContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) QueryOne(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) QueryOne(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Stmt) QueryOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Stmt) QueryOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } type Tx struct{} -func (_ *Tx) Begin() (*Tx, interface { - Error() string -}) { +func (_ *Tx) Begin() (*Tx, error) { return nil, nil } -func (_ *Tx) Close() interface { - Error() string -} { +func (_ *Tx) Close() error { return nil } -func (_ *Tx) Commit() interface { - Error() string -} { +func (_ *Tx) Commit() error { return nil } @@ -590,63 +448,43 @@ func (_ *Tx) Context() context.Context { return nil } -func (_ *Tx) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) CopyFrom(_ io.Reader, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) CopyTo(_ io.Writer, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) CreateTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ *Tx) CreateTable(_ interface{}, _ interface{}) error { return nil } -func (_ *Tx) Delete(_ interface{}) interface { - Error() string -} { +func (_ *Tx) Delete(_ interface{}) error { return nil } -func (_ *Tx) DropTable(_ interface{}, _ *interface{}) interface { - Error() string -} { +func (_ *Tx) DropTable(_ interface{}, _ interface{}) error { return nil } -func (_ *Tx) Exec(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) Exec(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) ExecContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) ExecOne(_ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) ExecOne(_ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) ExecOneContext(_ context.Context, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) ForceDelete(_ interface{}) interface { - Error() string -} { +func (_ *Tx) ForceDelete(_ interface{}) error { return nil } @@ -654,67 +492,47 @@ func (_ *Tx) Formatter() interface{} { return nil } -func (_ *Tx) Insert(_ ...interface{}) interface { - Error() string -} { +func (_ *Tx) Insert(_ ...interface{}) error { return nil } -func (_ *Tx) Model(_ ...interface{}) *interface{} { +func (_ *Tx) Model(_ ...interface{}) interface{} { return nil } -func (_ *Tx) ModelContext(_ context.Context, _ ...interface{}) *interface{} { +func (_ *Tx) ModelContext(_ context.Context, _ ...interface{}) interface{} { return nil } -func (_ *Tx) Prepare(_ string) (*Stmt, interface { - Error() string -}) { +func (_ *Tx) Prepare(_ string) (*Stmt, error) { return nil, nil } -func (_ *Tx) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) Query(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) QueryContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) QueryOne(_ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, interface { - Error() string -}) { +func (_ *Tx) QueryOneContext(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (interface{}, error) { return nil, nil } -func (_ *Tx) Rollback() interface { - Error() string -} { +func (_ *Tx) Rollback() error { return nil } -func (_ *Tx) RunInTransaction(_ func(*Tx) interface { - Error() string -}) interface { - Error() string -} { +func (_ *Tx) RunInTransaction(_ func(*Tx) error) error { return nil } -func (_ *Tx) Select(_ interface{}) interface { - Error() string -} { +func (_ *Tx) Select(_ interface{}) error { return nil } @@ -722,8 +540,6 @@ func (_ *Tx) Stmt(_ *Stmt) *Stmt { return nil } -func (_ *Tx) Update(_ interface{}) interface { - Error() string -} { +func (_ *Tx) Update(_ interface{}) error { return nil } diff --git a/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/SystemCommandExecutors.expected b/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/SystemCommandExecutors.expected index 0aaee3714bf..619b92eebb8 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/SystemCommandExecutors.expected +++ b/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/SystemCommandExecutors.expected @@ -1,55 +1,60 @@ -| SystemCommandExecutors.go:29:3:29:36 | call to StartProcess | SystemCommandExecutors.go:29:19:29:24 | source | -| SystemCommandExecutors.go:33:3:33:47 | call to StartProcess | SystemCommandExecutors.go:33:19:33:23 | shell | -| SystemCommandExecutors.go:33:3:33:47 | call to StartProcess | SystemCommandExecutors.go:33:26:33:41 | composite literal | -| SystemCommandExecutors.go:33:3:33:47 | call to StartProcess | SystemCommandExecutors.go:33:44:33:46 | nil | -| SystemCommandExecutors.go:36:3:36:64 | call to StartProcess | SystemCommandExecutors.go:36:19:36:23 | shell | -| SystemCommandExecutors.go:36:3:36:64 | call to StartProcess | SystemCommandExecutors.go:36:26:36:58 | call to append | -| SystemCommandExecutors.go:36:3:36:64 | call to StartProcess | SystemCommandExecutors.go:36:61:36:63 | nil | -| SystemCommandExecutors.go:39:3:39:61 | call to StartProcess | SystemCommandExecutors.go:39:19:39:23 | shell | -| SystemCommandExecutors.go:39:3:39:61 | call to StartProcess | SystemCommandExecutors.go:39:26:39:55 | call to append | -| SystemCommandExecutors.go:39:3:39:61 | call to StartProcess | SystemCommandExecutors.go:39:58:39:60 | nil | -| SystemCommandExecutors.go:47:3:47:57 | call to StartProcess | SystemCommandExecutors.go:47:19:47:33 | assumedNonShell | -| SystemCommandExecutors.go:50:3:50:74 | call to StartProcess | SystemCommandExecutors.go:50:19:50:33 | assumedNonShell | -| SystemCommandExecutors.go:53:3:53:82 | call to StartProcess | SystemCommandExecutors.go:53:19:53:33 | assumedNonShell | -| SystemCommandExecutors.go:59:3:59:31 | call to Command | SystemCommandExecutors.go:59:16:59:21 | source | -| SystemCommandExecutors.go:62:3:62:41 | call to Command | SystemCommandExecutors.go:62:16:62:20 | shell | -| SystemCommandExecutors.go:62:3:62:41 | call to Command | SystemCommandExecutors.go:62:23:62:26 | "a0" | -| SystemCommandExecutors.go:62:3:62:41 | call to Command | SystemCommandExecutors.go:62:29:62:32 | "a1" | -| SystemCommandExecutors.go:62:3:62:41 | call to Command | SystemCommandExecutors.go:62:35:62:40 | source | -| SystemCommandExecutors.go:65:3:65:56 | call to Command | SystemCommandExecutors.go:65:16:65:20 | shell | -| SystemCommandExecutors.go:65:3:65:56 | call to Command | SystemCommandExecutors.go:65:23:65:52 | call to append | -| SystemCommandExecutors.go:68:3:68:114 | call to Command | SystemCommandExecutors.go:68:16:68:19 | "sh" | -| SystemCommandExecutors.go:68:3:68:114 | call to Command | SystemCommandExecutors.go:68:22:68:25 | "-c" | -| SystemCommandExecutors.go:68:3:68:114 | call to Command | SystemCommandExecutors.go:68:28:68:113 | ...+... | -| SystemCommandExecutors.go:69:3:69:42 | call to Command | SystemCommandExecutors.go:69:16:69:21 | "sudo" | -| SystemCommandExecutors.go:69:3:69:42 | call to Command | SystemCommandExecutors.go:69:24:69:27 | "sh" | -| SystemCommandExecutors.go:69:3:69:42 | call to Command | SystemCommandExecutors.go:69:30:69:33 | "-c" | -| SystemCommandExecutors.go:69:3:69:42 | call to Command | SystemCommandExecutors.go:69:36:69:41 | source | -| SystemCommandExecutors.go:72:3:72:68 | call to Command | SystemCommandExecutors.go:72:16:72:21 | "ruby" | -| SystemCommandExecutors.go:72:3:72:68 | call to Command | SystemCommandExecutors.go:72:24:72:27 | "-e" | -| SystemCommandExecutors.go:72:3:72:68 | call to Command | SystemCommandExecutors.go:72:30:72:67 | call to Sprintf | -| SystemCommandExecutors.go:73:3:73:80 | call to Command | SystemCommandExecutors.go:73:16:73:21 | "perl" | -| SystemCommandExecutors.go:73:3:73:80 | call to Command | SystemCommandExecutors.go:73:24:73:27 | "-e" | -| SystemCommandExecutors.go:73:3:73:80 | call to Command | SystemCommandExecutors.go:73:30:73:79 | call to Sprintf | -| SystemCommandExecutors.go:74:3:74:86 | call to Command | SystemCommandExecutors.go:74:16:74:26 | "python2.7" | -| SystemCommandExecutors.go:74:3:74:86 | call to Command | SystemCommandExecutors.go:74:29:74:32 | "-c" | -| SystemCommandExecutors.go:74:3:74:86 | call to Command | SystemCommandExecutors.go:74:35:74:85 | call to Sprintf | -| SystemCommandExecutors.go:75:3:75:87 | call to Command | SystemCommandExecutors.go:75:16:75:27 | "python3.6m" | -| SystemCommandExecutors.go:75:3:75:87 | call to Command | SystemCommandExecutors.go:75:30:75:33 | "-c" | -| SystemCommandExecutors.go:75:3:75:87 | call to Command | SystemCommandExecutors.go:75:36:75:86 | call to Sprintf | -| SystemCommandExecutors.go:77:3:77:56 | call to Command | SystemCommandExecutors.go:77:16:77:33 | "python3.7-config" | -| SystemCommandExecutors.go:78:3:78:44 | call to Command | SystemCommandExecutors.go:78:16:78:28 | "python3-pbr" | -| SystemCommandExecutors.go:81:3:81:56 | call to Command | SystemCommandExecutors.go:81:16:81:20 | "ssh" | -| SystemCommandExecutors.go:81:3:81:56 | call to Command | SystemCommandExecutors.go:81:23:81:26 | "-t" | -| SystemCommandExecutors.go:81:3:81:56 | call to Command | SystemCommandExecutors.go:81:29:81:39 | "user@host" | -| SystemCommandExecutors.go:81:3:81:56 | call to Command | SystemCommandExecutors.go:81:42:81:55 | ...+... | -| SystemCommandExecutors.go:86:3:86:32 | call to CombinedOutput | SystemCommandExecutors.go:86:26:86:31 | source | -| SystemCommandExecutors.go:87:3:87:24 | call to Output | SystemCommandExecutors.go:87:18:87:23 | source | -| SystemCommandExecutors.go:88:3:88:21 | call to Run | SystemCommandExecutors.go:88:15:88:20 | source | -| SystemCommandExecutors.go:89:3:89:23 | call to Start | SystemCommandExecutors.go:89:17:89:22 | source | -| SystemCommandExecutors.go:93:3:93:83 | call to Command | SystemCommandExecutors.go:93:14:93:18 | shell | -| SystemCommandExecutors.go:93:3:93:83 | call to Command | SystemCommandExecutors.go:93:21:93:79 | call to toInterfaceArray | -| SystemCommandExecutors.go:94:3:94:101 | call to Call | SystemCommandExecutors.go:94:32:94:36 | shell | -| SystemCommandExecutors.go:94:3:94:101 | call to Call | SystemCommandExecutors.go:94:39:94:97 | call to toInterfaceArray | -| SystemCommandExecutors.go:95:3:95:104 | call to Command | SystemCommandExecutors.go:95:35:95:39 | shell | -| SystemCommandExecutors.go:95:3:95:104 | call to Command | SystemCommandExecutors.go:95:42:95:100 | call to toInterfaceArray | +| SystemCommandExecutors.go:30:3:30:36 | call to StartProcess | SystemCommandExecutors.go:30:19:30:24 | source | +| SystemCommandExecutors.go:34:3:34:47 | call to StartProcess | SystemCommandExecutors.go:34:19:34:23 | shell | +| SystemCommandExecutors.go:34:3:34:47 | call to StartProcess | SystemCommandExecutors.go:34:26:34:41 | composite literal | +| SystemCommandExecutors.go:34:3:34:47 | call to StartProcess | SystemCommandExecutors.go:34:44:34:46 | nil | +| SystemCommandExecutors.go:37:3:37:64 | call to StartProcess | SystemCommandExecutors.go:37:19:37:23 | shell | +| SystemCommandExecutors.go:37:3:37:64 | call to StartProcess | SystemCommandExecutors.go:37:26:37:58 | call to append | +| SystemCommandExecutors.go:37:3:37:64 | call to StartProcess | SystemCommandExecutors.go:37:61:37:63 | nil | +| SystemCommandExecutors.go:40:3:40:61 | call to StartProcess | SystemCommandExecutors.go:40:19:40:23 | shell | +| SystemCommandExecutors.go:40:3:40:61 | call to StartProcess | SystemCommandExecutors.go:40:26:40:55 | call to append | +| SystemCommandExecutors.go:40:3:40:61 | call to StartProcess | SystemCommandExecutors.go:40:58:40:60 | nil | +| SystemCommandExecutors.go:48:3:48:57 | call to StartProcess | SystemCommandExecutors.go:48:19:48:33 | assumedNonShell | +| SystemCommandExecutors.go:51:3:51:74 | call to StartProcess | SystemCommandExecutors.go:51:19:51:33 | assumedNonShell | +| SystemCommandExecutors.go:54:3:54:82 | call to StartProcess | SystemCommandExecutors.go:54:19:54:33 | assumedNonShell | +| SystemCommandExecutors.go:60:3:60:31 | call to Command | SystemCommandExecutors.go:60:16:60:21 | source | +| SystemCommandExecutors.go:63:3:63:41 | call to Command | SystemCommandExecutors.go:63:16:63:20 | shell | +| SystemCommandExecutors.go:63:3:63:41 | call to Command | SystemCommandExecutors.go:63:23:63:26 | "a0" | +| SystemCommandExecutors.go:63:3:63:41 | call to Command | SystemCommandExecutors.go:63:29:63:32 | "a1" | +| SystemCommandExecutors.go:63:3:63:41 | call to Command | SystemCommandExecutors.go:63:35:63:40 | source | +| SystemCommandExecutors.go:66:3:66:56 | call to Command | SystemCommandExecutors.go:66:16:66:20 | shell | +| SystemCommandExecutors.go:66:3:66:56 | call to Command | SystemCommandExecutors.go:66:23:66:52 | call to append | +| SystemCommandExecutors.go:69:3:69:114 | call to Command | SystemCommandExecutors.go:69:16:69:19 | "sh" | +| SystemCommandExecutors.go:69:3:69:114 | call to Command | SystemCommandExecutors.go:69:22:69:25 | "-c" | +| SystemCommandExecutors.go:69:3:69:114 | call to Command | SystemCommandExecutors.go:69:28:69:113 | ...+... | +| SystemCommandExecutors.go:70:3:70:42 | call to Command | SystemCommandExecutors.go:70:16:70:21 | "sudo" | +| SystemCommandExecutors.go:70:3:70:42 | call to Command | SystemCommandExecutors.go:70:24:70:27 | "sh" | +| SystemCommandExecutors.go:70:3:70:42 | call to Command | SystemCommandExecutors.go:70:30:70:33 | "-c" | +| SystemCommandExecutors.go:70:3:70:42 | call to Command | SystemCommandExecutors.go:70:36:70:41 | source | +| SystemCommandExecutors.go:73:3:73:68 | call to Command | SystemCommandExecutors.go:73:16:73:21 | "ruby" | +| SystemCommandExecutors.go:73:3:73:68 | call to Command | SystemCommandExecutors.go:73:24:73:27 | "-e" | +| SystemCommandExecutors.go:73:3:73:68 | call to Command | SystemCommandExecutors.go:73:30:73:67 | call to Sprintf | +| SystemCommandExecutors.go:74:3:74:80 | call to Command | SystemCommandExecutors.go:74:16:74:21 | "perl" | +| SystemCommandExecutors.go:74:3:74:80 | call to Command | SystemCommandExecutors.go:74:24:74:27 | "-e" | +| SystemCommandExecutors.go:74:3:74:80 | call to Command | SystemCommandExecutors.go:74:30:74:79 | call to Sprintf | +| SystemCommandExecutors.go:75:3:75:86 | call to Command | SystemCommandExecutors.go:75:16:75:26 | "python2.7" | +| SystemCommandExecutors.go:75:3:75:86 | call to Command | SystemCommandExecutors.go:75:29:75:32 | "-c" | +| SystemCommandExecutors.go:75:3:75:86 | call to Command | SystemCommandExecutors.go:75:35:75:85 | call to Sprintf | +| SystemCommandExecutors.go:76:3:76:87 | call to Command | SystemCommandExecutors.go:76:16:76:27 | "python3.6m" | +| SystemCommandExecutors.go:76:3:76:87 | call to Command | SystemCommandExecutors.go:76:30:76:33 | "-c" | +| SystemCommandExecutors.go:76:3:76:87 | call to Command | SystemCommandExecutors.go:76:36:76:86 | call to Sprintf | +| SystemCommandExecutors.go:78:3:78:56 | call to Command | SystemCommandExecutors.go:78:16:78:33 | "python3.7-config" | +| SystemCommandExecutors.go:79:3:79:44 | call to Command | SystemCommandExecutors.go:79:16:79:28 | "python3-pbr" | +| SystemCommandExecutors.go:82:3:82:56 | call to Command | SystemCommandExecutors.go:82:16:82:20 | "ssh" | +| SystemCommandExecutors.go:82:3:82:56 | call to Command | SystemCommandExecutors.go:82:23:82:26 | "-t" | +| SystemCommandExecutors.go:82:3:82:56 | call to Command | SystemCommandExecutors.go:82:29:82:39 | "user@host" | +| SystemCommandExecutors.go:82:3:82:56 | call to Command | SystemCommandExecutors.go:82:42:82:55 | ...+... | +| SystemCommandExecutors.go:87:3:87:32 | call to CombinedOutput | SystemCommandExecutors.go:87:26:87:31 | source | +| SystemCommandExecutors.go:88:3:88:24 | call to Output | SystemCommandExecutors.go:88:18:88:23 | source | +| SystemCommandExecutors.go:89:3:89:21 | call to Run | SystemCommandExecutors.go:89:15:89:20 | source | +| SystemCommandExecutors.go:90:3:90:23 | call to Start | SystemCommandExecutors.go:90:17:90:22 | source | +| SystemCommandExecutors.go:94:3:94:83 | call to Command | SystemCommandExecutors.go:94:14:94:18 | shell | +| SystemCommandExecutors.go:94:3:94:83 | call to Command | SystemCommandExecutors.go:94:21:94:79 | call to toInterfaceArray | +| SystemCommandExecutors.go:95:3:95:101 | call to Call | SystemCommandExecutors.go:95:32:95:36 | shell | +| SystemCommandExecutors.go:95:3:95:101 | call to Call | SystemCommandExecutors.go:95:39:95:97 | call to toInterfaceArray | +| SystemCommandExecutors.go:96:3:96:104 | call to Command | SystemCommandExecutors.go:96:35:96:39 | shell | +| SystemCommandExecutors.go:96:3:96:104 | call to Command | SystemCommandExecutors.go:96:42:96:100 | call to toInterfaceArray | +| SystemCommandExecutors.go:100:3:100:60 | call to Exec | SystemCommandExecutors.go:100:16:100:21 | source | +| SystemCommandExecutors.go:101:3:101:77 | call to StartProcess | SystemCommandExecutors.go:101:24:101:29 | source | +| SystemCommandExecutors.go:103:3:103:76 | call to StartProcess | SystemCommandExecutors.go:103:24:103:28 | shell | +| SystemCommandExecutors.go:103:3:103:76 | call to StartProcess | SystemCommandExecutors.go:103:31:103:54 | composite literal | +| SystemCommandExecutors.go:103:3:103:76 | call to StartProcess | SystemCommandExecutors.go:103:57:103:75 | &... | diff --git a/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/SystemCommandExecutors.go b/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/SystemCommandExecutors.go index 94f3748225b..2c184f3b00f 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/SystemCommandExecutors.go +++ b/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/SystemCommandExecutors.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "os/exec" + "syscall" sh "github.com/codeskyblue/go-sh" "golang.org/x/crypto/ssh" @@ -94,6 +95,13 @@ func handler(w http.ResponseWriter, req *http.Request) { sh.InteractiveSession().Call(shell, toInterfaceArray(append([]string{assumedNonShell}, source))...) sh.InteractiveSession().Command(shell, toInterfaceArray(append([]string{assumedNonShell}, source))...) } + // syscall + { + syscall.Exec(source, []string{"arg1", "arg2"}, []string{}) + syscall.StartProcess(source, []string{"arg1", "arg2"}, &syscall.ProcAttr{}) + + syscall.StartProcess(shell, []string{source, "arg2"}, &syscall.ProcAttr{}) + } } func toInterfaceArray(strs []string) []interface{} { res := make([]interface{}, 0) diff --git a/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/go.mod b/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/go.mod index 7e5ad05ed01..a38d4a4518b 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/go.mod +++ b/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/go.mod @@ -4,6 +4,5 @@ go 1.14 require ( github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 - github.com/github/depstubber v0.0.0-20200414025517-59c2db8e9405 // indirect golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 ) diff --git a/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/vendor/modules.txt index 768df0566ee..2fdb2ae4eae 100644 --- a/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/vendor/modules.txt +++ b/ql/test/library-tests/semmle/go/frameworks/SystemCommandExecutors/vendor/modules.txt @@ -1,9 +1,6 @@ # github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 ## explicit github.com/codeskyblue/go-sh -# github.com/github/depstubber v0.0.0-20200414025517-59c2db8e9405 -## explicit -github.com/github/depstubber # golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 ## explicit golang.org/x/crypto diff --git a/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected b/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected index d912d745d7a..eeb8e85e203 100644 --- a/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected +++ b/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected @@ -5,6 +5,54 @@ | crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | ... := ...[0] | | crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | ... := ...[1] | | crypto.go:11:42:11:51 | ciphertext | crypto.go:11:2:11:57 | ... := ...[0] | +| io.go:15:3:15:3 | definition of w | io.go:15:23:15:27 | &... | +| io.go:15:3:15:3 | definition of w | io.go:15:30:15:34 | &... | +| io.go:15:23:15:27 | &... | io.go:14:7:14:10 | definition of buf1 | +| io.go:15:24:15:27 | buf1 | io.go:15:23:15:27 | &... | +| io.go:15:30:15:34 | &... | io.go:14:13:14:16 | definition of buf2 | +| io.go:15:31:15:34 | buf2 | io.go:15:30:15:34 | &... | +| io.go:17:14:17:19 | reader | io.go:15:3:15:3 | definition of w | +| io.go:24:19:24:23 | &... | io.go:22:7:22:10 | definition of buf1 | +| io.go:24:20:24:23 | buf1 | io.go:24:19:24:23 | &... | +| io.go:26:21:26:26 | reader | io.go:24:3:24:4 | definition of w2 | +| io.go:32:19:32:23 | &... | io.go:31:7:31:10 | definition of buf1 | +| io.go:32:20:32:23 | buf1 | io.go:32:19:32:23 | &... | +| io.go:34:16:34:21 | reader | io.go:32:3:32:4 | definition of w2 | +| io.go:38:3:38:3 | definition of r | io.go:38:3:38:19 | ... := ...[1] | +| io.go:38:11:38:19 | call to Pipe | io.go:38:3:38:19 | ... := ...[0] | +| io.go:38:11:38:19 | call to Pipe | io.go:38:3:38:19 | ... := ...[1] | +| io.go:39:17:39:31 | "some string\\n" | io.go:38:6:38:6 | definition of w | +| io.go:42:16:42:16 | r | io.go:41:3:41:5 | definition of buf | +| io.go:43:13:43:15 | buf | io.go:43:13:43:24 | call to String | +| io.go:49:18:49:23 | reader | io.go:48:3:48:5 | definition of buf | +| io.go:55:15:55:20 | reader | io.go:54:3:54:5 | definition of buf | +| io.go:60:18:60:21 | &... | io.go:59:7:59:9 | definition of buf | +| io.go:60:19:60:21 | buf | io.go:60:18:60:21 | &... | +| io.go:61:21:61:26 | "test" | io.go:60:3:60:3 | definition of w | +| io.go:66:11:66:16 | reader | io.go:66:3:66:27 | ... := ...[0] | +| io.go:66:11:66:27 | call to ReadByte | io.go:66:3:66:27 | ... := ...[0] | +| io.go:66:11:66:27 | call to ReadByte | io.go:66:3:66:27 | ... := ...[1] | +| io.go:68:21:68:21 | t | io.go:67:7:67:13 | definition of bwriter | +| io.go:74:3:74:8 | reader | io.go:73:3:73:5 | definition of buf | +| io.go:79:3:79:8 | reader | io.go:78:3:78:5 | definition of buf | +| io.go:84:24:84:29 | reader | io.go:84:9:84:33 | call to LimitReader | +| io.go:85:22:85:23 | lr | io.go:85:11:85:19 | selection of Stdout | +| io.go:92:23:92:24 | r1 | io.go:92:8:92:33 | call to MultiReader | +| io.go:92:27:92:28 | r2 | io.go:92:8:92:33 | call to MultiReader | +| io.go:92:31:92:32 | r3 | io.go:92:8:92:33 | call to MultiReader | +| io.go:93:22:93:22 | r | io.go:93:11:93:19 | selection of Stdout | +| io.go:98:23:98:23 | r | io.go:98:10:98:30 | call to TeeReader | +| io.go:98:23:98:23 | r | io.go:98:26:98:29 | &... | +| io.go:98:26:98:29 | &... | io.go:97:7:97:9 | definition of buf | +| io.go:98:27:98:29 | buf | io.go:98:26:98:29 | &... | +| io.go:100:22:100:24 | tee | io.go:100:11:100:19 | selection of Stdout | +| io.go:104:28:104:28 | r | io.go:104:8:104:36 | call to NewSectionReader | +| io.go:105:22:105:22 | s | io.go:105:11:105:19 | selection of Stdout | +| io.go:109:16:109:16 | r | io.go:109:3:109:27 | ... := ...[0] | +| io.go:109:16:109:27 | call to ReadRune | io.go:109:3:109:27 | ... := ...[0] | +| io.go:109:16:109:27 | call to ReadRune | io.go:109:3:109:27 | ... := ...[1] | +| io.go:109:16:109:27 | call to ReadRune | io.go:109:3:109:27 | ... := ...[2] | +| io.go:114:3:114:3 | r | io.go:114:13:114:21 | selection of Stdout | | main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | ... := ...[0] | | main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | ... := ...[1] | | main.go:11:25:11:25 | v | main.go:11:2:11:26 | ... := ...[0] | diff --git a/ql/test/library-tests/semmle/go/frameworks/TaintSteps/io.go b/ql/test/library-tests/semmle/go/frameworks/TaintSteps/io.go new file mode 100644 index 00000000000..82aa4d377c5 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/TaintSteps/io.go @@ -0,0 +1,117 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" +) + +func io2() { + { + reader := strings.NewReader("some string") + var buf1, buf2 bytes.Buffer + w := io.MultiWriter(&buf1, &buf2) + + io.Copy(w, reader) + } + + { + reader := strings.NewReader("some string") + var buf1 bytes.Buffer + buf := make([]byte, 512) + w2 := io.Writer(&buf1) + + io.CopyBuffer(w2, reader, buf) + } + + { + reader := strings.NewReader("some string") + var buf1 bytes.Buffer + w2 := io.Writer(&buf1) + + io.CopyN(w2, reader, 512) + } + + { + r, w := io.Pipe() + fmt.Fprint(w, "some string\n") + + buf := new(bytes.Buffer) + buf.ReadFrom(r) + fmt.Print(buf.String()) + } + + { + reader := strings.NewReader("some string") + buf := make([]byte, 512) + io.ReadAtLeast(reader, buf, 512) + } + + { + reader := strings.NewReader("some string") + buf := make([]byte, 512) + io.ReadFull(reader, buf) + } + + { + var buf bytes.Buffer + w := io.Writer(&buf) + io.WriteString(w, "test") + } + + { + reader := strings.NewReader("some string") + t, _ := reader.ReadByte() + var bwriter io.ByteWriter + bwriter.WriteByte(t) + } + + { + reader := strings.NewReader("some string") + buf := make([]byte, 512) + reader.Read(buf) + } + { + reader := strings.NewReader("some string") + buf := make([]byte, 512) + reader.ReadAt(buf, 10) + } + + { + reader := strings.NewReader("some string") + lr := io.LimitReader(reader, 4) + io.Copy(os.Stdout, lr) + } + + { + r1 := strings.NewReader("reader1 ") + r2 := strings.NewReader("reader2 ") + r3 := strings.NewReader("reader3") + r := io.MultiReader(r1, r2, r3) + io.Copy(os.Stdout, r) + } + { + r := strings.NewReader("some string") + var buf bytes.Buffer + tee := io.TeeReader(r, &buf) + + io.Copy(os.Stdout, tee) + } + { + r := strings.NewReader("some string") + s := io.NewSectionReader(r, 5, 17) + io.Copy(os.Stdout, s) + } + { + r := strings.NewReader("some string") + run, _, _ := r.ReadRune() + fmt.Println(run) + } + { + r := strings.NewReader("some string") + r.WriteTo(os.Stdout) + } + +} diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.expected b/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.expected new file mode 100644 index 00000000000..58d63f6c496 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.expected @@ -0,0 +1,13 @@ +| DialFunction.go:21:2:21:43 | call to Dial | DialFunction.go:21:17:21:30 | untrustedInput | +| DialFunction.go:24:2:24:29 | call to DialConfig | DialFunction.go:23:35:23:48 | untrustedInput | +| DialFunction.go:26:2:26:49 | call to Dial | DialFunction.go:26:30:26:43 | untrustedInput | +| DialFunction.go:29:2:29:33 | call to Dial | DialFunction.go:29:14:29:27 | untrustedInput | +| DialFunction.go:31:2:31:56 | call to DialContext | DialFunction.go:31:37:31:50 | untrustedInput | +| DialFunction.go:33:2:33:44 | call to Dial | DialFunction.go:33:30:33:43 | untrustedInput | +| DialFunction.go:36:2:36:45 | call to Dial | DialFunction.go:36:31:36:44 | untrustedInput | +| DialFunction.go:38:2:38:31 | call to BuildProxy | DialFunction.go:38:17:38:30 | untrustedInput | +| DialFunction.go:39:2:39:24 | call to New | DialFunction.go:39:10:39:23 | untrustedInput | +| WebSocketReadWrite.go:30:12:30:42 | call to Dial | WebSocketReadWrite.go:30:27:30:29 | uri | +| WebSocketReadWrite.go:40:14:40:50 | call to Dial | WebSocketReadWrite.go:40:42:40:44 | uri | +| WebSocketReadWrite.go:50:17:50:37 | call to Dial | WebSocketReadWrite.go:50:29:50:31 | uri | +| WebSocketReadWrite.go:66:20:66:51 | call to Dial | WebSocketReadWrite.go:66:48:66:50 | uri | diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.go b/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.go new file mode 100644 index 00000000000..6943404ec65 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.go @@ -0,0 +1,40 @@ +package main + +//go:generate depstubber -vendor github.com/sacOO7/gowebsocket "" New,BuildProxy + +import ( + "context" + + gobwas "github.com/gobwas/ws" + gorilla "github.com/gorilla/websocket" + sac "github.com/sacOO7/gowebsocket" + "golang.org/x/net/websocket" + nhooyr "nhooyr.io/websocket" +) + +func main() { + untrustedInput := "referrer" + + origin := "http://localhost/" + + // bad as input is directly passed to dial function + websocket.Dial(untrustedInput, "", origin) + + config, _ := websocket.NewConfig(untrustedInput, origin) // good + websocket.DialConfig(config) + + nhooyr.Dial(context.TODO(), untrustedInput, nil) + + dialer := gorilla.Dialer{} + dialer.Dial(untrustedInput, nil) + + dialer.DialContext(context.TODO(), untrustedInput, nil) + + gobwas.Dial(context.TODO(), untrustedInput) + + dialer2 := gobwas.Dialer{} + dialer2.Dial(context.TODO(), untrustedInput) + + sac.BuildProxy(untrustedInput) + sac.New(untrustedInput) +} diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.ql b/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.ql new file mode 100644 index 00000000000..d6545ca87e6 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/DialFunction.ql @@ -0,0 +1,4 @@ +import go + +from WebSocketRequestCall::Range r +select r, r.getRequestUrl() diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/Read.expected b/ql/test/library-tests/semmle/go/frameworks/WebSocket/Read.expected new file mode 100644 index 00000000000..4bfef26418a --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/Read.expected @@ -0,0 +1,8 @@ +| WebSocketReadWrite.go:31:7:31:10 | definition of xnet | +| WebSocketReadWrite.go:35:3:35:7 | definition of xnet2 | +| WebSocketReadWrite.go:41:3:41:40 | ... := ...[1] | +| WebSocketReadWrite.go:44:3:44:48 | ... := ...[1] | +| WebSocketReadWrite.go:51:7:51:16 | definition of gorillaMsg | +| WebSocketReadWrite.go:55:3:55:10 | definition of gorilla2 | +| WebSocketReadWrite.go:61:3:61:38 | ... := ...[1] | +| WebSocketReadWrite.go:67:3:67:36 | ... := ...[0] | diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/Read.ql b/ql/test/library-tests/semmle/go/frameworks/WebSocket/Read.ql new file mode 100644 index 00000000000..3c163eb4337 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/Read.ql @@ -0,0 +1,6 @@ +import go +import semmle.go.frameworks.WebSocket + +from WebSocketReader r, DataFlow::Node nd +where nd = r.getAnOutput().getNode(r.getACall()) +select nd diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/WebSocketReadWrite.go b/ql/test/library-tests/semmle/go/frameworks/WebSocket/WebSocketReadWrite.go new file mode 100644 index 00000000000..6cb2692a6cf --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/WebSocketReadWrite.go @@ -0,0 +1,74 @@ +package main + +//go:generate depstubber -vendor github.com/gobwas/ws Dialer ReadFrame,WriteFrame,NewTextFrame,Dial +//go:generate depstubber -vendor github.com/gorilla/websocket Dialer ReadJSON,WriteJSON,NewPreparedMessage +//go:generate depstubber -vendor golang.org/x/net/websocket Codec Dial,NewConfig,DialConfig +//go:generate depstubber -vendor nhooyr.io/websocket "" Dial + +import ( + "context" + "io" + "net/http" + + gobwas "github.com/gobwas/ws" + gorilla "github.com/gorilla/websocket" + websocket "golang.org/x/net/websocket" + nhooyr "nhooyr.io/websocket" +) + +func marshal(v interface{}) (data []byte, payloadType byte, err error) { + return nil, 0, nil +} +func unmarshal(data []byte, payloadType byte, v interface{}) (err error) { + return nil +} + +func xss(w http.ResponseWriter, r *http.Request) { + uri := r.Header.Get("X-Header") + origin := "test" + { + ws, _ := websocket.Dial(uri, "", origin) + var xnet = make([]byte, 512) + ws.Read(xnet) + ws.Write([]byte(xnet)) + codec := &websocket.Codec{marshal, unmarshal} + xnet2 := make([]byte, 512) + codec.Receive(ws, xnet2) + codec.Send(ws, []byte(xnet2)) + } + { + n, _, _ := nhooyr.Dial(context.TODO(), uri, nil) + _, nhooyr, _ := n.Read(context.TODO()) + n.Write(context.TODO(), 0, nhooyr) + + _, nhooyrReader, _ := n.Reader(context.TODO()) + writer, _ := n.Writer(context.TODO(), 0) + io.Copy(writer, nhooyrReader) + } + { + dialer := gorilla.Dialer{} + conn, _, _ := dialer.Dial(uri, nil) + var gorillaMsg = make([]byte, 512) + gorilla.ReadJSON(conn, gorillaMsg) + gorilla.WriteJSON(conn, gorillaMsg) + + gorilla2 := make([]byte, 512) + conn.ReadJSON(gorilla2) + pm, _ := gorilla.NewPreparedMessage(0, gorilla2) + conn.WritePreparedMessage(pm) + conn.WriteJSON(gorilla2) + + _, gorilla3, _ := conn.ReadMessage() + conn.WriteMessage(0, gorilla3) + + } + { + conn, _, _, _ := gobwas.Dial(context.TODO(), uri) + frame, _ := gobwas.ReadFrame(conn) + + gobwas.WriteFrame(conn, frame) + + resp := gobwas.NewTextFrame([]byte(uri)) + gobwas.WriteFrame(conn, resp) + } +} diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/go.mod b/ql/test/library-tests/semmle/go/frameworks/WebSocket/go.mod new file mode 100644 index 00000000000..8faf9ebd264 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/go.mod @@ -0,0 +1,11 @@ +module codeql-go-tests/frameworks/WebSocket + +go 1.14 + +require ( + github.com/gobwas/ws v1.0.3 + github.com/gorilla/websocket v1.4.2 + github.com/sacOO7/gowebsocket v0.0.0-20180719182212-1436bb906a4e + golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + nhooyr.io/websocket v1.8.5 +) diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gobwas/ws/LICENSE b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gobwas/ws/LICENSE new file mode 100644 index 00000000000..d2611fddf55 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gobwas/ws/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2018 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gobwas/ws/stub.go b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gobwas/ws/stub.go new file mode 100644 index 00000000000..f4919cdd659 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gobwas/ws/stub.go @@ -0,0 +1,106 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gobwas/ws, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gobwas/ws (exports: Dialer; functions: ReadFrame,WriteFrame,NewTextFrame,Dial) + +// Package ws is a stub of github.com/gobwas/ws, generated by depstubber. +package ws + +import ( + bufio "bufio" + context "context" + tls "crypto/tls" + io "io" + net "net" + url "net/url" + time "time" +) + +func Dial(_ context.Context, _ string) (net.Conn, *bufio.Reader, Handshake, error) { + return nil, nil, Handshake{}, nil +} + +type Dialer struct { + ReadBufferSize int + WriteBufferSize int + Timeout time.Duration + Protocols []string + Extensions []interface{} + Header HandshakeHeader + OnStatusError func(int, []byte, io.Reader) + OnHeader func([]byte, []byte) error + NetDial func(context.Context, string, string) (net.Conn, error) + TLSClient func(net.Conn, string) net.Conn + TLSConfig *tls.Config + WrapConn func(net.Conn) net.Conn +} + +func (_ Dialer) Dial(_ context.Context, _ string) (net.Conn, *bufio.Reader, Handshake, error) { + return nil, nil, Handshake{}, nil +} + +func (_ Dialer) Upgrade(_ io.ReadWriter, _ *url.URL) (*bufio.Reader, Handshake, error) { + return nil, Handshake{}, nil +} + +type Frame struct { + Header Header + Payload []byte +} + +type Handshake struct { + Protocol string + Extensions []interface{} +} + +type HandshakeHeader interface { + WriteTo(_ io.Writer) (int64, error) +} + +type Header struct { + Fin bool + Rsv byte + OpCode OpCode + Masked bool + Mask [4]byte + Length int64 +} + +func (_ Header) Rsv1() bool { + return false +} + +func (_ Header) Rsv2() bool { + return false +} + +func (_ Header) Rsv3() bool { + return false +} + +func NewTextFrame(_ []byte) Frame { + return Frame{} +} + +type OpCode uint8 + +func (_ OpCode) IsControl() bool { + return false +} + +func (_ OpCode) IsData() bool { + return false +} + +func (_ OpCode) IsReserved() bool { + return false +} + +func ReadFrame(_ io.Reader) (Frame, error) { + return Frame{}, nil +} + +func WriteFrame(_ io.Writer, _ Frame) error { + return nil +} diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gorilla/websocket/LICENSE b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gorilla/websocket/LICENSE new file mode 100644 index 00000000000..9171c972252 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gorilla/websocket/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gorilla/websocket/stub.go b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gorilla/websocket/stub.go new file mode 100644 index 00000000000..c9beb63767a --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/gorilla/websocket/stub.go @@ -0,0 +1,147 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gorilla/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gorilla/websocket (exports: Dialer; functions: ReadJSON,WriteJSON,NewPreparedMessage) + +// Package websocket is a stub of github.com/gorilla/websocket, generated by depstubber. +package websocket + +import ( + context "context" + tls "crypto/tls" + io "io" + net "net" + http "net/http" + url "net/url" + time "time" +) + +type BufferPool interface { + Get() interface{} + Put(_ interface{}) +} + +type Conn struct{} + +func (_ *Conn) Close() error { + return nil +} + +func (_ *Conn) CloseHandler() func(int, string) error { + return nil +} + +func (_ *Conn) EnableWriteCompression(_ bool) {} + +func (_ *Conn) LocalAddr() net.Addr { + return nil +} + +func (_ *Conn) NextReader() (int, io.Reader, error) { + return 0, nil, nil +} + +func (_ *Conn) NextWriter(_ int) (io.WriteCloser, error) { + return nil, nil +} + +func (_ *Conn) PingHandler() func(string) error { + return nil +} + +func (_ *Conn) PongHandler() func(string) error { + return nil +} + +func (_ *Conn) ReadJSON(_ interface{}) error { + return nil +} + +func (_ *Conn) ReadMessage() (int, []byte, error) { + return 0, nil, nil +} + +func (_ *Conn) RemoteAddr() net.Addr { + return nil +} + +func (_ *Conn) SetCloseHandler(_ func(int, string) error) {} + +func (_ *Conn) SetCompressionLevel(_ int) error { + return nil +} + +func (_ *Conn) SetPingHandler(_ func(string) error) {} + +func (_ *Conn) SetPongHandler(_ func(string) error) {} + +func (_ *Conn) SetReadDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetReadLimit(_ int64) {} + +func (_ *Conn) SetWriteDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) Subprotocol() string { + return "" +} + +func (_ *Conn) UnderlyingConn() net.Conn { + return nil +} + +func (_ *Conn) WriteControl(_ int, _ []byte, _ time.Time) error { + return nil +} + +func (_ *Conn) WriteJSON(_ interface{}) error { + return nil +} + +func (_ *Conn) WriteMessage(_ int, _ []byte) error { + return nil +} + +func (_ *Conn) WritePreparedMessage(_ *PreparedMessage) error { + return nil +} + +type Dialer struct { + NetDial func(string, string) (net.Conn, error) + NetDialContext func(context.Context, string, string) (net.Conn, error) + Proxy func(*http.Request) (*url.URL, error) + TLSClientConfig *tls.Config + HandshakeTimeout time.Duration + ReadBufferSize int + WriteBufferSize int + WriteBufferPool BufferPool + Subprotocols []string + EnableCompression bool + Jar http.CookieJar +} + +func (_ *Dialer) Dial(_ string, _ http.Header) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +func (_ *Dialer) DialContext(_ context.Context, _ string, _ http.Header) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +func NewPreparedMessage(_ int, _ []byte) (*PreparedMessage, error) { + return nil, nil +} + +type PreparedMessage struct{} + +func ReadJSON(_ *Conn, _ interface{}) error { + return nil +} + +func WriteJSON(_ *Conn, _ interface{}) error { + return nil +} diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/sacOO7/gowebsocket/LICENSE b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/sacOO7/gowebsocket/LICENSE new file mode 100644 index 00000000000..95ab2c9a687 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/sacOO7/gowebsocket/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Sachin Shinde + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/sacOO7/gowebsocket/stub.go b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/sacOO7/gowebsocket/stub.go new file mode 100644 index 00000000000..f1f20c86857 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/github.com/sacOO7/gowebsocket/stub.go @@ -0,0 +1,58 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/sacOO7/gowebsocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/sacOO7/gowebsocket (exports: ; functions: New,BuildProxy) + +// Package gowebsocket is a stub of github.com/sacOO7/gowebsocket, generated by depstubber. +package gowebsocket + +import ( + http "net/http" + url "net/url" +) + +func BuildProxy(_ string) func(*http.Request) (*url.URL, error) { + return nil +} + +type ConnectionOptions struct { + UseCompression bool + UseSSL bool + Proxy func(*http.Request) (*url.URL, error) + Subprotocols []string +} + +func New(_ string) Socket { + return Socket{} +} + +type Socket struct { + Conn interface{} + WebsocketDialer interface{} + Url string + ConnectionOptions ConnectionOptions + RequestHeader http.Header + OnConnected func(Socket) + OnTextMessage func(string, Socket) + OnBinaryMessage func([]byte, Socket) + OnConnectError func(error, Socket) + OnDisconnected func(error, Socket) + OnPingReceived func(string, Socket) + OnPongReceived func(string, Socket) + IsConnected bool +} + +func (_ Socket) EnableLogging() {} + +func (_ Socket) GetLogger() interface{} { + return nil +} + +func (_ *Socket) Close() {} + +func (_ *Socket) Connect() {} + +func (_ *Socket) SendBinary(_ []byte) {} + +func (_ *Socket) SendText(_ string) {} diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/golang.org/x/net/websocket/LICENSE b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/golang.org/x/net/websocket/LICENSE new file mode 100644 index 00000000000..6a66aea5eaf --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/golang.org/x/net/websocket/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/golang.org/x/net/websocket/stub.go b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/golang.org/x/net/websocket/stub.go new file mode 100644 index 00000000000..1bd9aef8da5 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/golang.org/x/net/websocket/stub.go @@ -0,0 +1,133 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for golang.org/x/net/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: golang.org/x/net/websocket (exports: Codec; functions: Dial,NewConfig,DialConfig) + +// Package websocket is a stub of golang.org/x/net/websocket, generated by depstubber. +package websocket + +import ( + tls "crypto/tls" + io "io" + net "net" + http "net/http" + url "net/url" + time "time" +) + +type Codec struct { + Marshal func(interface{}) ([]byte, byte, error) + Unmarshal func([]byte, byte, interface{}) error +} + +func (_ Codec) Receive(_ *Conn, _ interface{}) error { + return nil +} + +func (_ Codec) Send(_ *Conn, _ interface{}) error { + return nil +} + +type Config struct { + Location *url.URL + Origin *url.URL + Protocol []string + Version int + TlsConfig *tls.Config + Header http.Header + Dialer *net.Dialer +} + +type Conn struct { + PayloadType byte + MaxPayloadBytes int +} + +func (_ Conn) HandleFrame(_ interface{}) (interface{}, error) { + return nil, nil +} + +func (_ Conn) HeaderReader() io.Reader { + return nil +} + +func (_ Conn) Len() int { + return 0 +} + +func (_ Conn) NewFrameReader() (interface{}, error) { + return nil, nil +} + +func (_ Conn) NewFrameWriter(_ byte) (interface{}, error) { + return nil, nil +} + +func (_ Conn) TrailerReader() io.Reader { + return nil +} + +func (_ Conn) WriteClose(_ int) error { + return nil +} + +func (_ *Conn) Close() error { + return nil +} + +func (_ *Conn) Config() *Config { + return nil +} + +func (_ *Conn) IsClientConn() bool { + return false +} + +func (_ *Conn) IsServerConn() bool { + return false +} + +func (_ *Conn) LocalAddr() net.Addr { + return nil +} + +func (_ *Conn) Read(_ []byte) (int, error) { + return 0, nil +} + +func (_ *Conn) RemoteAddr() net.Addr { + return nil +} + +func (_ *Conn) Request() *http.Request { + return nil +} + +func (_ *Conn) SetDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetReadDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetWriteDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) Write(_ []byte) (int, error) { + return 0, nil +} + +func Dial(_ string, _ string, _ string) (*Conn, error) { + return nil, nil +} + +func DialConfig(_ *Config) (*Conn, error) { + return nil, nil +} + +func NewConfig(_ string, _ string) (*Config, error) { + return nil, nil +} diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/modules.txt new file mode 100644 index 00000000000..43950046ca3 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/modules.txt @@ -0,0 +1,15 @@ +# github.com/gobwas/ws v1.0.3 +## explicit +github.com/gobwas/ws +# github.com/gorilla/websocket v1.4.2 +## explicit +github.com/gorilla/websocket +# github.com/sacOO7/gowebsocket v0.0.0-20180719182212-1436bb906a4e +## explicit +github.com/sacOO7/gowebsocket +# golang.org/x/net v0.0.0-20200505041828-1ed23360d12c +## explicit +golang.org/x/net +# nhooyr.io/websocket v1.8.5 +## explicit +nhooyr.io/websocket diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/nhooyr.io/websocket/LICENSE b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/nhooyr.io/websocket/LICENSE new file mode 100644 index 00000000000..b5b5fef31f0 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/nhooyr.io/websocket/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Anmol Sethi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/nhooyr.io/websocket/stub.go b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/nhooyr.io/websocket/stub.go new file mode 100644 index 00000000000..7bf2a208dac --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/WebSocket/vendor/nhooyr.io/websocket/stub.go @@ -0,0 +1,76 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for nhooyr.io/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: nhooyr.io/websocket (exports: ; functions: Dial) + +// Package websocket is a stub of nhooyr.io/websocket, generated by depstubber. +package websocket + +import ( + context "context" + io "io" + http "net/http" +) + +type CompressionMode int + +type Conn struct{} + +func (_ *Conn) Close(_ StatusCode, _ string) error { + return nil +} + +func (_ *Conn) CloseRead(_ context.Context) context.Context { + return nil +} + +func (_ *Conn) Ping(_ context.Context) error { + return nil +} + +func (_ *Conn) Read(_ context.Context) (MessageType, []byte, error) { + return 0, nil, nil +} + +func (_ *Conn) Reader(_ context.Context) (MessageType, io.Reader, error) { + return 0, nil, nil +} + +func (_ *Conn) SetReadLimit(_ int64) {} + +func (_ *Conn) Subprotocol() string { + return "" +} + +func (_ *Conn) Write(_ context.Context, _ MessageType, _ []byte) error { + return nil +} + +func (_ *Conn) Writer(_ context.Context, _ MessageType) (io.WriteCloser, error) { + return nil, nil +} + +func Dial(_ context.Context, _ string, _ *DialOptions) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +type DialOptions struct { + HTTPClient *http.Client + HTTPHeader http.Header + Subprotocols []string + CompressionMode CompressionMode + CompressionThreshold int +} + +type MessageType int + +func (_ MessageType) String() string { + return "" +} + +type StatusCode int + +func (_ StatusCode) String() string { + return "" +} diff --git a/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/DeadStoreOfLocal.expected b/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/DeadStoreOfLocal.expected index b646631be51..58fd429b859 100644 --- a/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/DeadStoreOfLocal.expected +++ b/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/DeadStoreOfLocal.expected @@ -1,4 +1,4 @@ -| main.go:25:2:25:2 | assignment to x | This definition of x is never used. | +| main.go:27:2:27:2 | assignment to x | This definition of x is never used. | | testdata.go:32:2:32:2 | assignment to x | This definition of x is never used. | | testdata.go:37:2:37:2 | assignment to x | This definition of x is never used. | | testdata.go:61:2:61:2 | assignment to x | This definition of x is never used. | diff --git a/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/main.go b/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/main.go index 31062a18f98..05cad065d61 100644 --- a/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/main.go +++ b/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/main.go @@ -2,6 +2,8 @@ package p import "fmt" +// codeql test: expect frontend errors + func test() { if false { x := deadStore() // OK diff --git a/ql/test/query-tests/RedundantCode/ImpossibleInterfaceNilCheck/err.go b/ql/test/query-tests/RedundantCode/ImpossibleInterfaceNilCheck/err.go index 8801b3b7465..034b34b3d67 100644 --- a/ql/test/query-tests/RedundantCode/ImpossibleInterfaceNilCheck/err.go +++ b/ql/test/query-tests/RedundantCode/ImpossibleInterfaceNilCheck/err.go @@ -2,6 +2,8 @@ package main import "fmt" +// codeql test: expect frontend errors + func errtest() { x := unknownFunction() var y interface{} = x diff --git a/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover.expected b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover.expected new file mode 100644 index 00000000000..fdc175c8cf0 --- /dev/null +++ b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover.expected @@ -0,0 +1,3 @@ +| RedundantRecover1.go:6:5:6:13 | call to recover | This call to 'recover' has no effect because $@ is never called using a defer statement. | RedundantRecover1.go:5:1:9:1 | function declaration | the enclosing function | +| RedundantRecover2.go:4:8:4:16 | call to recover | Deferred calls to 'recover' have no effect. | RedundantRecover2.go:3:1:6:1 | function declaration | the enclosing function | +| tst.go:8:5:8:13 | call to recover | This call to 'recover' has no effect because $@ is never called using a defer statement. | tst.go:5:1:11:1 | function declaration | the enclosing function | diff --git a/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover.qlref b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover.qlref new file mode 100644 index 00000000000..c8997068734 --- /dev/null +++ b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover.qlref @@ -0,0 +1 @@ +RedundantCode/RedundantRecover.ql diff --git a/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover1.go b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover1.go new file mode 100644 index 00000000000..d058dd0dfde --- /dev/null +++ b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover1.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func callRecover1() { + if recover() != nil { + fmt.Printf("recovered") + } +} + +func fun1() { + defer func() { + callRecover1() + }() + panic("1") +} diff --git a/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover1Good.go b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover1Good.go new file mode 100644 index 00000000000..b017e050dc4 --- /dev/null +++ b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover1Good.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +func callRecover1Good() { + if recover() != nil { + fmt.Printf("recovered") + } +} + +func fun1Good() { + defer callRecover1Good() + panic("1") +} diff --git a/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover2.go b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover2.go new file mode 100644 index 00000000000..4365cb7c9fe --- /dev/null +++ b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover2.go @@ -0,0 +1,6 @@ +package main + +func fun2() { + defer recover() + panic("2") +} diff --git a/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover2Good.go b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover2Good.go new file mode 100644 index 00000000000..d34e5c82b63 --- /dev/null +++ b/ql/test/query-tests/RedundantCode/RedundantRecover/RedundantRecover2Good.go @@ -0,0 +1,6 @@ +package main + +func fun2Good() { + defer func() { recover() }() + panic("2") +} diff --git a/ql/test/query-tests/RedundantCode/RedundantRecover/tst.go b/ql/test/query-tests/RedundantCode/RedundantRecover/tst.go new file mode 100644 index 00000000000..0533a060931 --- /dev/null +++ b/ql/test/query-tests/RedundantCode/RedundantRecover/tst.go @@ -0,0 +1,49 @@ +package main + +import "fmt" + +func callRecover3() { + // This will have no effect because panics do not propagate down the stack, + // only back up the stack + if recover() != nil { + fmt.Printf("recovered") + } +} + +func fun3() { + panic("3") + callRecover3() +} + +func callRecover4() { + // This is not flagged because callRecover4 is called in a defer statement + // at least once + if recover() != nil { + fmt.Printf("recovered") + } +} + +func fun4a() { + panic("4") + callRecover4() +} + +func fun4b() { + defer callRecover4() + panic("4") +} + +func neverCalled() { + // This will not be flagged because it is not called from anywhere + if recover() != nil { + fmt.Printf("recovered") + } +} + +func main() { + fun1() + fun2() + fun3() + fun4a() + fun4b() +} diff --git a/ql/test/query-tests/Security/CWE-022/TaintedPath.expected b/ql/test/query-tests/Security/CWE-022/TaintedPath.expected index 04e3459926c..39e4380b136 100644 --- a/ql/test/query-tests/Security/CWE-022/TaintedPath.expected +++ b/ql/test/query-tests/Security/CWE-022/TaintedPath.expected @@ -1,10 +1,18 @@ edges | TaintedPath.go:10:10:10:14 | selection of URL : pointer type | TaintedPath.go:13:29:13:32 | path | | TaintedPath.go:10:10:10:14 | selection of URL : pointer type | TaintedPath.go:17:28:17:61 | call to Join | +| tst.go:14:22:14:39 | call to FormFile : tuple type | tst.go:17:41:17:47 | implicit dereference : FileHeader | +| tst.go:14:22:14:39 | call to FormFile : tuple type | tst.go:17:41:17:56 | selection of Filename | +| tst.go:17:41:17:47 | implicit dereference : FileHeader | tst.go:17:41:17:47 | implicit dereference : FileHeader | +| tst.go:17:41:17:47 | implicit dereference : FileHeader | tst.go:17:41:17:56 | selection of Filename | nodes | TaintedPath.go:10:10:10:14 | selection of URL : pointer type | semmle.label | selection of URL : pointer type | | TaintedPath.go:13:29:13:32 | path | semmle.label | path | | TaintedPath.go:17:28:17:61 | call to Join | semmle.label | call to Join | +| tst.go:14:22:14:39 | call to FormFile : tuple type | semmle.label | call to FormFile : tuple type | +| tst.go:17:41:17:47 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader | +| tst.go:17:41:17:56 | selection of Filename | semmle.label | selection of Filename | #select | TaintedPath.go:13:29:13:32 | path | TaintedPath.go:10:10:10:14 | selection of URL : pointer type | TaintedPath.go:13:29:13:32 | path | This path depends on $@. | TaintedPath.go:10:10:10:14 | selection of URL | a user-provided value | | TaintedPath.go:17:28:17:61 | call to Join | TaintedPath.go:10:10:10:14 | selection of URL : pointer type | TaintedPath.go:17:28:17:61 | call to Join | This path depends on $@. | TaintedPath.go:10:10:10:14 | selection of URL | a user-provided value | +| tst.go:17:41:17:56 | selection of Filename | tst.go:14:22:14:39 | call to FormFile : tuple type | tst.go:17:41:17:56 | selection of Filename | This path depends on $@. | tst.go:14:22:14:39 | call to FormFile | a user-provided value | diff --git a/ql/test/query-tests/Security/CWE-022/ZipSlip.expected b/ql/test/query-tests/Security/CWE-022/ZipSlip.expected index 212eb53924d..d26767d52bf 100644 --- a/ql/test/query-tests/Security/CWE-022/ZipSlip.expected +++ b/ql/test/query-tests/Security/CWE-022/ZipSlip.expected @@ -1,15 +1,15 @@ edges | ZipSlip.go:12:24:12:29 | selection of Name : string | ZipSlip.go:14:20:14:20 | p | | tarslip.go:14:23:14:33 | selection of Name : string | tarslip.go:14:14:14:34 | call to Dir | -| tst.go:15:11:15:16 | selection of Name : string | tst.go:20:20:20:23 | path | +| tst.go:24:11:24:16 | selection of Name : string | tst.go:29:20:29:23 | path | nodes | ZipSlip.go:12:24:12:29 | selection of Name : string | semmle.label | selection of Name : string | | ZipSlip.go:14:20:14:20 | p | semmle.label | p | | tarslip.go:14:14:14:34 | call to Dir | semmle.label | call to Dir | | tarslip.go:14:23:14:33 | selection of Name : string | semmle.label | selection of Name : string | -| tst.go:15:11:15:16 | selection of Name : string | semmle.label | selection of Name : string | -| tst.go:20:20:20:23 | path | semmle.label | path | +| tst.go:24:11:24:16 | selection of Name : string | semmle.label | selection of Name : string | +| tst.go:29:20:29:23 | path | semmle.label | path | #select | ZipSlip.go:12:24:12:29 | selection of Name | ZipSlip.go:12:24:12:29 | selection of Name : string | ZipSlip.go:14:20:14:20 | p | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipSlip.go:14:20:14:20 | p | file system operation | | tarslip.go:14:23:14:33 | selection of Name | tarslip.go:14:23:14:33 | selection of Name : string | tarslip.go:14:14:14:34 | call to Dir | Unsanitized archive entry, which may contain '..', is used in a $@. | tarslip.go:14:14:14:34 | call to Dir | file system operation | -| tst.go:15:11:15:16 | selection of Name | tst.go:15:11:15:16 | selection of Name : string | tst.go:20:20:20:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:20:20:20:23 | path | file system operation | +| tst.go:24:11:24:16 | selection of Name | tst.go:24:11:24:16 | selection of Name : string | tst.go:29:20:29:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:29:20:29:23 | path | file system operation | diff --git a/ql/test/query-tests/Security/CWE-022/tst.go b/ql/test/query-tests/Security/CWE-022/tst.go index 766f37029a7..599faccf0f1 100644 --- a/ql/test/query-tests/Security/CWE-022/tst.go +++ b/ql/test/query-tests/Security/CWE-022/tst.go @@ -3,12 +3,21 @@ package main import ( "archive/zip" "io/ioutil" + "net/http" "os" "path/filepath" "regexp" "strings" ) +func uploadFile(w http.ResponseWriter, r *http.Request) { + file, handler, _ := r.FormFile("file") + // err handling + defer file.Close() + tempFile, _ := ioutil.TempFile("/tmp", handler.Filename) // NOT OK + use(tempFile) +} + func unzip2(f string, root string) { r, _ := zip.OpenReader(f) for _, f := range r.File { @@ -41,3 +50,5 @@ func containedIn(f string, root string) bool { } return false } + +func use(v interface{}) {} diff --git a/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected index a4dcddef714..c7aab9062b3 100644 --- a/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected +++ b/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected @@ -2,7 +2,14 @@ edges | ReflectedXss.go:11:15:11:20 | selection of Form : Values | ReflectedXss.go:14:44:14:51 | username | | contenttype.go:11:11:11:16 | selection of Form : Values | contenttype.go:17:11:17:22 | type conversion | | contenttype.go:49:11:49:16 | selection of Form : Values | contenttype.go:53:34:53:37 | data | -| tst.go:11:15:11:20 | selection of Form : Values | tst.go:15:12:15:39 | type conversion | +| tst.go:14:15:14:20 | selection of Form : Values | tst.go:18:12:18:39 | type conversion | +| tst.go:48:14:48:19 | selection of Form : Values | tst.go:53:12:53:26 | type conversion | +| websocketXss.go:30:7:30:10 | definition of xnet : slice type | websocketXss.go:32:24:32:27 | xnet | +| websocketXss.go:34:3:34:7 | definition of xnet2 : slice type | websocketXss.go:36:24:36:28 | xnet2 | +| websocketXss.go:40:3:40:40 | ... := ...[1] : slice type | websocketXss.go:41:24:41:29 | nhooyr | +| websocketXss.go:46:7:46:16 | definition of gorillaMsg : slice type | websocketXss.go:48:24:48:33 | gorillaMsg | +| websocketXss.go:50:3:50:10 | definition of gorilla2 : slice type | websocketXss.go:52:24:52:31 | gorilla2 | +| websocketXss.go:54:3:54:38 | ... := ...[1] : slice type | websocketXss.go:55:24:55:31 | gorilla3 | nodes | ReflectedXss.go:11:15:11:20 | selection of Form : Values | semmle.label | selection of Form : Values | | ReflectedXss.go:14:44:14:51 | username | semmle.label | username | @@ -10,10 +17,31 @@ nodes | contenttype.go:17:11:17:22 | type conversion | semmle.label | type conversion | | contenttype.go:49:11:49:16 | selection of Form : Values | semmle.label | selection of Form : Values | | contenttype.go:53:34:53:37 | data | semmle.label | data | -| tst.go:11:15:11:20 | selection of Form : Values | semmle.label | selection of Form : Values | -| tst.go:15:12:15:39 | type conversion | semmle.label | type conversion | +| tst.go:14:15:14:20 | selection of Form : Values | semmle.label | selection of Form : Values | +| tst.go:18:12:18:39 | type conversion | semmle.label | type conversion | +| tst.go:48:14:48:19 | selection of Form : Values | semmle.label | selection of Form : Values | +| tst.go:53:12:53:26 | type conversion | semmle.label | type conversion | +| websocketXss.go:30:7:30:10 | definition of xnet : slice type | semmle.label | definition of xnet : slice type | +| websocketXss.go:32:24:32:27 | xnet | semmle.label | xnet | +| websocketXss.go:34:3:34:7 | definition of xnet2 : slice type | semmle.label | definition of xnet2 : slice type | +| websocketXss.go:36:24:36:28 | xnet2 | semmle.label | xnet2 | +| websocketXss.go:40:3:40:40 | ... := ...[1] : slice type | semmle.label | ... := ...[1] : slice type | +| websocketXss.go:41:24:41:29 | nhooyr | semmle.label | nhooyr | +| websocketXss.go:46:7:46:16 | definition of gorillaMsg : slice type | semmle.label | definition of gorillaMsg : slice type | +| websocketXss.go:48:24:48:33 | gorillaMsg | semmle.label | gorillaMsg | +| websocketXss.go:50:3:50:10 | definition of gorilla2 : slice type | semmle.label | definition of gorilla2 : slice type | +| websocketXss.go:52:24:52:31 | gorilla2 | semmle.label | gorilla2 | +| websocketXss.go:54:3:54:38 | ... := ...[1] : slice type | semmle.label | ... := ...[1] : slice type | +| websocketXss.go:55:24:55:31 | gorilla3 | semmle.label | gorilla3 | #select | ReflectedXss.go:14:44:14:51 | username | ReflectedXss.go:11:15:11:20 | selection of Form : Values | ReflectedXss.go:14:44:14:51 | username | Cross-site scripting vulnerability due to $@. | ReflectedXss.go:11:15:11:20 | selection of Form | user-provided value | | contenttype.go:17:11:17:22 | type conversion | contenttype.go:11:11:11:16 | selection of Form : Values | contenttype.go:17:11:17:22 | type conversion | Cross-site scripting vulnerability due to $@. | contenttype.go:11:11:11:16 | selection of Form | user-provided value | | contenttype.go:53:34:53:37 | data | contenttype.go:49:11:49:16 | selection of Form : Values | contenttype.go:53:34:53:37 | data | Cross-site scripting vulnerability due to $@. | contenttype.go:49:11:49:16 | selection of Form | user-provided value | -| tst.go:15:12:15:39 | type conversion | tst.go:11:15:11:20 | selection of Form : Values | tst.go:15:12:15:39 | type conversion | Cross-site scripting vulnerability due to $@. | tst.go:11:15:11:20 | selection of Form | user-provided value | +| tst.go:18:12:18:39 | type conversion | tst.go:14:15:14:20 | selection of Form : Values | tst.go:18:12:18:39 | type conversion | Cross-site scripting vulnerability due to $@. | tst.go:14:15:14:20 | selection of Form | user-provided value | +| tst.go:53:12:53:26 | type conversion | tst.go:48:14:48:19 | selection of Form : Values | tst.go:53:12:53:26 | type conversion | Cross-site scripting vulnerability due to $@. | tst.go:48:14:48:19 | selection of Form | user-provided value | +| websocketXss.go:32:24:32:27 | xnet | websocketXss.go:30:7:30:10 | definition of xnet : slice type | websocketXss.go:32:24:32:27 | xnet | Cross-site scripting vulnerability due to $@. | websocketXss.go:30:7:30:10 | definition of xnet | user-provided value | +| websocketXss.go:36:24:36:28 | xnet2 | websocketXss.go:34:3:34:7 | definition of xnet2 : slice type | websocketXss.go:36:24:36:28 | xnet2 | Cross-site scripting vulnerability due to $@. | websocketXss.go:34:3:34:7 | definition of xnet2 | user-provided value | +| websocketXss.go:41:24:41:29 | nhooyr | websocketXss.go:40:3:40:40 | ... := ...[1] : slice type | websocketXss.go:41:24:41:29 | nhooyr | Cross-site scripting vulnerability due to $@. | websocketXss.go:40:3:40:40 | ... := ...[1] | user-provided value | +| websocketXss.go:48:24:48:33 | gorillaMsg | websocketXss.go:46:7:46:16 | definition of gorillaMsg : slice type | websocketXss.go:48:24:48:33 | gorillaMsg | Cross-site scripting vulnerability due to $@. | websocketXss.go:46:7:46:16 | definition of gorillaMsg | user-provided value | +| websocketXss.go:52:24:52:31 | gorilla2 | websocketXss.go:50:3:50:10 | definition of gorilla2 : slice type | websocketXss.go:52:24:52:31 | gorilla2 | Cross-site scripting vulnerability due to $@. | websocketXss.go:50:3:50:10 | definition of gorilla2 | user-provided value | +| websocketXss.go:55:24:55:31 | gorilla3 | websocketXss.go:54:3:54:38 | ... := ...[1] : slice type | websocketXss.go:55:24:55:31 | gorilla3 | Cross-site scripting vulnerability due to $@. | websocketXss.go:54:3:54:38 | ... := ...[1] | user-provided value | diff --git a/ql/test/query-tests/Security/CWE-079/go.mod b/ql/test/query-tests/Security/CWE-079/go.mod new file mode 100644 index 00000000000..67a02662281 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/go.mod @@ -0,0 +1,10 @@ +module codeql-go-tests/CWE-079 + +go 1.14 + +require ( + github.com/gobwas/ws v1.0.3 + github.com/gorilla/websocket v1.4.2 + golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + nhooyr.io/websocket v1.8.5 +) diff --git a/ql/test/query-tests/Security/CWE-079/tst.go b/ql/test/query-tests/Security/CWE-079/tst.go index 20e2edd4935..dfdf3bbf6b0 100644 --- a/ql/test/query-tests/Security/CWE-079/tst.go +++ b/ql/test/query-tests/Security/CWE-079/tst.go @@ -1,6 +1,9 @@ package main import ( + "encoding/json" + "fmt" + "io" "net/http" "strings" ) @@ -19,3 +22,50 @@ func serve6() { }) http.ListenAndServe(":80", nil) } + +type User struct { + name string +} + +func serve7() { + http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + username := r.Form.Get("username") + if !isValidUsername(username) { + // OK: json data cannot cause an HTML content type to be detected + a, _ := json.Marshal(User{username}) + w.Write(a) + } else { + // TODO: do something exciting + } + }) + http.ListenAndServe(":80", nil) +} + +func serve8() { + http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + service := r.Form.Get("service") + if service != "service1" && service != "service2" { + fmt.Fprintln(w, "Service not found") + } else { + // OK (service is known to be either "service1" or "service2" here), but currently flagged + w.Write([]byte(service)) + } + }) +} + +type mix struct { + io.Writer + http.ResponseWriter +} + +func serve9(log io.Writer) { + http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + username := r.Form.Get("username") + // OK: not a ResponseWriter + log.Write([]byte(username)) + }) + http.ListenAndServe(":80", nil) +} diff --git a/ql/test/query-tests/Security/CWE-079/vendor/github.com/gobwas/ws/LICENSE b/ql/test/query-tests/Security/CWE-079/vendor/github.com/gobwas/ws/LICENSE new file mode 100644 index 00000000000..d2611fddf55 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/github.com/gobwas/ws/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2018 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/query-tests/Security/CWE-079/vendor/github.com/gobwas/ws/stub.go b/ql/test/query-tests/Security/CWE-079/vendor/github.com/gobwas/ws/stub.go new file mode 100644 index 00000000000..3cd71fffeba --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/github.com/gobwas/ws/stub.go @@ -0,0 +1,76 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gobwas/ws, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gobwas/ws (exports: ; functions: ReadFrame,WriteFrame,NewTextFrame,Dial) + +// Package ws is a stub of github.com/gobwas/ws, generated by depstubber. +package ws + +import ( + bufio "bufio" + context "context" + io "io" + net "net" +) + +func Dial(_ context.Context, _ string) (net.Conn, *bufio.Reader, Handshake, error) { + return nil, nil, Handshake{}, nil +} + +type Frame struct { + Header Header + Payload []byte +} + +type Handshake struct { + Protocol string + Extensions []interface{} +} + +type Header struct { + Fin bool + Rsv byte + OpCode OpCode + Masked bool + Mask [4]byte + Length int64 +} + +func (_ Header) Rsv1() bool { + return false +} + +func (_ Header) Rsv2() bool { + return false +} + +func (_ Header) Rsv3() bool { + return false +} + +func NewTextFrame(_ []byte) Frame { + return Frame{} +} + +type OpCode uint8 + +func (_ OpCode) IsControl() bool { + return false +} + +func (_ OpCode) IsData() bool { + return false +} + +func (_ OpCode) IsReserved() bool { + return false +} + +func ReadFrame(_ io.Reader) (Frame, error) { + return Frame{}, nil +} + +func WriteFrame(_ io.Writer, _ Frame) error { + return nil +} diff --git a/ql/test/query-tests/Security/CWE-079/vendor/github.com/gorilla/websocket/LICENSE b/ql/test/query-tests/Security/CWE-079/vendor/github.com/gorilla/websocket/LICENSE new file mode 100644 index 00000000000..9171c972252 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/github.com/gorilla/websocket/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ql/test/query-tests/Security/CWE-079/vendor/github.com/gorilla/websocket/stub.go b/ql/test/query-tests/Security/CWE-079/vendor/github.com/gorilla/websocket/stub.go new file mode 100644 index 00000000000..c9beb63767a --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/github.com/gorilla/websocket/stub.go @@ -0,0 +1,147 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gorilla/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gorilla/websocket (exports: Dialer; functions: ReadJSON,WriteJSON,NewPreparedMessage) + +// Package websocket is a stub of github.com/gorilla/websocket, generated by depstubber. +package websocket + +import ( + context "context" + tls "crypto/tls" + io "io" + net "net" + http "net/http" + url "net/url" + time "time" +) + +type BufferPool interface { + Get() interface{} + Put(_ interface{}) +} + +type Conn struct{} + +func (_ *Conn) Close() error { + return nil +} + +func (_ *Conn) CloseHandler() func(int, string) error { + return nil +} + +func (_ *Conn) EnableWriteCompression(_ bool) {} + +func (_ *Conn) LocalAddr() net.Addr { + return nil +} + +func (_ *Conn) NextReader() (int, io.Reader, error) { + return 0, nil, nil +} + +func (_ *Conn) NextWriter(_ int) (io.WriteCloser, error) { + return nil, nil +} + +func (_ *Conn) PingHandler() func(string) error { + return nil +} + +func (_ *Conn) PongHandler() func(string) error { + return nil +} + +func (_ *Conn) ReadJSON(_ interface{}) error { + return nil +} + +func (_ *Conn) ReadMessage() (int, []byte, error) { + return 0, nil, nil +} + +func (_ *Conn) RemoteAddr() net.Addr { + return nil +} + +func (_ *Conn) SetCloseHandler(_ func(int, string) error) {} + +func (_ *Conn) SetCompressionLevel(_ int) error { + return nil +} + +func (_ *Conn) SetPingHandler(_ func(string) error) {} + +func (_ *Conn) SetPongHandler(_ func(string) error) {} + +func (_ *Conn) SetReadDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetReadLimit(_ int64) {} + +func (_ *Conn) SetWriteDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) Subprotocol() string { + return "" +} + +func (_ *Conn) UnderlyingConn() net.Conn { + return nil +} + +func (_ *Conn) WriteControl(_ int, _ []byte, _ time.Time) error { + return nil +} + +func (_ *Conn) WriteJSON(_ interface{}) error { + return nil +} + +func (_ *Conn) WriteMessage(_ int, _ []byte) error { + return nil +} + +func (_ *Conn) WritePreparedMessage(_ *PreparedMessage) error { + return nil +} + +type Dialer struct { + NetDial func(string, string) (net.Conn, error) + NetDialContext func(context.Context, string, string) (net.Conn, error) + Proxy func(*http.Request) (*url.URL, error) + TLSClientConfig *tls.Config + HandshakeTimeout time.Duration + ReadBufferSize int + WriteBufferSize int + WriteBufferPool BufferPool + Subprotocols []string + EnableCompression bool + Jar http.CookieJar +} + +func (_ *Dialer) Dial(_ string, _ http.Header) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +func (_ *Dialer) DialContext(_ context.Context, _ string, _ http.Header) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +func NewPreparedMessage(_ int, _ []byte) (*PreparedMessage, error) { + return nil, nil +} + +type PreparedMessage struct{} + +func ReadJSON(_ *Conn, _ interface{}) error { + return nil +} + +func WriteJSON(_ *Conn, _ interface{}) error { + return nil +} diff --git a/ql/test/query-tests/Security/CWE-079/vendor/golang.org/x/net/websocket/LICENSE b/ql/test/query-tests/Security/CWE-079/vendor/golang.org/x/net/websocket/LICENSE new file mode 100644 index 00000000000..6a66aea5eaf --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/golang.org/x/net/websocket/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ql/test/query-tests/Security/CWE-079/vendor/golang.org/x/net/websocket/stub.go b/ql/test/query-tests/Security/CWE-079/vendor/golang.org/x/net/websocket/stub.go new file mode 100644 index 00000000000..480729efdba --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/golang.org/x/net/websocket/stub.go @@ -0,0 +1,125 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for golang.org/x/net/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: golang.org/x/net/websocket (exports: Codec; functions: Dial) + +// Package websocket is a stub of golang.org/x/net/websocket, generated by depstubber. +package websocket + +import ( + tls "crypto/tls" + io "io" + net "net" + http "net/http" + url "net/url" + time "time" +) + +type Codec struct { + Marshal func(interface{}) ([]byte, byte, error) + Unmarshal func([]byte, byte, interface{}) error +} + +func (_ Codec) Receive(_ *Conn, _ interface{}) error { + return nil +} + +func (_ Codec) Send(_ *Conn, _ interface{}) error { + return nil +} + +type Config struct { + Location *url.URL + Origin *url.URL + Protocol []string + Version int + TlsConfig *tls.Config + Header http.Header + Dialer *net.Dialer +} + +type Conn struct { + PayloadType byte + MaxPayloadBytes int +} + +func (_ Conn) HandleFrame(_ interface{}) (interface{}, error) { + return nil, nil +} + +func (_ Conn) HeaderReader() io.Reader { + return nil +} + +func (_ Conn) Len() int { + return 0 +} + +func (_ Conn) NewFrameReader() (interface{}, error) { + return nil, nil +} + +func (_ Conn) NewFrameWriter(_ byte) (interface{}, error) { + return nil, nil +} + +func (_ Conn) TrailerReader() io.Reader { + return nil +} + +func (_ Conn) WriteClose(_ int) error { + return nil +} + +func (_ *Conn) Close() error { + return nil +} + +func (_ *Conn) Config() *Config { + return nil +} + +func (_ *Conn) IsClientConn() bool { + return false +} + +func (_ *Conn) IsServerConn() bool { + return false +} + +func (_ *Conn) LocalAddr() net.Addr { + return nil +} + +func (_ *Conn) Read(_ []byte) (int, error) { + return 0, nil +} + +func (_ *Conn) RemoteAddr() net.Addr { + return nil +} + +func (_ *Conn) Request() *http.Request { + return nil +} + +func (_ *Conn) SetDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetReadDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetWriteDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) Write(_ []byte) (int, error) { + return 0, nil +} + +func Dial(_ string, _ string, _ string) (*Conn, error) { + return nil, nil +} diff --git a/ql/test/query-tests/Security/CWE-079/vendor/modules.txt b/ql/test/query-tests/Security/CWE-079/vendor/modules.txt new file mode 100644 index 00000000000..5e59711dd7b --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/modules.txt @@ -0,0 +1,12 @@ +# github.com/gobwas/ws v1.0.3 +## explicit +github.com/gobwas/ws +# github.com/gorilla/websocket v1.4.2 +## explicit +github.com/gorilla/websocket +# golang.org/x/net v0.0.0-20200505041828-1ed23360d12c +## explicit +golang.org/x/net +# nhooyr.io/websocket v1.8.5 +## explicit +nhooyr.io/websocket diff --git a/ql/test/query-tests/Security/CWE-079/vendor/nhooyr.io/websocket/LICENSE b/ql/test/query-tests/Security/CWE-079/vendor/nhooyr.io/websocket/LICENSE new file mode 100644 index 00000000000..b5b5fef31f0 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/nhooyr.io/websocket/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Anmol Sethi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/query-tests/Security/CWE-079/vendor/nhooyr.io/websocket/stub.go b/ql/test/query-tests/Security/CWE-079/vendor/nhooyr.io/websocket/stub.go new file mode 100644 index 00000000000..7bf2a208dac --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/vendor/nhooyr.io/websocket/stub.go @@ -0,0 +1,76 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for nhooyr.io/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: nhooyr.io/websocket (exports: ; functions: Dial) + +// Package websocket is a stub of nhooyr.io/websocket, generated by depstubber. +package websocket + +import ( + context "context" + io "io" + http "net/http" +) + +type CompressionMode int + +type Conn struct{} + +func (_ *Conn) Close(_ StatusCode, _ string) error { + return nil +} + +func (_ *Conn) CloseRead(_ context.Context) context.Context { + return nil +} + +func (_ *Conn) Ping(_ context.Context) error { + return nil +} + +func (_ *Conn) Read(_ context.Context) (MessageType, []byte, error) { + return 0, nil, nil +} + +func (_ *Conn) Reader(_ context.Context) (MessageType, io.Reader, error) { + return 0, nil, nil +} + +func (_ *Conn) SetReadLimit(_ int64) {} + +func (_ *Conn) Subprotocol() string { + return "" +} + +func (_ *Conn) Write(_ context.Context, _ MessageType, _ []byte) error { + return nil +} + +func (_ *Conn) Writer(_ context.Context, _ MessageType) (io.WriteCloser, error) { + return nil, nil +} + +func Dial(_ context.Context, _ string, _ *DialOptions) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +type DialOptions struct { + HTTPClient *http.Client + HTTPHeader http.Header + Subprotocols []string + CompressionMode CompressionMode + CompressionThreshold int +} + +type MessageType int + +func (_ MessageType) String() string { + return "" +} + +type StatusCode int + +func (_ StatusCode) String() string { + return "" +} diff --git a/ql/test/query-tests/Security/CWE-079/websocketXss.go b/ql/test/query-tests/Security/CWE-079/websocketXss.go new file mode 100644 index 00000000000..06f15e52cd3 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-079/websocketXss.go @@ -0,0 +1,58 @@ +package main + +//go:generate depstubber -vendor github.com/gobwas/ws "" ReadFrame,WriteFrame,NewTextFrame,Dial +//go:generate depstubber -vendor github.com/gorilla/websocket Dialer ReadJSON,WriteJSON,NewPreparedMessage +//go:generate depstubber -vendor golang.org/x/net/websocket Codec Dial +//go:generate depstubber -vendor nhooyr.io/websocket "" Dial + +import ( + "context" + "fmt" + "net/http" + + gorilla "github.com/gorilla/websocket" + websocket "golang.org/x/net/websocket" + nhooyr "nhooyr.io/websocket" +) + +func marshal(v interface{}) (data []byte, payloadType byte, err error) { + return nil, 0, nil +} +func unmarshal(data []byte, payloadType byte, v interface{}) (err error) { + return nil +} + +func xss(w http.ResponseWriter, r *http.Request) { + uri := r.Header.Get("X-Header") + origin := "test" + { + ws, _ := websocket.Dial(uri, "", origin) + var xnet = make([]byte, 512) + ws.Read(xnet) + fmt.Fprintf(w, "%v", xnet) + codec := &websocket.Codec{marshal, unmarshal} + xnet2 := make([]byte, 512) + codec.Receive(ws, xnet2) + fmt.Fprintf(w, "%v", xnet2) + } + { + n, _, _ := nhooyr.Dial(context.TODO(), uri, nil) + _, nhooyr, _ := n.Read(context.TODO()) + fmt.Fprintf(w, "%v", nhooyr) + } + { + dialer := gorilla.Dialer{} + conn, _, _ := dialer.Dial(uri, nil) + var gorillaMsg = make([]byte, 512) + gorilla.ReadJSON(conn, gorillaMsg) + fmt.Fprintf(w, "%v", gorillaMsg) + + gorilla2 := make([]byte, 512) + conn.ReadJSON(gorilla2) + fmt.Fprintf(w, "%v", gorilla2) + + _, gorilla3, _ := conn.ReadMessage() + fmt.Fprintf(w, "%v", gorilla3) + + } +} diff --git a/ql/test/query-tests/Security/CWE-089/SqlInjection.expected b/ql/test/query-tests/Security/CWE-089/SqlInjection.expected index c12da6e5205..89f2978af2f 100644 --- a/ql/test/query-tests/Security/CWE-089/SqlInjection.expected +++ b/ql/test/query-tests/Security/CWE-089/SqlInjection.expected @@ -2,8 +2,8 @@ edges | SqlInjection.go:11:3:11:9 | selection of URL : pointer type | SqlInjection.go:12:11:12:11 | q | | issue48.go:17:25:17:32 | selection of Body : ReadCloser | issue48.go:22:11:22:12 | q3 | | issue48.go:27:26:27:33 | selection of Body : ReadCloser | issue48.go:32:11:32:12 | q4 | -| issue48.go:37:17:37:50 | type conversion : slice type | issue48.go:41:11:41:12 | q5 | -| issue48.go:37:24:37:30 | selection of URL : pointer type | issue48.go:37:17:37:50 | type conversion : slice type | +| issue48.go:37:17:37:50 | type conversion : string | issue48.go:41:11:41:12 | q5 | +| issue48.go:37:24:37:30 | selection of URL : pointer type | issue48.go:37:17:37:50 | type conversion : string | | main.go:10:11:10:16 | selection of Form : Values | main.go:10:11:10:28 | index expression | | main.go:14:63:14:67 | selection of URL : pointer type | main.go:14:11:14:84 | call to Sprintf | | main.go:15:63:15:70 | selection of Header : Header | main.go:15:11:15:85 | call to Sprintf | @@ -41,6 +41,20 @@ edges | main.go:60:3:60:25 | selection of Category : slice type | main.go:61:11:61:11 | q | | main.go:60:4:60:15 | star expression [Category] : slice type | main.go:60:3:60:25 | selection of Category : slice type | | main.go:60:5:60:15 | RequestData [pointer, Category] | main.go:60:4:60:15 | star expression [Category] : slice type | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:57:22:57:29 | pipeline | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:61:27:61:32 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:63:23:63:28 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:64:22:64:27 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:66:32:66:37 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:69:17:69:22 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:70:20:70:25 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:71:29:71:34 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:72:30:72:35 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:73:29:73:34 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:78:23:78:28 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:79:23:79:28 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:80:22:80:27 | filter | +| mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:81:18:81:25 | pipeline | nodes | SqlInjection.go:11:3:11:9 | selection of URL : pointer type | semmle.label | selection of URL : pointer type | | SqlInjection.go:12:11:12:11 | q | semmle.label | q | @@ -48,7 +62,7 @@ nodes | issue48.go:22:11:22:12 | q3 | semmle.label | q3 | | issue48.go:27:26:27:33 | selection of Body : ReadCloser | semmle.label | selection of Body : ReadCloser | | issue48.go:32:11:32:12 | q4 | semmle.label | q4 | -| issue48.go:37:17:37:50 | type conversion : slice type | semmle.label | type conversion : slice type | +| issue48.go:37:17:37:50 | type conversion : string | semmle.label | type conversion : string | | issue48.go:37:24:37:30 | selection of URL : pointer type | semmle.label | selection of URL : pointer type | | issue48.go:41:11:41:12 | q5 | semmle.label | q5 | | main.go:10:11:10:16 | selection of Form : Values | semmle.label | selection of Form : Values | @@ -92,6 +106,21 @@ nodes | main.go:60:4:60:15 | star expression [Category] : slice type | semmle.label | star expression [Category] : slice type | | main.go:60:5:60:15 | RequestData [pointer, Category] | semmle.label | RequestData [pointer, Category] | | main.go:61:11:61:11 | q | semmle.label | q | +| mongoDB.go:40:20:40:30 | call to Referer : string | semmle.label | call to Referer : string | +| mongoDB.go:57:22:57:29 | pipeline | semmle.label | pipeline | +| mongoDB.go:61:27:61:32 | filter | semmle.label | filter | +| mongoDB.go:63:23:63:28 | filter | semmle.label | filter | +| mongoDB.go:64:22:64:27 | filter | semmle.label | filter | +| mongoDB.go:66:32:66:37 | filter | semmle.label | filter | +| mongoDB.go:69:17:69:22 | filter | semmle.label | filter | +| mongoDB.go:70:20:70:25 | filter | semmle.label | filter | +| mongoDB.go:71:29:71:34 | filter | semmle.label | filter | +| mongoDB.go:72:30:72:35 | filter | semmle.label | filter | +| mongoDB.go:73:29:73:34 | filter | semmle.label | filter | +| mongoDB.go:78:23:78:28 | filter | semmle.label | filter | +| mongoDB.go:79:23:79:28 | filter | semmle.label | filter | +| mongoDB.go:80:22:80:27 | filter | semmle.label | filter | +| mongoDB.go:81:18:81:25 | pipeline | semmle.label | pipeline | #select | SqlInjection.go:12:11:12:11 | q | SqlInjection.go:11:3:11:9 | selection of URL : pointer type | SqlInjection.go:12:11:12:11 | q | This query depends on $@. | SqlInjection.go:11:3:11:9 | selection of URL | a user-provided value | | issue48.go:22:11:22:12 | q3 | issue48.go:17:25:17:32 | selection of Body : ReadCloser | issue48.go:22:11:22:12 | q3 | This query depends on $@. | issue48.go:17:25:17:32 | selection of Body | a user-provided value | @@ -104,3 +133,17 @@ nodes | main.go:43:11:43:11 | q | main.go:39:25:39:31 | selection of URL : pointer type | main.go:43:11:43:11 | q | This query depends on $@. | main.go:39:25:39:31 | selection of URL | a user-provided value | | main.go:52:11:52:11 | q | main.go:48:28:48:34 | selection of URL : pointer type | main.go:52:11:52:11 | q | This query depends on $@. | main.go:48:28:48:34 | selection of URL | a user-provided value | | main.go:61:11:61:11 | q | main.go:57:28:57:34 | selection of URL : pointer type | main.go:61:11:61:11 | q | This query depends on $@. | main.go:57:28:57:34 | selection of URL | a user-provided value | +| mongoDB.go:57:22:57:29 | pipeline | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:57:22:57:29 | pipeline | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:61:27:61:32 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:61:27:61:32 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:63:23:63:28 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:63:23:63:28 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:64:22:64:27 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:64:22:64:27 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:66:32:66:37 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:66:32:66:37 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:69:17:69:22 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:69:17:69:22 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:70:20:70:25 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:70:20:70:25 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:71:29:71:34 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:71:29:71:34 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:72:30:72:35 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:72:30:72:35 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:73:29:73:34 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:73:29:73:34 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:78:23:78:28 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:78:23:78:28 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:79:23:79:28 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:79:23:79:28 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:80:22:80:27 | filter | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:80:22:80:27 | filter | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | +| mongoDB.go:81:18:81:25 | pipeline | mongoDB.go:40:20:40:30 | call to Referer : string | mongoDB.go:81:18:81:25 | pipeline | This query depends on $@. | mongoDB.go:40:20:40:30 | call to Referer | a user-provided value | diff --git a/ql/test/query-tests/Security/CWE-089/go.mod b/ql/test/query-tests/Security/CWE-089/go.mod index 9c55bcb4ffc..6101c095cbc 100644 --- a/ql/test/query-tests/Security/CWE-089/go.mod +++ b/ql/test/query-tests/Security/CWE-089/go.mod @@ -4,8 +4,5 @@ go 1.14 require ( github.com/Masterminds/squirrel v1.1.0 - github.com/github/depstubber v0.0.0-20200414023404-c355b630c381 // indirect - github.com/go-sql-driver/mysql v1.5.0 // indirect - github.com/lib/pq v1.3.0 // indirect - github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect + go.mongodb.org/mongo-driver v1.3.3 ) diff --git a/ql/test/query-tests/Security/CWE-089/mongoDB.go b/ql/test/query-tests/Security/CWE-089/mongoDB.go new file mode 100644 index 00000000000..818f8adb13c --- /dev/null +++ b/ql/test/query-tests/Security/CWE-089/mongoDB.go @@ -0,0 +1,83 @@ +package main + +//go:generate depstubber -vendor go.mongodb.org/mongo-driver/bson/primitive D +//go:generate depstubber -vendor go.mongodb.org/mongo-driver/mongo Pipeline Connect +//go:generate depstubber -vendor go.mongodb.org/mongo-driver/mongo/options "" Client + +import ( + "context" + "fmt" + "log" + "net/http" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func mongo2(w http.ResponseWriter, r *http.Request) { + + // Set client options + clientOptions := options.Client().ApplyURI("mongodb://test:test@localhost:27017") + + // Connect to MongoDB + client, err := mongo.Connect(context.TODO(), clientOptions) + if err != nil { + log.Fatal(err) + } + + // Check the connection + err = client.Ping(context.TODO(), nil) + if err != nil { + log.Fatal(err) + } + + fmt.Println("Connected to MongoDB!") + + // Get a handle for your collection + db := client.Database("test") + coll := db.Collection("collection") + untrustedInput := r.Referer() + + filter := bson.D{{"name", untrustedInput}} + + fieldName := "test" + document := filter + documents := []interface{}{ + document, + bson.D{{"name", "Bob"}}, + } + matchStage := bson.D{{"$match", filter}} + pipeline := mongo.Pipeline{matchStage} + ctx := context.TODO() + replacement := bson.D{{"location", "NYC"}} + update := bson.D{{"$inc", bson.D{{"age", 1}}}} + // models := nil + + coll.Aggregate(ctx, pipeline, nil) + // coll.BulkWrite(ctx, models, nil) + coll.BulkWrite(ctx, nil, nil) + coll.Clone(nil) + coll.CountDocuments(ctx, filter, nil) + coll.Database() + coll.DeleteMany(ctx, filter, nil) + coll.DeleteOne(ctx, filter, nil) + + coll.Distinct(ctx, fieldName, filter) + coll.Drop(ctx) + coll.EstimatedDocumentCount(ctx, nil) + coll.Find(ctx, filter, nil) + coll.FindOne(ctx, filter, nil) + coll.FindOneAndDelete(ctx, filter, nil) + coll.FindOneAndReplace(ctx, filter, nil) + coll.FindOneAndUpdate(ctx, filter, nil) + coll.Indexes() + coll.InsertMany(ctx, documents) + coll.InsertOne(ctx, document, nil) + coll.Name() + coll.ReplaceOne(ctx, filter, replacement) + coll.UpdateMany(ctx, filter, update) + coll.UpdateOne(ctx, filter, update) + coll.Watch(ctx, pipeline) + +} diff --git a/ql/test/query-tests/Security/CWE-089/vendor/github.com/Masterminds/squirrel/stub.go b/ql/test/query-tests/Security/CWE-089/vendor/github.com/Masterminds/squirrel/stub.go index 925f13e5a8c..0e85e0f5e84 100644 --- a/ql/test/query-tests/Security/CWE-089/vendor/github.com/Masterminds/squirrel/stub.go +++ b/ql/test/query-tests/Security/CWE-089/vendor/github.com/Masterminds/squirrel/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/Masterminds/squirrel, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/Masterminds/squirrel (exports: ; functions: Expr,StatementBuilder) // Package squirrel is a stub of github.com/Masterminds/squirrel, generated by depstubber. @@ -12,25 +13,17 @@ import ( ) type BaseRunner interface { - Exec(_ string, _ ...interface{}) (sql.Result, interface { - Error() string - }) - Query(_ string, _ ...interface{}) (*sql.Rows, interface { - Error() string - }) + Exec(_ string, _ ...interface{}) (sql.Result, error) + Query(_ string, _ ...interface{}) (*sql.Rows, error) } type DeleteBuilder struct{} -func (_ DeleteBuilder) Exec() (sql.Result, interface { - Error() string -}) { +func (_ DeleteBuilder) Exec() (sql.Result, error) { return nil, nil } -func (_ DeleteBuilder) ExecContext(_ context.Context) (sql.Result, interface { - Error() string -}) { +func (_ DeleteBuilder) ExecContext(_ context.Context) (sql.Result, error) { return nil, nil } @@ -58,9 +51,7 @@ func (_ DeleteBuilder) Prefix(_ string, _ ...interface{}) DeleteBuilder { return DeleteBuilder{} } -func (_ DeleteBuilder) Query() (*sql.Rows, interface { - Error() string -}) { +func (_ DeleteBuilder) Query() (*sql.Rows, error) { return nil, nil } @@ -72,9 +63,7 @@ func (_ DeleteBuilder) Suffix(_ string, _ ...interface{}) DeleteBuilder { return DeleteBuilder{} } -func (_ DeleteBuilder) ToSql() (string, []interface{}, interface { - Error() string -}) { +func (_ DeleteBuilder) ToSql() (string, []interface{}, error) { return "", nil, nil } @@ -92,15 +81,11 @@ func (_ InsertBuilder) Columns(_ ...string) InsertBuilder { return InsertBuilder{} } -func (_ InsertBuilder) Exec() (sql.Result, interface { - Error() string -}) { +func (_ InsertBuilder) Exec() (sql.Result, error) { return nil, nil } -func (_ InsertBuilder) ExecContext(_ context.Context) (sql.Result, interface { - Error() string -}) { +func (_ InsertBuilder) ExecContext(_ context.Context) (sql.Result, error) { return nil, nil } @@ -120,15 +105,11 @@ func (_ InsertBuilder) Prefix(_ string, _ ...interface{}) InsertBuilder { return InsertBuilder{} } -func (_ InsertBuilder) Query() (*sql.Rows, interface { - Error() string -}) { +func (_ InsertBuilder) Query() (*sql.Rows, error) { return nil, nil } -func (_ InsertBuilder) QueryContext(_ context.Context) (*sql.Rows, interface { - Error() string -}) { +func (_ InsertBuilder) QueryContext(_ context.Context) (*sql.Rows, error) { return nil, nil } @@ -144,15 +125,11 @@ func (_ InsertBuilder) RunWith(_ BaseRunner) InsertBuilder { return InsertBuilder{} } -func (_ InsertBuilder) Scan(_ ...interface{}) interface { - Error() string -} { +func (_ InsertBuilder) Scan(_ ...interface{}) error { return nil } -func (_ InsertBuilder) ScanContext(_ context.Context, _ ...interface{}) interface { - Error() string -} { +func (_ InsertBuilder) ScanContext(_ context.Context, _ ...interface{}) error { return nil } @@ -168,9 +145,7 @@ func (_ InsertBuilder) Suffix(_ string, _ ...interface{}) InsertBuilder { return InsertBuilder{} } -func (_ InsertBuilder) ToSql() (string, []interface{}, interface { - Error() string -}) { +func (_ InsertBuilder) ToSql() (string, []interface{}, error) { return "", nil, nil } @@ -179,15 +154,11 @@ func (_ InsertBuilder) Values(_ ...interface{}) InsertBuilder { } type PlaceholderFormat interface { - ReplacePlaceholders(_ string) (string, interface { - Error() string - }) + ReplacePlaceholders(_ string) (string, error) } type RowScanner interface { - Scan(_ ...interface{}) interface { - Error() string - } + Scan(_ ...interface{}) error } type SelectBuilder struct{} @@ -204,15 +175,11 @@ func (_ SelectBuilder) Distinct() SelectBuilder { return SelectBuilder{} } -func (_ SelectBuilder) Exec() (sql.Result, interface { - Error() string -}) { +func (_ SelectBuilder) Exec() (sql.Result, error) { return nil, nil } -func (_ SelectBuilder) ExecContext(_ context.Context) (sql.Result, interface { - Error() string -}) { +func (_ SelectBuilder) ExecContext(_ context.Context) (sql.Result, error) { return nil, nil } @@ -272,15 +239,11 @@ func (_ SelectBuilder) Prefix(_ string, _ ...interface{}) SelectBuilder { return SelectBuilder{} } -func (_ SelectBuilder) Query() (*sql.Rows, interface { - Error() string -}) { +func (_ SelectBuilder) Query() (*sql.Rows, error) { return nil, nil } -func (_ SelectBuilder) QueryContext(_ context.Context) (*sql.Rows, interface { - Error() string -}) { +func (_ SelectBuilder) QueryContext(_ context.Context) (*sql.Rows, error) { return nil, nil } @@ -304,15 +267,11 @@ func (_ SelectBuilder) RunWith(_ BaseRunner) SelectBuilder { return SelectBuilder{} } -func (_ SelectBuilder) Scan(_ ...interface{}) interface { - Error() string -} { +func (_ SelectBuilder) Scan(_ ...interface{}) error { return nil } -func (_ SelectBuilder) ScanContext(_ context.Context, _ ...interface{}) interface { - Error() string -} { +func (_ SelectBuilder) ScanContext(_ context.Context, _ ...interface{}) error { return nil } @@ -320,9 +279,7 @@ func (_ SelectBuilder) Suffix(_ string, _ ...interface{}) SelectBuilder { return SelectBuilder{} } -func (_ SelectBuilder) ToSql() (string, []interface{}, interface { - Error() string -}) { +func (_ SelectBuilder) ToSql() (string, []interface{}, error) { return "", nil, nil } @@ -360,15 +317,11 @@ func (_ StatementBuilderType) Update(_ string) UpdateBuilder { type UpdateBuilder struct{} -func (_ UpdateBuilder) Exec() (sql.Result, interface { - Error() string -}) { +func (_ UpdateBuilder) Exec() (sql.Result, error) { return nil, nil } -func (_ UpdateBuilder) ExecContext(_ context.Context) (sql.Result, interface { - Error() string -}) { +func (_ UpdateBuilder) ExecContext(_ context.Context) (sql.Result, error) { return nil, nil } @@ -392,15 +345,11 @@ func (_ UpdateBuilder) Prefix(_ string, _ ...interface{}) UpdateBuilder { return UpdateBuilder{} } -func (_ UpdateBuilder) Query() (*sql.Rows, interface { - Error() string -}) { +func (_ UpdateBuilder) Query() (*sql.Rows, error) { return nil, nil } -func (_ UpdateBuilder) QueryContext(_ context.Context) (*sql.Rows, interface { - Error() string -}) { +func (_ UpdateBuilder) QueryContext(_ context.Context) (*sql.Rows, error) { return nil, nil } @@ -416,15 +365,11 @@ func (_ UpdateBuilder) RunWith(_ BaseRunner) UpdateBuilder { return UpdateBuilder{} } -func (_ UpdateBuilder) Scan(_ ...interface{}) interface { - Error() string -} { +func (_ UpdateBuilder) Scan(_ ...interface{}) error { return nil } -func (_ UpdateBuilder) ScanContext(_ context.Context, _ ...interface{}) interface { - Error() string -} { +func (_ UpdateBuilder) ScanContext(_ context.Context, _ ...interface{}) error { return nil } @@ -444,9 +389,7 @@ func (_ UpdateBuilder) Table(_ string) UpdateBuilder { return UpdateBuilder{} } -func (_ UpdateBuilder) ToSql() (string, []interface{}, interface { - Error() string -}) { +func (_ UpdateBuilder) ToSql() (string, []interface{}, error) { return "", nil, nil } diff --git a/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/LICENSE b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/bson/primitive/stub.go b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/bson/primitive/stub.go new file mode 100644 index 00000000000..6f07aaff4ee --- /dev/null +++ b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/bson/primitive/stub.go @@ -0,0 +1,23 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go.mongodb.org/mongo-driver/bson/primitive, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go.mongodb.org/mongo-driver/bson/primitive (exports: D; functions: ) + +// Package primitive is a stub of go.mongodb.org/mongo-driver/bson/primitive, generated by depstubber. +package primitive + +import () + +type D []E + +func (_ D) Map() M { + return nil +} + +type E struct { + Key string + Value interface{} +} + +type M map[string]interface{} diff --git a/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/bson/stub.go b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/bson/stub.go new file mode 100644 index 00000000000..de80f55501f --- /dev/null +++ b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/bson/stub.go @@ -0,0 +1,5 @@ +package bson + +import "go.mongodb.org/mongo-driver/bson/primitive" + +type D = primitive.D diff --git a/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/mongo/options/stub.go b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/mongo/options/stub.go new file mode 100644 index 00000000000..f435b8e112f --- /dev/null +++ b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/mongo/options/stub.go @@ -0,0 +1,217 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go.mongodb.org/mongo-driver/mongo/options, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go.mongodb.org/mongo-driver/mongo/options (exports: ; functions: Client) + +// Package options is a stub of go.mongodb.org/mongo-driver/mongo/options, generated by depstubber. +package options + +import ( + context "context" + tls "crypto/tls" + net "net" + time "time" +) + +type AutoEncryptionOptions struct { + KeyVaultClientOptions *ClientOptions + KeyVaultNamespace string + KmsProviders map[string]map[string]interface{} + SchemaMap map[string]interface{} + BypassAutoEncryption *bool + ExtraOptions map[string]interface{} +} + +func (_ *AutoEncryptionOptions) SetBypassAutoEncryption(_ bool) *AutoEncryptionOptions { + return nil +} + +func (_ *AutoEncryptionOptions) SetExtraOptions(_ map[string]interface{}) *AutoEncryptionOptions { + return nil +} + +func (_ *AutoEncryptionOptions) SetKeyVaultClientOptions(_ *ClientOptions) *AutoEncryptionOptions { + return nil +} + +func (_ *AutoEncryptionOptions) SetKeyVaultNamespace(_ string) *AutoEncryptionOptions { + return nil +} + +func (_ *AutoEncryptionOptions) SetKmsProviders(_ map[string]map[string]interface{}) *AutoEncryptionOptions { + return nil +} + +func (_ *AutoEncryptionOptions) SetSchemaMap(_ map[string]interface{}) *AutoEncryptionOptions { + return nil +} + +func Client() *ClientOptions { + return nil +} + +type ClientOptions struct { + AppName *string + Auth *Credential + ConnectTimeout *time.Duration + Compressors []string + Dialer ContextDialer + HeartbeatInterval *time.Duration + Hosts []string + LocalThreshold *time.Duration + MaxConnIdleTime *time.Duration + MaxPoolSize *uint64 + MinPoolSize *uint64 + PoolMonitor interface{} + Monitor interface{} + ReadConcern interface{} + ReadPreference interface{} + Registry interface{} + ReplicaSet *string + RetryWrites *bool + RetryReads *bool + ServerSelectionTimeout *time.Duration + Direct *bool + SocketTimeout *time.Duration + TLSConfig *tls.Config + WriteConcern interface{} + ZlibLevel *int + ZstdLevel *int + AutoEncryptionOptions *AutoEncryptionOptions + AuthenticateToAnything *bool + Deployment interface{} +} + +func (_ *ClientOptions) ApplyURI(_ string) *ClientOptions { + return nil +} + +func (_ *ClientOptions) GetURI() string { + return "" +} + +func (_ *ClientOptions) SetAppName(_ string) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetAuth(_ Credential) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetAutoEncryptionOptions(_ *AutoEncryptionOptions) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetCompressors(_ []string) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetConnectTimeout(_ time.Duration) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetDialer(_ ContextDialer) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetDirect(_ bool) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetHeartbeatInterval(_ time.Duration) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetHosts(_ []string) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetLocalThreshold(_ time.Duration) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetMaxConnIdleTime(_ time.Duration) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetMaxPoolSize(_ uint64) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetMinPoolSize(_ uint64) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetMonitor(_ interface{}) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetPoolMonitor(_ interface{}) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetReadConcern(_ interface{}) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetReadPreference(_ interface{}) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetRegistry(_ interface{}) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetReplicaSet(_ string) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetRetryReads(_ bool) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetRetryWrites(_ bool) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetServerSelectionTimeout(_ time.Duration) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetSocketTimeout(_ time.Duration) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetTLSConfig(_ *tls.Config) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetWriteConcern(_ interface{}) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetZlibLevel(_ int) *ClientOptions { + return nil +} + +func (_ *ClientOptions) SetZstdLevel(_ int) *ClientOptions { + return nil +} + +func (_ *ClientOptions) Validate() error { + return nil +} + +type ContextDialer interface { + DialContext(_ context.Context, _ string, _ string) (net.Conn, error) +} + +type Credential struct { + AuthMechanism string + AuthMechanismProperties map[string]string + AuthSource string + Username string + Password string + PasswordSet bool +} diff --git a/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/mongo/stub.go b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/mongo/stub.go new file mode 100644 index 00000000000..8cdf40dc69e --- /dev/null +++ b/ql/test/query-tests/Security/CWE-089/vendor/go.mongodb.org/mongo-driver/mongo/stub.go @@ -0,0 +1,393 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go.mongodb.org/mongo-driver/mongo, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go.mongodb.org/mongo-driver/mongo (exports: Pipeline; functions: Connect) + +// Package mongo is a stub of go.mongodb.org/mongo-driver/mongo, generated by depstubber. +package mongo + +import ( + context "context" + time "time" +) + +type BulkWriteResult struct { + InsertedCount int64 + MatchedCount int64 + ModifiedCount int64 + DeletedCount int64 + UpsertedCount int64 + UpsertedIDs map[int64]interface{} +} + +type ChangeStream struct { + Current interface{} +} + +func (_ *ChangeStream) Close(_ context.Context) error { + return nil +} + +func (_ *ChangeStream) Decode(_ interface{}) error { + return nil +} + +func (_ *ChangeStream) Err() error { + return nil +} + +func (_ *ChangeStream) ID() int64 { + return 0 +} + +func (_ *ChangeStream) Next(_ context.Context) bool { + return false +} + +func (_ *ChangeStream) ResumeToken() interface{} { + return nil +} + +func (_ *ChangeStream) TryNext(_ context.Context) bool { + return false +} + +type Client struct{} + +func (_ *Client) Connect(_ context.Context) error { + return nil +} + +func (_ *Client) Database(_ string, _ ...interface{}) *Database { + return nil +} + +func (_ *Client) Disconnect(_ context.Context) error { + return nil +} + +func (_ *Client) ListDatabaseNames(_ context.Context, _ interface{}, _ ...interface{}) ([]string, error) { + return nil, nil +} + +func (_ *Client) ListDatabases(_ context.Context, _ interface{}, _ ...interface{}) (ListDatabasesResult, error) { + return ListDatabasesResult{}, nil +} + +func (_ *Client) NumberSessionsInProgress() int { + return 0 +} + +func (_ *Client) Ping(_ context.Context, _ interface{}) error { + return nil +} + +func (_ *Client) StartSession(_ ...interface{}) (Session, error) { + return nil, nil +} + +func (_ *Client) UseSession(_ context.Context, _ func(SessionContext) error) error { + return nil +} + +func (_ *Client) UseSessionWithOptions(_ context.Context, _ interface{}, _ func(SessionContext) error) error { + return nil +} + +func (_ *Client) Watch(_ context.Context, _ interface{}, _ ...interface{}) (*ChangeStream, error) { + return nil, nil +} + +type Collection struct{} + +func (_ *Collection) Aggregate(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Collection) BulkWrite(_ context.Context, _ []WriteModel, _ ...interface{}) (*BulkWriteResult, error) { + return nil, nil +} + +func (_ *Collection) Clone(_ ...interface{}) (*Collection, error) { + return nil, nil +} + +func (_ *Collection) CountDocuments(_ context.Context, _ interface{}, _ ...interface{}) (int64, error) { + return 0, nil +} + +func (_ *Collection) Database() *Database { + return nil +} + +func (_ *Collection) DeleteMany(_ context.Context, _ interface{}, _ ...interface{}) (*DeleteResult, error) { + return nil, nil +} + +func (_ *Collection) DeleteOne(_ context.Context, _ interface{}, _ ...interface{}) (*DeleteResult, error) { + return nil, nil +} + +func (_ *Collection) Distinct(_ context.Context, _ string, _ interface{}, _ ...interface{}) ([]interface{}, error) { + return nil, nil +} + +func (_ *Collection) Drop(_ context.Context) error { + return nil +} + +func (_ *Collection) EstimatedDocumentCount(_ context.Context, _ ...interface{}) (int64, error) { + return 0, nil +} + +func (_ *Collection) Find(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Collection) FindOne(_ context.Context, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Collection) FindOneAndDelete(_ context.Context, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Collection) FindOneAndReplace(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Collection) FindOneAndUpdate(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Collection) Indexes() IndexView { + return IndexView{} +} + +func (_ *Collection) InsertMany(_ context.Context, _ []interface{}, _ ...interface{}) (*InsertManyResult, error) { + return nil, nil +} + +func (_ *Collection) InsertOne(_ context.Context, _ interface{}, _ ...interface{}) (*InsertOneResult, error) { + return nil, nil +} + +func (_ *Collection) Name() string { + return "" +} + +func (_ *Collection) ReplaceOne(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (*UpdateResult, error) { + return nil, nil +} + +func (_ *Collection) UpdateMany(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (*UpdateResult, error) { + return nil, nil +} + +func (_ *Collection) UpdateOne(_ context.Context, _ interface{}, _ interface{}, _ ...interface{}) (*UpdateResult, error) { + return nil, nil +} + +func (_ *Collection) Watch(_ context.Context, _ interface{}, _ ...interface{}) (*ChangeStream, error) { + return nil, nil +} + +func Connect(_ context.Context, _ ...interface{}) (*Client, error) { + return nil, nil +} + +type Cursor struct { + Current interface{} +} + +func (_ *Cursor) All(_ context.Context, _ interface{}) error { + return nil +} + +func (_ *Cursor) Close(_ context.Context) error { + return nil +} + +func (_ *Cursor) Decode(_ interface{}) error { + return nil +} + +func (_ *Cursor) Err() error { + return nil +} + +func (_ *Cursor) ID() int64 { + return 0 +} + +func (_ *Cursor) Next(_ context.Context) bool { + return false +} + +func (_ *Cursor) TryNext(_ context.Context) bool { + return false +} + +type Database struct{} + +func (_ *Database) Aggregate(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Database) Client() *Client { + return nil +} + +func (_ *Database) Collection(_ string, _ ...interface{}) *Collection { + return nil +} + +func (_ *Database) Drop(_ context.Context) error { + return nil +} + +func (_ *Database) ListCollectionNames(_ context.Context, _ interface{}, _ ...interface{}) ([]string, error) { + return nil, nil +} + +func (_ *Database) ListCollections(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Database) Name() string { + return "" +} + +func (_ *Database) ReadConcern() interface{} { + return nil +} + +func (_ *Database) ReadPreference() interface{} { + return nil +} + +func (_ *Database) RunCommand(_ context.Context, _ interface{}, _ ...interface{}) *SingleResult { + return nil +} + +func (_ *Database) RunCommandCursor(_ context.Context, _ interface{}, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +func (_ *Database) Watch(_ context.Context, _ interface{}, _ ...interface{}) (*ChangeStream, error) { + return nil, nil +} + +func (_ *Database) WriteConcern() interface{} { + return nil +} + +type DatabaseSpecification struct { + Name string + SizeOnDisk int64 + Empty bool +} + +type DeleteResult struct { + DeletedCount int64 +} + +type IndexModel struct { + Keys interface{} + Options interface{} +} + +type IndexView struct{} + +func (_ IndexView) CreateMany(_ context.Context, _ []IndexModel, _ ...interface{}) ([]string, error) { + return nil, nil +} + +func (_ IndexView) CreateOne(_ context.Context, _ IndexModel, _ ...interface{}) (string, error) { + return "", nil +} + +func (_ IndexView) DropAll(_ context.Context, _ ...interface{}) (interface{}, error) { + return nil, nil +} + +func (_ IndexView) DropOne(_ context.Context, _ string, _ ...interface{}) (interface{}, error) { + return nil, nil +} + +func (_ IndexView) List(_ context.Context, _ ...interface{}) (*Cursor, error) { + return nil, nil +} + +type InsertManyResult struct { + InsertedIDs []interface{} +} + +type InsertOneResult struct { + InsertedID interface{} +} + +type ListDatabasesResult struct { + Databases []DatabaseSpecification + TotalSize int64 +} + +type Pipeline []interface{} + +type Session interface { + AbortTransaction(_ context.Context) error + AdvanceClusterTime(_ interface{}) error + AdvanceOperationTime(_ interface{}) error + Client() *Client + ClusterTime() interface{} + CommitTransaction(_ context.Context) error + EndSession(_ context.Context) + OperationTime() interface{} + StartTransaction(_ ...interface{}) error + WithTransaction(_ context.Context, _ func(SessionContext) (interface{}, error), _ ...interface{}) (interface{}, error) +} + +type SessionContext interface { + AbortTransaction(_ context.Context) error + AdvanceClusterTime(_ interface{}) error + AdvanceOperationTime(_ interface{}) error + Client() *Client + ClusterTime() interface{} + CommitTransaction(_ context.Context) error + Deadline() (time.Time, bool) + Done() <-chan struct{} + EndSession(_ context.Context) + Err() error + OperationTime() interface{} + StartTransaction(_ ...interface{}) error + Value(_ interface{}) interface{} + WithTransaction(_ context.Context, _ func(SessionContext) (interface{}, error), _ ...interface{}) (interface{}, error) +} + +type SingleResult struct{} + +func (_ *SingleResult) Decode(_ interface{}) error { + return nil +} + +func (_ *SingleResult) DecodeBytes() (interface{}, error) { + return nil, nil +} + +func (_ *SingleResult) Err() error { + return nil +} + +type UpdateResult struct { + MatchedCount int64 + ModifiedCount int64 + UpsertedCount int64 + UpsertedID interface{} +} + +func (_ *UpdateResult) UnmarshalBSON(_ []byte) error { + return nil +} + +type WriteModel interface{} diff --git a/ql/test/query-tests/Security/CWE-089/vendor/modules.txt b/ql/test/query-tests/Security/CWE-089/vendor/modules.txt index c32aa371c7a..ddbc30953a2 100644 --- a/ql/test/query-tests/Security/CWE-089/vendor/modules.txt +++ b/ql/test/query-tests/Security/CWE-089/vendor/modules.txt @@ -1,15 +1,6 @@ # github.com/Masterminds/squirrel v1.1.0 ## explicit github.com/Masterminds/squirrel -# github.com/github/depstubber v0.0.0-20200414023404-c355b630c381 +# go.mongodb.org/mongo-driver v1.3.3 ## explicit -github.com/github/depstubber -# github.com/go-sql-driver/mysql v1.5.0 -## explicit -github.com/go-sql-driver/mysql -# github.com/lib/pq v1.3.0 -## explicit -github.com/lib/pq -# github.com/mattn/go-sqlite3 v2.0.3+incompatible -## explicit -github.com/mattn/go-sqlite3 +go.mongodb.org/mongo-driver diff --git a/ql/test/query-tests/Security/CWE-312/go.mod b/ql/test/query-tests/Security/CWE-312/go.mod index 358ac75f5d0..d56c33ffc85 100644 --- a/ql/test/query-tests/Security/CWE-312/go.mod +++ b/ql/test/query-tests/Security/CWE-312/go.mod @@ -3,7 +3,6 @@ module main go 1.14 require ( - github.com/github/depstubber v0.0.0-20200414023404-c355b630c381 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/sirupsen/logrus v1.5.0 ) diff --git a/ql/test/query-tests/Security/CWE-312/vendor/modules.txt b/ql/test/query-tests/Security/CWE-312/vendor/modules.txt index e6b6e8f0401..b0149947f41 100644 --- a/ql/test/query-tests/Security/CWE-312/vendor/modules.txt +++ b/ql/test/query-tests/Security/CWE-312/vendor/modules.txt @@ -1,6 +1,3 @@ -# github.com/github/depstubber v0.0.0-20200414023404-c355b630c381 -## explicit -github.com/github/depstubber # github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b ## explicit github.com/golang/glog diff --git a/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/BadRedirectCheck.expected b/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/BadRedirectCheck.expected index e2830337608..8b27ffececd 100644 --- a/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/BadRedirectCheck.expected +++ b/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/BadRedirectCheck.expected @@ -1,5 +1,45 @@ -| BadRedirectCheck.go:4:23:4:37 | ...==... | This expression checks '$@' for a leading slash but checks do not exist for both '/' and '\\' in the second position. | BadRedirectCheck.go:3:18:3:22 | (def@3:18) | redir | -| cves.go:11:26:11:38 | ...==... | This expression checks '$@' for a leading slash but checks do not exist for both '/' and '\\' in the second position. | cves.go:10:22:10:24 | (def@10:22) | url | -| cves.go:22:6:22:37 | call to HasPrefix | This expression checks '$@' for a leading slash but checks do not exist for both '/' and '\\' in the second position. | cves.go:21:2:21:9 | (def@21:2) | redirect | -| cves.go:29:6:29:37 | call to HasPrefix | This expression checks '$@' for a leading slash but checks do not exist for both '/' and '\\' in the second position. | cves.go:28:2:28:9 | (def@28:2) | redirect | -| main.go:8:7:8:38 | call to HasPrefix | This expression checks '$@' for a leading slash but checks do not exist for both '/' and '\\' in the second position. | main.go:5:19:5:26 | (def@5:19) | redirect | +edges +| BadRedirectCheck.go:3:18:3:22 | argument corresponding to redir : string | BadRedirectCheck.go:5:10:5:14 | redir : string | +| BadRedirectCheck.go:5:10:5:14 | redir : string | main.go:11:25:11:45 | call to sanitizeUrl | +| cves.go:14:23:14:25 | argument corresponding to url : string | cves.go:16:26:16:28 | url | +| cves.go:33:14:33:34 | call to Get : string | cves.go:37:25:37:32 | redirect | +| cves.go:41:14:41:34 | call to Get : string | cves.go:45:25:45:32 | redirect | +| main.go:10:18:10:25 | argument corresponding to redirect : string | main.go:11:37:11:44 | redirect : string | +| main.go:11:37:11:44 | redirect : string | main.go:11:25:11:45 | call to sanitizeUrl | +| main.go:32:24:32:26 | argument corresponding to url : string | main.go:34:26:34:28 | url | +| main.go:68:17:68:24 | argument corresponding to redirect : string | main.go:73:9:73:28 | call to Clean : string | +| main.go:73:9:73:28 | call to Clean : string | main.go:77:25:77:39 | call to getTarget1 | +| main.go:76:19:76:21 | argument corresponding to url : string | main.go:77:36:77:38 | url : string | +| main.go:77:36:77:38 | url : string | main.go:77:25:77:39 | call to getTarget1 | +| main.go:87:9:87:14 | selection of Path : string | main.go:91:25:91:39 | call to getTarget2 | +nodes +| BadRedirectCheck.go:3:18:3:22 | argument corresponding to redir : string | semmle.label | argument corresponding to redir : string | +| BadRedirectCheck.go:5:10:5:14 | redir : string | semmle.label | redir : string | +| cves.go:14:23:14:25 | argument corresponding to url : string | semmle.label | argument corresponding to url : string | +| cves.go:16:26:16:28 | url | semmle.label | url | +| cves.go:33:14:33:34 | call to Get : string | semmle.label | call to Get : string | +| cves.go:37:25:37:32 | redirect | semmle.label | redirect | +| cves.go:41:14:41:34 | call to Get : string | semmle.label | call to Get : string | +| cves.go:45:25:45:32 | redirect | semmle.label | redirect | +| main.go:10:18:10:25 | argument corresponding to redirect : string | semmle.label | argument corresponding to redirect : string | +| main.go:11:25:11:45 | call to sanitizeUrl | semmle.label | call to sanitizeUrl | +| main.go:11:37:11:44 | redirect : string | semmle.label | redirect : string | +| main.go:32:24:32:26 | argument corresponding to url : string | semmle.label | argument corresponding to url : string | +| main.go:34:26:34:28 | url | semmle.label | url | +| main.go:68:17:68:24 | argument corresponding to redirect : string | semmle.label | argument corresponding to redirect : string | +| main.go:73:9:73:28 | call to Clean : string | semmle.label | call to Clean : string | +| main.go:76:19:76:21 | argument corresponding to url : string | semmle.label | argument corresponding to url : string | +| main.go:77:25:77:39 | call to getTarget1 | semmle.label | call to getTarget1 | +| main.go:77:36:77:38 | url : string | semmle.label | url : string | +| main.go:87:9:87:14 | selection of Path : string | semmle.label | selection of Path : string | +| main.go:91:25:91:39 | call to getTarget2 | semmle.label | call to getTarget2 | +#select +| BadRedirectCheck.go:4:23:4:37 | ...==... | BadRedirectCheck.go:3:18:3:22 | argument corresponding to redir : string | main.go:11:25:11:45 | call to sanitizeUrl | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | BadRedirectCheck.go:3:18:3:22 | argument corresponding to redir | this value | main.go:11:25:11:45 | call to sanitizeUrl | redirect | +| BadRedirectCheck.go:4:23:4:37 | ...==... | main.go:10:18:10:25 | argument corresponding to redirect : string | main.go:11:25:11:45 | call to sanitizeUrl | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | main.go:10:18:10:25 | argument corresponding to redirect | this value | main.go:11:25:11:45 | call to sanitizeUrl | redirect | +| cves.go:11:26:11:38 | ...==... | cves.go:14:23:14:25 | argument corresponding to url : string | cves.go:16:26:16:28 | url | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | cves.go:14:23:14:25 | argument corresponding to url | this value | cves.go:16:26:16:28 | url | redirect | +| cves.go:34:6:34:37 | call to HasPrefix | cves.go:33:14:33:34 | call to Get : string | cves.go:37:25:37:32 | redirect | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | cves.go:33:14:33:34 | call to Get | this value | cves.go:37:25:37:32 | redirect | redirect | +| cves.go:42:6:42:37 | call to HasPrefix | cves.go:41:14:41:34 | call to Get : string | cves.go:45:25:45:32 | redirect | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | cves.go:41:14:41:34 | call to Get | this value | cves.go:45:25:45:32 | redirect | redirect | +| main.go:25:7:25:38 | call to HasPrefix | main.go:32:24:32:26 | argument corresponding to url : string | main.go:34:26:34:28 | url | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | main.go:32:24:32:26 | argument corresponding to url | this value | main.go:34:26:34:28 | url | redirect | +| main.go:69:5:69:22 | ...!=... | main.go:68:17:68:24 | argument corresponding to redirect : string | main.go:77:25:77:39 | call to getTarget1 | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | main.go:68:17:68:24 | argument corresponding to redirect | this value | main.go:77:25:77:39 | call to getTarget1 | redirect | +| main.go:69:5:69:22 | ...!=... | main.go:76:19:76:21 | argument corresponding to url : string | main.go:77:25:77:39 | call to getTarget1 | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | main.go:76:19:76:21 | argument corresponding to url | this value | main.go:77:25:77:39 | call to getTarget1 | redirect | +| main.go:83:5:83:20 | ...!=... | main.go:87:9:87:14 | selection of Path : string | main.go:91:25:91:39 | call to getTarget2 | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | main.go:87:9:87:14 | selection of Path | this value | main.go:91:25:91:39 | call to getTarget2 | redirect | diff --git a/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/BadRedirectCheckGood.go b/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/BadRedirectCheckGood.go index 89af5110a29..fe5fcbb2fde 100644 --- a/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/BadRedirectCheckGood.go +++ b/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/BadRedirectCheckGood.go @@ -1,6 +1,6 @@ package main -func sanitizeUrl1(redir string) string { +func sanitizeUrlGood(redir string) string { if len(redir) > 1 && redir[0] == '/' && redir[1] != '/' && redir[1] != '\\' { return redir } diff --git a/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/cves.go b/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/cves.go index cd91df4d4a7..42e8bab3452 100644 --- a/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/cves.go +++ b/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/cves.go @@ -11,10 +11,22 @@ func isValidRedirect(url string) bool { return len(url) >= 2 && url[0] == '/' && url[1] != '/' // NOT OK } -func isValidRedirect1(url string) bool { +func alsoABadRedirect(url string, rw http.ResponseWriter, req *http.Request) { + if isValidRedirect(url) { + http.Redirect(rw, req, url, 302) + } +} + +func isValidRedirectGood(url string) bool { return len(url) >= 2 && url[0] == '/' && url[1] != '/' && url[1] != '\\' // OK } +func alsoAGoodRedirect(url string, rw http.ResponseWriter, req *http.Request) { + if isValidRedirectGood(url) { + http.Redirect(rw, req, url, 302) + } +} + // CVE-2017-1000070 (both vulnerable!) // Code from github.com/bitly/oauth2_proxy func OAuthCallback(rw http.ResponseWriter, req *http.Request) { @@ -22,6 +34,7 @@ func OAuthCallback(rw http.ResponseWriter, req *http.Request) { if !strings.HasPrefix(redirect, "/") { // NOT OK redirect = "/" } + http.Redirect(rw, req, redirect, 302) } func OAuthCallback1(rw http.ResponseWriter, req *http.Request) { @@ -29,4 +42,5 @@ func OAuthCallback1(rw http.ResponseWriter, req *http.Request) { if !strings.HasPrefix(redirect, "/") || strings.HasPrefix(redirect, "//") { // NOT OK redirect = "/" } + http.Redirect(rw, req, redirect, 302) } diff --git a/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/main.go b/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/main.go index 97b7b9c5f89..beccc9a135d 100644 --- a/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/main.go +++ b/ql/test/query-tests/Security/CWE-601/BadRedirectCheck/main.go @@ -1,6 +1,23 @@ package main -import "strings" +import ( + "net/http" + "net/url" + "path" + "strings" +) + +func badRedirect(redirect string, rw http.ResponseWriter, req *http.Request) { + http.Redirect(rw, req, sanitizeUrl(redirect), 302) +} + +func goodRedirect(redirect string, rw http.ResponseWriter, req *http.Request) { + http.Redirect(rw, req, sanitizeUrlGood(redirect), 302) +} + +func goodRedirect2(url string, rw http.ResponseWriter, req *http.Request) { + http.Redirect(rw, req, path.Join("/", sanitizeUrl(url)), 302) +} func isValidRedir(redirect string) bool { switch { @@ -12,6 +29,12 @@ func isValidRedir(redirect string) bool { } } +func alsoABadRedirect1(url string, rw http.ResponseWriter, req *http.Request) { + if isValidRedir(url) { + http.Redirect(rw, req, url, 302) + } +} + func isValidRedir1(redirect string) bool { switch { // OK @@ -21,3 +44,49 @@ func isValidRedir1(redirect string) bool { return false } } + +func goodRedirect3(url string, rw http.ResponseWriter, req *http.Request) { + if isValidRedirectGood(url) { + http.Redirect(rw, req, url, 302) + } +} + +func getTarget(redirect string) string { + u, _ := url.Parse(redirect) + + if u.Path[0] != '/' { + return "/" + } + + return path.Clean(u.Path) +} + +func goodRedirect4(url string, rw http.ResponseWriter, req *http.Request) { + http.Redirect(rw, req, getTarget(url), 302) +} + +func getTarget1(redirect string) string { + if redirect[0] != '/' { + return "/" + } + + return path.Clean(redirect) +} + +func badRedirect1(url string, rw http.ResponseWriter, req *http.Request) { + http.Redirect(rw, req, getTarget1(url), 302) +} + +func getTarget2(redirect string) string { + u, _ := url.Parse(redirect) + + if u.Path[0] != '/' { + return "/" + } + + return u.Path +} + +func badRedirect2(url string, rw http.ResponseWriter, req *http.Request) { + http.Redirect(rw, req, getTarget2(url), 302) +} diff --git a/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirect.expected b/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirect.expected index 88af62567b3..e1c953408ff 100644 --- a/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirect.expected +++ b/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirect.expected @@ -15,6 +15,7 @@ edges | stdlib.go:146:11:146:15 | selection of URL : pointer type | stdlib.go:149:24:149:35 | call to String | | stdlib.go:160:35:160:39 | selection of URL : pointer type | stdlib.go:160:24:160:52 | ...+... | | stdlib.go:160:35:160:39 | selection of URL : pointer type | stdlib.go:160:24:160:52 | ...+... | +| stdlib.go:169:13:169:33 | call to FormValue : string | stdlib.go:171:23:171:28 | target | nodes | OpenUrlRedirect.go:10:23:10:28 | selection of Form : Values | semmle.label | selection of Form : Values | | OpenUrlRedirect.go:10:23:10:42 | call to Get | semmle.label | call to Get | @@ -44,6 +45,8 @@ nodes | stdlib.go:160:24:160:52 | ...+... | semmle.label | ...+... | | stdlib.go:160:35:160:39 | selection of URL : pointer type | semmle.label | selection of URL : pointer type | | stdlib.go:160:35:160:39 | selection of URL : pointer type | semmle.label | selection of URL : pointer type | +| stdlib.go:169:13:169:33 | call to FormValue : string | semmle.label | call to FormValue : string | +| stdlib.go:171:23:171:28 | target | semmle.label | target | #select | OpenUrlRedirect.go:10:23:10:42 | call to Get | OpenUrlRedirect.go:10:23:10:28 | selection of Form : Values | OpenUrlRedirect.go:10:23:10:42 | call to Get | Untrusted URL redirection due to $@. | OpenUrlRedirect.go:10:23:10:28 | selection of Form | user-provided value | | stdlib.go:14:30:14:35 | target | stdlib.go:12:13:12:18 | selection of Form : Values | stdlib.go:14:30:14:35 | target | Untrusted URL redirection due to $@. | stdlib.go:12:13:12:18 | selection of Form | user-provided value | @@ -53,3 +56,4 @@ nodes | stdlib.go:66:23:66:40 | ...+... | stdlib.go:63:13:63:18 | selection of Form : Values | stdlib.go:66:23:66:40 | ...+... | Untrusted URL redirection due to $@. | stdlib.go:63:13:63:18 | selection of Form | user-provided value | | stdlib.go:91:23:91:28 | target | stdlib.go:88:13:88:18 | selection of Form : Values | stdlib.go:91:23:91:28 | target | Untrusted URL redirection due to $@. | stdlib.go:88:13:88:18 | selection of Form | user-provided value | | stdlib.go:139:23:139:28 | target | stdlib.go:133:13:133:18 | selection of Form : Values | stdlib.go:139:23:139:28 | target | Untrusted URL redirection due to $@. | stdlib.go:133:13:133:18 | selection of Form | user-provided value | +| stdlib.go:171:23:171:28 | target | stdlib.go:169:13:169:33 | call to FormValue : string | stdlib.go:171:23:171:28 | target | Untrusted URL redirection due to $@. | stdlib.go:169:13:169:33 | call to FormValue | user-provided value | diff --git a/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/stdlib.go b/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/stdlib.go index 50f74fc8689..edb152684ef 100644 --- a/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/stdlib.go +++ b/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/stdlib.go @@ -163,5 +163,13 @@ func serveStdlib() { } }) + http.HandleFunc("/ex9", func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + + target := r.FormValue("target") + // BAD: a request parameter is incorporated without validation into a URL redirect + http.Redirect(w, r, target, 301) + }) + http.ListenAndServe(":80", nil) } diff --git a/ql/test/query-tests/Security/CWE-643/go.mod b/ql/test/query-tests/Security/CWE-643/go.mod index 92c3e25f94c..fc7016cc9f6 100644 --- a/ql/test/query-tests/Security/CWE-643/go.mod +++ b/ql/test/query-tests/Security/CWE-643/go.mod @@ -8,13 +8,7 @@ require ( github.com/antchfx/jsonquery v1.1.2 github.com/antchfx/xmlquery v1.2.3 github.com/antchfx/xpath v1.1.5 - github.com/github/depstubber v0.0.0-20200413231600-392d5a70208e // indirect github.com/go-xmlpath/xmlpath v0.0.0-20150820204837-860cbeca3ebc - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/jbowtie/gokogiri v0.0.0-20190301021639-37f655d3078f - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/santhosh-tekuri/xpathparser v1.0.0 - golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect - gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc // indirect ) diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/ChrisTrenkamp/goxpath/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/ChrisTrenkamp/goxpath/stub.go index 9fc8651cf61..4b1e74cc192 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/ChrisTrenkamp/goxpath/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/ChrisTrenkamp/goxpath/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/ChrisTrenkamp/goxpath, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/ChrisTrenkamp/goxpath (exports: Opts,FuncOpts,XPathExec; functions: Parse,MustParse,ParseExec) // Package goxpath is a stub of github.com/ChrisTrenkamp/goxpath, generated by depstubber. @@ -22,41 +23,29 @@ type Opts struct { Vars map[string]interface{} } -func Parse(_ string) (XPathExec, interface { - Error() string -}) { +func Parse(_ string) (XPathExec, error) { return XPathExec{}, nil } -func ParseExec(_ string, _ interface{}, _ ...FuncOpts) (interface{}, interface { - Error() string -}) { +func ParseExec(_ string, _ interface{}, _ ...FuncOpts) (interface{}, error) { return nil, nil } type XPathExec struct{} -func (_ XPathExec) Exec(_ interface{}, _ ...FuncOpts) (interface{}, interface { - Error() string -}) { +func (_ XPathExec) Exec(_ interface{}, _ ...FuncOpts) (interface{}, error) { return nil, nil } -func (_ XPathExec) ExecBool(_ interface{}, _ ...FuncOpts) (bool, interface { - Error() string -}) { +func (_ XPathExec) ExecBool(_ interface{}, _ ...FuncOpts) (bool, error) { return false, nil } -func (_ XPathExec) ExecNode(_ interface{}, _ ...FuncOpts) (interface{}, interface { - Error() string -}) { +func (_ XPathExec) ExecNode(_ interface{}, _ ...FuncOpts) (interface{}, error) { return nil, nil } -func (_ XPathExec) ExecNum(_ interface{}, _ ...FuncOpts) (float64, interface { - Error() string -}) { +func (_ XPathExec) ExecNum(_ interface{}, _ ...FuncOpts) (float64, error) { return 0, nil } diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/ChrisTrenkamp/goxpath/tree/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/ChrisTrenkamp/goxpath/tree/stub.go index 9c538842b2a..7cc3b01c626 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/ChrisTrenkamp/goxpath/tree/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/ChrisTrenkamp/goxpath/tree/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/ChrisTrenkamp/goxpath/tree, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/ChrisTrenkamp/goxpath/tree (exports: Node,String; functions: ) // Package tree is a stub of github.com/ChrisTrenkamp/goxpath/tree, generated by depstubber. diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/htmlquery/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/htmlquery/stub.go index 0f7f7095aa8..0bac0acfe55 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/htmlquery/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/htmlquery/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/antchfx/htmlquery, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/antchfx/htmlquery (exports: ; functions: Find,FindOne,QueryAll,Query) // Package htmlquery is a stub of github.com/antchfx/htmlquery, generated by depstubber. @@ -8,22 +9,18 @@ package htmlquery import () -func Find(_ *interface{}, _ string) []*interface{} { +func Find(_ interface{}, _ string) []interface{} { return nil } -func FindOne(_ *interface{}, _ string) *interface{} { +func FindOne(_ interface{}, _ string) interface{} { return nil } -func Query(_ *interface{}, _ string) (*interface{}, interface { - Error() string -}) { +func Query(_ interface{}, _ string) (interface{}, error) { return nil, nil } -func QueryAll(_ *interface{}, _ string) ([]*interface{}, interface { - Error() string -}) { +func QueryAll(_ interface{}, _ string) ([]interface{}, error) { return nil, nil } diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/jsonquery/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/jsonquery/stub.go index 8aca2f88266..2948daae031 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/jsonquery/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/jsonquery/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/antchfx/jsonquery, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/antchfx/jsonquery (exports: Node; functions: Find,FindOne,QueryAll,Query) // Package jsonquery is a stub of github.com/antchfx/jsonquery, generated by depstubber. @@ -40,14 +41,10 @@ func (_ *Node) SelectElement(_ string) *Node { type NodeType uint -func Query(_ *Node, _ string) (*Node, interface { - Error() string -}) { +func Query(_ *Node, _ string) (*Node, error) { return nil, nil } -func QueryAll(_ *Node, _ string) ([]*Node, interface { - Error() string -}) { +func QueryAll(_ *Node, _ string) ([]*Node, error) { return nil, nil } diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/xmlquery/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/xmlquery/stub.go index 771d87641fc..a4c5d8cc806 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/xmlquery/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/xmlquery/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/antchfx/xmlquery, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/antchfx/xmlquery (exports: Node; functions: Find,FindOne,FindEach,FindEachWithBreak,QueryAll,Query) // Package xmlquery is a stub of github.com/antchfx/xmlquery, generated by depstubber. @@ -57,14 +58,10 @@ func (_ *Node) SelectElements(_ string) []*Node { type NodeType uint -func Query(_ *Node, _ string) (*Node, interface { - Error() string -}) { +func Query(_ *Node, _ string) (*Node, error) { return nil, nil } -func QueryAll(_ *Node, _ string) ([]*Node, interface { - Error() string -}) { +func QueryAll(_ *Node, _ string) ([]*Node, error) { return nil, nil } diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/xpath/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/xpath/stub.go index cb611c75edd..d470abbed9d 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/xpath/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/antchfx/xpath/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/antchfx/xpath, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/antchfx/xpath (exports: ; functions: Compile,MustCompile,Select) // Package xpath is a stub of github.com/antchfx/xpath, generated by depstubber. @@ -8,9 +9,7 @@ package xpath import () -func Compile(_ string) (*Expr, interface { - Error() string -}) { +func Compile(_ string) (*Expr, error) { return nil, nil } diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/go-xmlpath/xmlpath/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/go-xmlpath/xmlpath/stub.go index 7df7a981cdd..2ec18b739df 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/go-xmlpath/xmlpath/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/go-xmlpath/xmlpath/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/go-xmlpath/xmlpath, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/go-xmlpath/xmlpath (exports: ; functions: Compile,MustCompile) // Package xmlpath is a stub of github.com/go-xmlpath/xmlpath, generated by depstubber. @@ -8,9 +9,7 @@ package xmlpath import () -func Compile(_ string) (*Path, interface { - Error() string -}) { +func Compile(_ string) (*Path, error) { return nil, nil } @@ -30,7 +29,7 @@ func MustCompile(_ string) *Path { type Node struct{} -func (_ *Node) Bytes() []uint8 { +func (_ *Node) Bytes() []byte { return nil } @@ -40,7 +39,7 @@ func (_ *Node) String() string { type Path struct{} -func (_ *Path) Bytes(_ *Node) ([]uint8, bool) { +func (_ *Path) Bytes(_ *Node) ([]byte, bool) { return nil, false } diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/jbowtie/gokogiri/xml/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/jbowtie/gokogiri/xml/stub.go index f0ac31d47cb..26db7472cdf 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/jbowtie/gokogiri/xml/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/jbowtie/gokogiri/xml/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/jbowtie/gokogiri/xml, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/jbowtie/gokogiri/xml (exports: Node; functions: ) // Package xml is a stub of github.com/jbowtie/gokogiri/xml, generated by depstubber. @@ -12,21 +13,15 @@ type AttributeNode struct { XmlNode *XmlNode } -func (_ AttributeNode) AddChild(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) AddChild(_ interface{}) error { return nil } -func (_ AttributeNode) AddNextSibling(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) AddNextSibling(_ interface{}) error { return nil } -func (_ AttributeNode) AddPreviousSibling(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) AddPreviousSibling(_ interface{}) error { return nil } @@ -50,9 +45,7 @@ func (_ AttributeNode) Attributes() map[string]*AttributeNode { func (_ AttributeNode) BookkeepFragment(_ *DocumentFragment) {} -func (_ AttributeNode) Coerce(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ AttributeNode) Coerce(_ interface{}) ([]Node, error) { return nil, nil } @@ -102,7 +95,7 @@ func (_ AttributeNode) DocType() NodeType { return 0 } -func (_ AttributeNode) DocXPathCtx() *interface{} { +func (_ AttributeNode) DocXPathCtx() interface{} { return nil } @@ -114,9 +107,7 @@ func (_ AttributeNode) DuplicateTo(_ Document, _ int) Node { return nil } -func (_ AttributeNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string -}) { +func (_ AttributeNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, error) { return nil, nil } @@ -134,31 +125,23 @@ func (_ AttributeNode) InnerHtml() string { return "" } -func (_ AttributeNode) InputEncoding() []uint8 { +func (_ AttributeNode) InputEncoding() []byte { return nil } -func (_ AttributeNode) InsertAfter(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) InsertAfter(_ interface{}) error { return nil } -func (_ AttributeNode) InsertBefore(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) InsertBefore(_ interface{}) error { return nil } -func (_ AttributeNode) InsertBegin(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) InsertBegin(_ interface{}) error { return nil } -func (_ AttributeNode) InsertEnd(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) InsertEnd(_ interface{}) error { return nil } @@ -202,7 +185,7 @@ func (_ AttributeNode) NodeType() NodeType { return 0 } -func (_ AttributeNode) OutputEncoding() []uint8 { +func (_ AttributeNode) OutputEncoding() []byte { return nil } @@ -210,9 +193,7 @@ func (_ AttributeNode) Parent() Node { return nil } -func (_ AttributeNode) ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string -}) { +func (_ AttributeNode) ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) { return nil, nil } @@ -224,9 +205,7 @@ func (_ AttributeNode) PreviousSibling() Node { return nil } -func (_ AttributeNode) RecursivelyRemoveNamespaces() interface { - Error() string -} { +func (_ AttributeNode) RecursivelyRemoveNamespaces() error { return nil } @@ -238,9 +217,7 @@ func (_ AttributeNode) RemoveUnlinkedNode(_ interface{}) bool { return false } -func (_ AttributeNode) Replace(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) Replace(_ interface{}) error { return nil } @@ -252,19 +229,15 @@ func (_ AttributeNode) Root() *ElementNode { return nil } -func (_ AttributeNode) Search(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ AttributeNode) Search(_ interface{}) ([]Node, error) { return nil, nil } -func (_ AttributeNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string -}) { +func (_ AttributeNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) { return nil, nil } -func (_ AttributeNode) SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) { +func (_ AttributeNode) SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -272,21 +245,15 @@ func (_ AttributeNode) SetAttr(_ string, _ string) string { return "" } -func (_ AttributeNode) SetChildren(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) SetChildren(_ interface{}) error { return nil } -func (_ AttributeNode) SetContent(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) SetContent(_ interface{}) error { return nil } -func (_ AttributeNode) SetInnerHtml(_ interface{}) interface { - Error() string -} { +func (_ AttributeNode) SetInnerHtml(_ interface{}) error { return nil } @@ -298,11 +265,11 @@ func (_ AttributeNode) SetNsAttr(_ string, _ string, _ string) string { return "" } -func (_ AttributeNode) ToBuffer(_ []uint8) []uint8 { +func (_ AttributeNode) ToBuffer(_ []byte) []byte { return nil } -func (_ AttributeNode) ToHtml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ AttributeNode) ToHtml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -310,7 +277,7 @@ func (_ AttributeNode) ToUnformattedXml() string { return "" } -func (_ AttributeNode) ToXml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ AttributeNode) ToXml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -324,9 +291,7 @@ func (_ AttributeNode) Uri() string { return "" } -func (_ AttributeNode) Wrap(_ string) interface { - Error() string -} { +func (_ AttributeNode) Wrap(_ string) error { return nil } @@ -344,21 +309,15 @@ type CDataNode struct { XmlNode *XmlNode } -func (_ CDataNode) AddChild(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) AddChild(_ interface{}) error { return nil } -func (_ CDataNode) AddNextSibling(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) AddNextSibling(_ interface{}) error { return nil } -func (_ CDataNode) AddPreviousSibling(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) AddPreviousSibling(_ interface{}) error { return nil } @@ -382,9 +341,7 @@ func (_ CDataNode) Attributes() map[string]*AttributeNode { func (_ CDataNode) BookkeepFragment(_ *DocumentFragment) {} -func (_ CDataNode) Coerce(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ CDataNode) Coerce(_ interface{}) ([]Node, error) { return nil, nil } @@ -434,7 +391,7 @@ func (_ CDataNode) DocType() NodeType { return 0 } -func (_ CDataNode) DocXPathCtx() *interface{} { +func (_ CDataNode) DocXPathCtx() interface{} { return nil } @@ -446,9 +403,7 @@ func (_ CDataNode) DuplicateTo(_ Document, _ int) Node { return nil } -func (_ CDataNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string -}) { +func (_ CDataNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, error) { return nil, nil } @@ -466,31 +421,23 @@ func (_ CDataNode) InnerHtml() string { return "" } -func (_ CDataNode) InputEncoding() []uint8 { +func (_ CDataNode) InputEncoding() []byte { return nil } -func (_ CDataNode) InsertAfter(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) InsertAfter(_ interface{}) error { return nil } -func (_ CDataNode) InsertBefore(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) InsertBefore(_ interface{}) error { return nil } -func (_ CDataNode) InsertBegin(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) InsertBegin(_ interface{}) error { return nil } -func (_ CDataNode) InsertEnd(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) InsertEnd(_ interface{}) error { return nil } @@ -534,7 +481,7 @@ func (_ CDataNode) NodeType() NodeType { return 0 } -func (_ CDataNode) OutputEncoding() []uint8 { +func (_ CDataNode) OutputEncoding() []byte { return nil } @@ -542,9 +489,7 @@ func (_ CDataNode) Parent() Node { return nil } -func (_ CDataNode) ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string -}) { +func (_ CDataNode) ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) { return nil, nil } @@ -556,9 +501,7 @@ func (_ CDataNode) PreviousSibling() Node { return nil } -func (_ CDataNode) RecursivelyRemoveNamespaces() interface { - Error() string -} { +func (_ CDataNode) RecursivelyRemoveNamespaces() error { return nil } @@ -570,9 +513,7 @@ func (_ CDataNode) RemoveUnlinkedNode(_ interface{}) bool { return false } -func (_ CDataNode) Replace(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) Replace(_ interface{}) error { return nil } @@ -584,19 +525,15 @@ func (_ CDataNode) Root() *ElementNode { return nil } -func (_ CDataNode) Search(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ CDataNode) Search(_ interface{}) ([]Node, error) { return nil, nil } -func (_ CDataNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string -}) { +func (_ CDataNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) { return nil, nil } -func (_ CDataNode) SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) { +func (_ CDataNode) SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -604,21 +541,15 @@ func (_ CDataNode) SetAttr(_ string, _ string) string { return "" } -func (_ CDataNode) SetChildren(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) SetChildren(_ interface{}) error { return nil } -func (_ CDataNode) SetContent(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) SetContent(_ interface{}) error { return nil } -func (_ CDataNode) SetInnerHtml(_ interface{}) interface { - Error() string -} { +func (_ CDataNode) SetInnerHtml(_ interface{}) error { return nil } @@ -634,11 +565,11 @@ func (_ CDataNode) String() string { return "" } -func (_ CDataNode) ToBuffer(_ []uint8) []uint8 { +func (_ CDataNode) ToBuffer(_ []byte) []byte { return nil } -func (_ CDataNode) ToHtml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ CDataNode) ToHtml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -646,7 +577,7 @@ func (_ CDataNode) ToUnformattedXml() string { return "" } -func (_ CDataNode) ToXml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ CDataNode) ToXml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -660,9 +591,7 @@ func (_ CDataNode) Uri() string { return "" } -func (_ CDataNode) Wrap(_ string) interface { - Error() string -} { +func (_ CDataNode) Wrap(_ string) error { return nil } @@ -670,21 +599,15 @@ type CommentNode struct { XmlNode *XmlNode } -func (_ CommentNode) AddChild(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) AddChild(_ interface{}) error { return nil } -func (_ CommentNode) AddNextSibling(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) AddNextSibling(_ interface{}) error { return nil } -func (_ CommentNode) AddPreviousSibling(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) AddPreviousSibling(_ interface{}) error { return nil } @@ -708,9 +631,7 @@ func (_ CommentNode) Attributes() map[string]*AttributeNode { func (_ CommentNode) BookkeepFragment(_ *DocumentFragment) {} -func (_ CommentNode) Coerce(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ CommentNode) Coerce(_ interface{}) ([]Node, error) { return nil, nil } @@ -760,7 +681,7 @@ func (_ CommentNode) DocType() NodeType { return 0 } -func (_ CommentNode) DocXPathCtx() *interface{} { +func (_ CommentNode) DocXPathCtx() interface{} { return nil } @@ -772,9 +693,7 @@ func (_ CommentNode) DuplicateTo(_ Document, _ int) Node { return nil } -func (_ CommentNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string -}) { +func (_ CommentNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, error) { return nil, nil } @@ -792,31 +711,23 @@ func (_ CommentNode) InnerHtml() string { return "" } -func (_ CommentNode) InputEncoding() []uint8 { +func (_ CommentNode) InputEncoding() []byte { return nil } -func (_ CommentNode) InsertAfter(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) InsertAfter(_ interface{}) error { return nil } -func (_ CommentNode) InsertBefore(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) InsertBefore(_ interface{}) error { return nil } -func (_ CommentNode) InsertBegin(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) InsertBegin(_ interface{}) error { return nil } -func (_ CommentNode) InsertEnd(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) InsertEnd(_ interface{}) error { return nil } @@ -860,7 +771,7 @@ func (_ CommentNode) NodeType() NodeType { return 0 } -func (_ CommentNode) OutputEncoding() []uint8 { +func (_ CommentNode) OutputEncoding() []byte { return nil } @@ -868,9 +779,7 @@ func (_ CommentNode) Parent() Node { return nil } -func (_ CommentNode) ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string -}) { +func (_ CommentNode) ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) { return nil, nil } @@ -882,9 +791,7 @@ func (_ CommentNode) PreviousSibling() Node { return nil } -func (_ CommentNode) RecursivelyRemoveNamespaces() interface { - Error() string -} { +func (_ CommentNode) RecursivelyRemoveNamespaces() error { return nil } @@ -896,9 +803,7 @@ func (_ CommentNode) RemoveUnlinkedNode(_ interface{}) bool { return false } -func (_ CommentNode) Replace(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) Replace(_ interface{}) error { return nil } @@ -910,19 +815,15 @@ func (_ CommentNode) Root() *ElementNode { return nil } -func (_ CommentNode) Search(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ CommentNode) Search(_ interface{}) ([]Node, error) { return nil, nil } -func (_ CommentNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string -}) { +func (_ CommentNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) { return nil, nil } -func (_ CommentNode) SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) { +func (_ CommentNode) SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -930,21 +831,15 @@ func (_ CommentNode) SetAttr(_ string, _ string) string { return "" } -func (_ CommentNode) SetChildren(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) SetChildren(_ interface{}) error { return nil } -func (_ CommentNode) SetContent(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) SetContent(_ interface{}) error { return nil } -func (_ CommentNode) SetInnerHtml(_ interface{}) interface { - Error() string -} { +func (_ CommentNode) SetInnerHtml(_ interface{}) error { return nil } @@ -960,11 +855,11 @@ func (_ CommentNode) String() string { return "" } -func (_ CommentNode) ToBuffer(_ []uint8) []uint8 { +func (_ CommentNode) ToBuffer(_ []byte) []byte { return nil } -func (_ CommentNode) ToHtml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ CommentNode) ToHtml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -972,7 +867,7 @@ func (_ CommentNode) ToUnformattedXml() string { return "" } -func (_ CommentNode) ToXml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ CommentNode) ToXml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -986,9 +881,7 @@ func (_ CommentNode) Uri() string { return "" } -func (_ CommentNode) Wrap(_ string) interface { - Error() string -} { +func (_ CommentNode) Wrap(_ string) error { return nil } @@ -1003,17 +896,13 @@ type Document interface { DocPtr() interface{} DocRef() Document DocType() NodeType - DocXPathCtx() *interface{} + DocXPathCtx() interface{} Free() - InputEncoding() []uint8 + InputEncoding() []byte NodeById(_ string) *ElementNode - OutputEncoding() []uint8 - ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string - }) - RecursivelyRemoveNamespaces() interface { - Error() string - } + OutputEncoding() []byte + ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) + RecursivelyRemoveNamespaces() error RemoveUnlinkedNode(_ interface{}) bool Root() *ElementNode String() string @@ -1023,25 +912,19 @@ type Document interface { type DocumentFragment struct { Node Node - InEncoding []uint8 - OutEncoding []uint8 + InEncoding []byte + OutEncoding []byte } -func (_ DocumentFragment) AddChild(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) AddChild(_ interface{}) error { return nil } -func (_ DocumentFragment) AddNextSibling(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) AddNextSibling(_ interface{}) error { return nil } -func (_ DocumentFragment) AddPreviousSibling(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) AddPreviousSibling(_ interface{}) error { return nil } @@ -1061,9 +944,7 @@ func (_ DocumentFragment) Attributes() map[string]*AttributeNode { return nil } -func (_ DocumentFragment) Coerce(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ DocumentFragment) Coerce(_ interface{}) ([]Node, error) { return nil, nil } @@ -1089,9 +970,7 @@ func (_ DocumentFragment) DuplicateTo(_ Document, _ int) Node { return nil } -func (_ DocumentFragment) EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string -}) { +func (_ DocumentFragment) EvalXPath(_ interface{}, _ interface{}) (interface{}, error) { return nil, nil } @@ -1107,27 +986,19 @@ func (_ DocumentFragment) InnerHtml() string { return "" } -func (_ DocumentFragment) InsertAfter(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) InsertAfter(_ interface{}) error { return nil } -func (_ DocumentFragment) InsertBefore(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) InsertBefore(_ interface{}) error { return nil } -func (_ DocumentFragment) InsertBegin(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) InsertBegin(_ interface{}) error { return nil } -func (_ DocumentFragment) InsertEnd(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) InsertEnd(_ interface{}) error { return nil } @@ -1171,9 +1042,7 @@ func (_ DocumentFragment) Parent() Node { return nil } -func (_ DocumentFragment) ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string -}) { +func (_ DocumentFragment) ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) { return nil, nil } @@ -1185,17 +1054,13 @@ func (_ DocumentFragment) PreviousSibling() Node { return nil } -func (_ DocumentFragment) RecursivelyRemoveNamespaces() interface { - Error() string -} { +func (_ DocumentFragment) RecursivelyRemoveNamespaces() error { return nil } func (_ DocumentFragment) RemoveDefaultNamespace() {} -func (_ DocumentFragment) Replace(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) Replace(_ interface{}) error { return nil } @@ -1203,19 +1068,15 @@ func (_ DocumentFragment) ResetChildren() {} func (_ DocumentFragment) ResetNodePtr() {} -func (_ DocumentFragment) Search(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ DocumentFragment) Search(_ interface{}) ([]Node, error) { return nil, nil } -func (_ DocumentFragment) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string -}) { +func (_ DocumentFragment) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) { return nil, nil } -func (_ DocumentFragment) SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) { +func (_ DocumentFragment) SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -1223,21 +1084,15 @@ func (_ DocumentFragment) SetAttr(_ string, _ string) string { return "" } -func (_ DocumentFragment) SetChildren(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) SetChildren(_ interface{}) error { return nil } -func (_ DocumentFragment) SetContent(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) SetContent(_ interface{}) error { return nil } -func (_ DocumentFragment) SetInnerHtml(_ interface{}) interface { - Error() string -} { +func (_ DocumentFragment) SetInnerHtml(_ interface{}) error { return nil } @@ -1249,7 +1104,7 @@ func (_ DocumentFragment) SetNsAttr(_ string, _ string, _ string) string { return "" } -func (_ DocumentFragment) ToHtml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ DocumentFragment) ToHtml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -1257,15 +1112,13 @@ func (_ DocumentFragment) ToUnformattedXml() string { return "" } -func (_ DocumentFragment) ToXml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ DocumentFragment) ToXml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } func (_ DocumentFragment) Unlink() {} -func (_ DocumentFragment) Wrap(_ string) interface { - Error() string -} { +func (_ DocumentFragment) Wrap(_ string) error { return nil } @@ -1279,7 +1132,7 @@ func (_ *DocumentFragment) String() string { return "" } -func (_ *DocumentFragment) ToBuffer(_ []uint8) []uint8 { +func (_ *DocumentFragment) ToBuffer(_ []byte) []byte { return nil } @@ -1287,21 +1140,15 @@ type ElementNode struct { XmlNode *XmlNode } -func (_ ElementNode) AddChild(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) AddChild(_ interface{}) error { return nil } -func (_ ElementNode) AddNextSibling(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) AddNextSibling(_ interface{}) error { return nil } -func (_ ElementNode) AddPreviousSibling(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) AddPreviousSibling(_ interface{}) error { return nil } @@ -1325,9 +1172,7 @@ func (_ ElementNode) Attributes() map[string]*AttributeNode { func (_ ElementNode) BookkeepFragment(_ *DocumentFragment) {} -func (_ ElementNode) Coerce(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ ElementNode) Coerce(_ interface{}) ([]Node, error) { return nil, nil } @@ -1377,7 +1222,7 @@ func (_ ElementNode) DocType() NodeType { return 0 } -func (_ ElementNode) DocXPathCtx() *interface{} { +func (_ ElementNode) DocXPathCtx() interface{} { return nil } @@ -1389,9 +1234,7 @@ func (_ ElementNode) DuplicateTo(_ Document, _ int) Node { return nil } -func (_ ElementNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string -}) { +func (_ ElementNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, error) { return nil, nil } @@ -1409,31 +1252,23 @@ func (_ ElementNode) InnerHtml() string { return "" } -func (_ ElementNode) InputEncoding() []uint8 { +func (_ ElementNode) InputEncoding() []byte { return nil } -func (_ ElementNode) InsertAfter(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) InsertAfter(_ interface{}) error { return nil } -func (_ ElementNode) InsertBefore(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) InsertBefore(_ interface{}) error { return nil } -func (_ ElementNode) InsertBegin(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) InsertBegin(_ interface{}) error { return nil } -func (_ ElementNode) InsertEnd(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) InsertEnd(_ interface{}) error { return nil } @@ -1477,7 +1312,7 @@ func (_ ElementNode) NodeType() NodeType { return 0 } -func (_ ElementNode) OutputEncoding() []uint8 { +func (_ ElementNode) OutputEncoding() []byte { return nil } @@ -1485,9 +1320,7 @@ func (_ ElementNode) Parent() Node { return nil } -func (_ ElementNode) ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string -}) { +func (_ ElementNode) ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) { return nil, nil } @@ -1499,9 +1332,7 @@ func (_ ElementNode) PreviousSibling() Node { return nil } -func (_ ElementNode) RecursivelyRemoveNamespaces() interface { - Error() string -} { +func (_ ElementNode) RecursivelyRemoveNamespaces() error { return nil } @@ -1513,9 +1344,7 @@ func (_ ElementNode) RemoveUnlinkedNode(_ interface{}) bool { return false } -func (_ ElementNode) Replace(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) Replace(_ interface{}) error { return nil } @@ -1527,19 +1356,15 @@ func (_ ElementNode) Root() *ElementNode { return nil } -func (_ ElementNode) Search(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ ElementNode) Search(_ interface{}) ([]Node, error) { return nil, nil } -func (_ ElementNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string -}) { +func (_ ElementNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) { return nil, nil } -func (_ ElementNode) SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) { +func (_ ElementNode) SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -1547,21 +1372,15 @@ func (_ ElementNode) SetAttr(_ string, _ string) string { return "" } -func (_ ElementNode) SetChildren(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) SetChildren(_ interface{}) error { return nil } -func (_ ElementNode) SetContent(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) SetContent(_ interface{}) error { return nil } -func (_ ElementNode) SetInnerHtml(_ interface{}) interface { - Error() string -} { +func (_ ElementNode) SetInnerHtml(_ interface{}) error { return nil } @@ -1577,11 +1396,11 @@ func (_ ElementNode) String() string { return "" } -func (_ ElementNode) ToBuffer(_ []uint8) []uint8 { +func (_ ElementNode) ToBuffer(_ []byte) []byte { return nil } -func (_ ElementNode) ToHtml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ ElementNode) ToHtml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -1589,7 +1408,7 @@ func (_ ElementNode) ToUnformattedXml() string { return "" } -func (_ ElementNode) ToXml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ ElementNode) ToXml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -1603,9 +1422,7 @@ func (_ ElementNode) Uri() string { return "" } -func (_ ElementNode) Wrap(_ string) interface { - Error() string -} { +func (_ ElementNode) Wrap(_ string) error { return nil } @@ -1615,46 +1432,28 @@ type NamespaceDeclaration struct { } type Node interface { - AddChild(_ interface{}) interface { - Error() string - } - AddNextSibling(_ interface{}) interface { - Error() string - } - AddPreviousSibling(_ interface{}) interface { - Error() string - } + AddChild(_ interface{}) error + AddNextSibling(_ interface{}) error + AddPreviousSibling(_ interface{}) error Attr(_ string) string Attribute(_ string) *AttributeNode AttributeList() []*AttributeNode Attributes() map[string]*AttributeNode - Coerce(_ interface{}) ([]Node, interface { - Error() string - }) + Coerce(_ interface{}) ([]Node, error) Content() string CountChildren() int DeclareNamespace(_ string, _ string) DeclaredNamespaces() []NamespaceDeclaration Duplicate(_ int) Node DuplicateTo(_ Document, _ int) Node - EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string - }) + EvalXPath(_ interface{}, _ interface{}) (interface{}, error) EvalXPathAsBoolean(_ interface{}, _ interface{}) bool FirstChild() Node InnerHtml() string - InsertAfter(_ interface{}) interface { - Error() string - } - InsertBefore(_ interface{}) interface { - Error() string - } - InsertBegin(_ interface{}) interface { - Error() string - } - InsertEnd(_ interface{}) interface { - Error() string - } + InsertAfter(_ interface{}) error + InsertBefore(_ interface{}) error + InsertBegin(_ interface{}) error + InsertEnd(_ interface{}) error IsValid() bool LastChild() Node LineNumber() int @@ -1665,50 +1464,32 @@ type Node interface { NodePtr() interface{} NodeType() NodeType Parent() Node - ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string - }) + ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) Path() string PreviousSibling() Node - RecursivelyRemoveNamespaces() interface { - Error() string - } + RecursivelyRemoveNamespaces() error Remove() RemoveDefaultNamespace() - Replace(_ interface{}) interface { - Error() string - } + Replace(_ interface{}) error ResetChildren() ResetNodePtr() - Search(_ interface{}) ([]Node, interface { - Error() string - }) - SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string - }) - SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) + Search(_ interface{}) ([]Node, error) + SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) + SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) SetAttr(_ string, _ string) string - SetChildren(_ interface{}) interface { - Error() string - } - SetContent(_ interface{}) interface { - Error() string - } - SetInnerHtml(_ interface{}) interface { - Error() string - } + SetChildren(_ interface{}) error + SetContent(_ interface{}) error + SetInnerHtml(_ interface{}) error SetName(_ string) SetNamespace(_ string, _ string) SetNsAttr(_ string, _ string, _ string) string String() string - ToBuffer(_ []uint8) []uint8 - ToHtml(_ []uint8, _ []uint8) ([]uint8, int) + ToBuffer(_ []byte) []byte + ToHtml(_ []byte, _ []byte) ([]byte, int) ToUnformattedXml() string - ToXml(_ []uint8, _ []uint8) ([]uint8, int) + ToXml(_ []byte, _ []byte) ([]byte, int) Unlink() - Wrap(_ string) interface { - Error() string - } + Wrap(_ string) error } type NodeType int @@ -1719,21 +1500,15 @@ type ProcessingInstructionNode struct { XmlNode *XmlNode } -func (_ ProcessingInstructionNode) AddChild(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) AddChild(_ interface{}) error { return nil } -func (_ ProcessingInstructionNode) AddNextSibling(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) AddNextSibling(_ interface{}) error { return nil } -func (_ ProcessingInstructionNode) AddPreviousSibling(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) AddPreviousSibling(_ interface{}) error { return nil } @@ -1757,9 +1532,7 @@ func (_ ProcessingInstructionNode) Attributes() map[string]*AttributeNode { func (_ ProcessingInstructionNode) BookkeepFragment(_ *DocumentFragment) {} -func (_ ProcessingInstructionNode) Coerce(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ ProcessingInstructionNode) Coerce(_ interface{}) ([]Node, error) { return nil, nil } @@ -1809,7 +1582,7 @@ func (_ ProcessingInstructionNode) DocType() NodeType { return 0 } -func (_ ProcessingInstructionNode) DocXPathCtx() *interface{} { +func (_ ProcessingInstructionNode) DocXPathCtx() interface{} { return nil } @@ -1821,9 +1594,7 @@ func (_ ProcessingInstructionNode) DuplicateTo(_ Document, _ int) Node { return nil } -func (_ ProcessingInstructionNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string -}) { +func (_ ProcessingInstructionNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, error) { return nil, nil } @@ -1841,31 +1612,23 @@ func (_ ProcessingInstructionNode) InnerHtml() string { return "" } -func (_ ProcessingInstructionNode) InputEncoding() []uint8 { +func (_ ProcessingInstructionNode) InputEncoding() []byte { return nil } -func (_ ProcessingInstructionNode) InsertAfter(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) InsertAfter(_ interface{}) error { return nil } -func (_ ProcessingInstructionNode) InsertBefore(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) InsertBefore(_ interface{}) error { return nil } -func (_ ProcessingInstructionNode) InsertBegin(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) InsertBegin(_ interface{}) error { return nil } -func (_ ProcessingInstructionNode) InsertEnd(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) InsertEnd(_ interface{}) error { return nil } @@ -1909,7 +1672,7 @@ func (_ ProcessingInstructionNode) NodeType() NodeType { return 0 } -func (_ ProcessingInstructionNode) OutputEncoding() []uint8 { +func (_ ProcessingInstructionNode) OutputEncoding() []byte { return nil } @@ -1917,9 +1680,7 @@ func (_ ProcessingInstructionNode) Parent() Node { return nil } -func (_ ProcessingInstructionNode) ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string -}) { +func (_ ProcessingInstructionNode) ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) { return nil, nil } @@ -1931,9 +1692,7 @@ func (_ ProcessingInstructionNode) PreviousSibling() Node { return nil } -func (_ ProcessingInstructionNode) RecursivelyRemoveNamespaces() interface { - Error() string -} { +func (_ ProcessingInstructionNode) RecursivelyRemoveNamespaces() error { return nil } @@ -1945,9 +1704,7 @@ func (_ ProcessingInstructionNode) RemoveUnlinkedNode(_ interface{}) bool { return false } -func (_ ProcessingInstructionNode) Replace(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) Replace(_ interface{}) error { return nil } @@ -1959,19 +1716,15 @@ func (_ ProcessingInstructionNode) Root() *ElementNode { return nil } -func (_ ProcessingInstructionNode) Search(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ ProcessingInstructionNode) Search(_ interface{}) ([]Node, error) { return nil, nil } -func (_ ProcessingInstructionNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string -}) { +func (_ ProcessingInstructionNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) { return nil, nil } -func (_ ProcessingInstructionNode) SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) { +func (_ ProcessingInstructionNode) SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -1979,21 +1732,15 @@ func (_ ProcessingInstructionNode) SetAttr(_ string, _ string) string { return "" } -func (_ ProcessingInstructionNode) SetChildren(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) SetChildren(_ interface{}) error { return nil } -func (_ ProcessingInstructionNode) SetContent(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) SetContent(_ interface{}) error { return nil } -func (_ ProcessingInstructionNode) SetInnerHtml(_ interface{}) interface { - Error() string -} { +func (_ ProcessingInstructionNode) SetInnerHtml(_ interface{}) error { return nil } @@ -2009,11 +1756,11 @@ func (_ ProcessingInstructionNode) String() string { return "" } -func (_ ProcessingInstructionNode) ToBuffer(_ []uint8) []uint8 { +func (_ ProcessingInstructionNode) ToBuffer(_ []byte) []byte { return nil } -func (_ ProcessingInstructionNode) ToHtml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ ProcessingInstructionNode) ToHtml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -2021,7 +1768,7 @@ func (_ ProcessingInstructionNode) ToUnformattedXml() string { return "" } -func (_ ProcessingInstructionNode) ToXml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ ProcessingInstructionNode) ToXml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -2035,9 +1782,7 @@ func (_ ProcessingInstructionNode) Uri() string { return "" } -func (_ ProcessingInstructionNode) Wrap(_ string) interface { - Error() string -} { +func (_ ProcessingInstructionNode) Wrap(_ string) error { return nil } @@ -2047,21 +1792,15 @@ type TextNode struct { XmlNode *XmlNode } -func (_ TextNode) AddChild(_ interface{}) interface { - Error() string -} { +func (_ TextNode) AddChild(_ interface{}) error { return nil } -func (_ TextNode) AddNextSibling(_ interface{}) interface { - Error() string -} { +func (_ TextNode) AddNextSibling(_ interface{}) error { return nil } -func (_ TextNode) AddPreviousSibling(_ interface{}) interface { - Error() string -} { +func (_ TextNode) AddPreviousSibling(_ interface{}) error { return nil } @@ -2085,9 +1824,7 @@ func (_ TextNode) Attributes() map[string]*AttributeNode { func (_ TextNode) BookkeepFragment(_ *DocumentFragment) {} -func (_ TextNode) Coerce(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ TextNode) Coerce(_ interface{}) ([]Node, error) { return nil, nil } @@ -2137,7 +1874,7 @@ func (_ TextNode) DocType() NodeType { return 0 } -func (_ TextNode) DocXPathCtx() *interface{} { +func (_ TextNode) DocXPathCtx() interface{} { return nil } @@ -2149,9 +1886,7 @@ func (_ TextNode) DuplicateTo(_ Document, _ int) Node { return nil } -func (_ TextNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string -}) { +func (_ TextNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, error) { return nil, nil } @@ -2169,31 +1904,23 @@ func (_ TextNode) InnerHtml() string { return "" } -func (_ TextNode) InputEncoding() []uint8 { +func (_ TextNode) InputEncoding() []byte { return nil } -func (_ TextNode) InsertAfter(_ interface{}) interface { - Error() string -} { +func (_ TextNode) InsertAfter(_ interface{}) error { return nil } -func (_ TextNode) InsertBefore(_ interface{}) interface { - Error() string -} { +func (_ TextNode) InsertBefore(_ interface{}) error { return nil } -func (_ TextNode) InsertBegin(_ interface{}) interface { - Error() string -} { +func (_ TextNode) InsertBegin(_ interface{}) error { return nil } -func (_ TextNode) InsertEnd(_ interface{}) interface { - Error() string -} { +func (_ TextNode) InsertEnd(_ interface{}) error { return nil } @@ -2237,7 +1964,7 @@ func (_ TextNode) NodeType() NodeType { return 0 } -func (_ TextNode) OutputEncoding() []uint8 { +func (_ TextNode) OutputEncoding() []byte { return nil } @@ -2245,9 +1972,7 @@ func (_ TextNode) Parent() Node { return nil } -func (_ TextNode) ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string -}) { +func (_ TextNode) ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) { return nil, nil } @@ -2259,9 +1984,7 @@ func (_ TextNode) PreviousSibling() Node { return nil } -func (_ TextNode) RecursivelyRemoveNamespaces() interface { - Error() string -} { +func (_ TextNode) RecursivelyRemoveNamespaces() error { return nil } @@ -2273,9 +1996,7 @@ func (_ TextNode) RemoveUnlinkedNode(_ interface{}) bool { return false } -func (_ TextNode) Replace(_ interface{}) interface { - Error() string -} { +func (_ TextNode) Replace(_ interface{}) error { return nil } @@ -2287,19 +2008,15 @@ func (_ TextNode) Root() *ElementNode { return nil } -func (_ TextNode) Search(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ TextNode) Search(_ interface{}) ([]Node, error) { return nil, nil } -func (_ TextNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string -}) { +func (_ TextNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) { return nil, nil } -func (_ TextNode) SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) { +func (_ TextNode) SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -2307,21 +2024,15 @@ func (_ TextNode) SetAttr(_ string, _ string) string { return "" } -func (_ TextNode) SetChildren(_ interface{}) interface { - Error() string -} { +func (_ TextNode) SetChildren(_ interface{}) error { return nil } -func (_ TextNode) SetContent(_ interface{}) interface { - Error() string -} { +func (_ TextNode) SetContent(_ interface{}) error { return nil } -func (_ TextNode) SetInnerHtml(_ interface{}) interface { - Error() string -} { +func (_ TextNode) SetInnerHtml(_ interface{}) error { return nil } @@ -2337,11 +2048,11 @@ func (_ TextNode) String() string { return "" } -func (_ TextNode) ToBuffer(_ []uint8) []uint8 { +func (_ TextNode) ToBuffer(_ []byte) []byte { return nil } -func (_ TextNode) ToHtml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ TextNode) ToHtml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -2349,7 +2060,7 @@ func (_ TextNode) ToUnformattedXml() string { return "" } -func (_ TextNode) ToXml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ TextNode) ToXml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -2363,16 +2074,14 @@ func (_ TextNode) Uri() string { return "" } -func (_ TextNode) Wrap(_ string) interface { - Error() string -} { +func (_ TextNode) Wrap(_ string) error { return nil } func (_ *TextNode) DisableOutputEscaping() {} type XmlNode struct { - Ptr *interface{} + Ptr interface{} Document Document } @@ -2412,13 +2121,13 @@ func (_ XmlNode) DocType() NodeType { return 0 } -func (_ XmlNode) DocXPathCtx() *interface{} { +func (_ XmlNode) DocXPathCtx() interface{} { return nil } func (_ XmlNode) Free() {} -func (_ XmlNode) InputEncoding() []uint8 { +func (_ XmlNode) InputEncoding() []byte { return nil } @@ -2426,7 +2135,7 @@ func (_ XmlNode) NodeById(_ string) *ElementNode { return nil } -func (_ XmlNode) OutputEncoding() []uint8 { +func (_ XmlNode) OutputEncoding() []byte { return nil } @@ -2446,21 +2155,15 @@ func (_ XmlNode) Uri() string { return "" } -func (_ *XmlNode) AddChild(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) AddChild(_ interface{}) error { return nil } -func (_ *XmlNode) AddNextSibling(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) AddNextSibling(_ interface{}) error { return nil } -func (_ *XmlNode) AddPreviousSibling(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) AddPreviousSibling(_ interface{}) error { return nil } @@ -2480,9 +2183,7 @@ func (_ *XmlNode) Attributes() map[string]*AttributeNode { return nil } -func (_ *XmlNode) Coerce(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ *XmlNode) Coerce(_ interface{}) ([]Node, error) { return nil, nil } @@ -2508,9 +2209,7 @@ func (_ *XmlNode) DuplicateTo(_ Document, _ int) Node { return nil } -func (_ *XmlNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, interface { - Error() string -}) { +func (_ *XmlNode) EvalXPath(_ interface{}, _ interface{}) (interface{}, error) { return nil, nil } @@ -2526,27 +2225,19 @@ func (_ *XmlNode) InnerHtml() string { return "" } -func (_ *XmlNode) InsertAfter(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) InsertAfter(_ interface{}) error { return nil } -func (_ *XmlNode) InsertBefore(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) InsertBefore(_ interface{}) error { return nil } -func (_ *XmlNode) InsertBegin(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) InsertBegin(_ interface{}) error { return nil } -func (_ *XmlNode) InsertEnd(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) InsertEnd(_ interface{}) error { return nil } @@ -2590,9 +2281,7 @@ func (_ *XmlNode) Parent() Node { return nil } -func (_ *XmlNode) ParseFragment(_ []uint8, _ []uint8, _ ParseOption) (*DocumentFragment, interface { - Error() string -}) { +func (_ *XmlNode) ParseFragment(_ []byte, _ []byte, _ ParseOption) (*DocumentFragment, error) { return nil, nil } @@ -2604,9 +2293,7 @@ func (_ *XmlNode) PreviousSibling() Node { return nil } -func (_ *XmlNode) RecursivelyRemoveNamespaces() interface { - Error() string -} { +func (_ *XmlNode) RecursivelyRemoveNamespaces() error { return nil } @@ -2614,9 +2301,7 @@ func (_ *XmlNode) Remove() {} func (_ *XmlNode) RemoveDefaultNamespace() {} -func (_ *XmlNode) Replace(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) Replace(_ interface{}) error { return nil } @@ -2624,19 +2309,15 @@ func (_ *XmlNode) ResetChildren() {} func (_ *XmlNode) ResetNodePtr() {} -func (_ *XmlNode) Search(_ interface{}) ([]Node, interface { - Error() string -}) { +func (_ *XmlNode) Search(_ interface{}) ([]Node, error) { return nil, nil } -func (_ *XmlNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, interface { - Error() string -}) { +func (_ *XmlNode) SearchWithVariables(_ interface{}, _ interface{}) ([]Node, error) { return nil, nil } -func (_ *XmlNode) SerializeWithFormat(_ SerializationOption, _ []uint8, _ []uint8) ([]uint8, int) { +func (_ *XmlNode) SerializeWithFormat(_ SerializationOption, _ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -2644,21 +2325,15 @@ func (_ *XmlNode) SetAttr(_ string, _ string) string { return "" } -func (_ *XmlNode) SetChildren(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) SetChildren(_ interface{}) error { return nil } -func (_ *XmlNode) SetContent(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) SetContent(_ interface{}) error { return nil } -func (_ *XmlNode) SetInnerHtml(_ interface{}) interface { - Error() string -} { +func (_ *XmlNode) SetInnerHtml(_ interface{}) error { return nil } @@ -2674,11 +2349,11 @@ func (_ *XmlNode) String() string { return "" } -func (_ *XmlNode) ToBuffer(_ []uint8) []uint8 { +func (_ *XmlNode) ToBuffer(_ []byte) []byte { return nil } -func (_ *XmlNode) ToHtml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ *XmlNode) ToHtml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } @@ -2686,14 +2361,12 @@ func (_ *XmlNode) ToUnformattedXml() string { return "" } -func (_ *XmlNode) ToXml(_ []uint8, _ []uint8) ([]uint8, int) { +func (_ *XmlNode) ToXml(_ []byte, _ []byte) ([]byte, int) { return nil, 0 } func (_ *XmlNode) Unlink() {} -func (_ *XmlNode) Wrap(_ string) interface { - Error() string -} { +func (_ *XmlNode) Wrap(_ string) error { return nil } diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/jbowtie/gokogiri/xpath/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/jbowtie/gokogiri/xpath/stub.go index 54f664cf2a1..c1e32cba5f2 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/jbowtie/gokogiri/xpath/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/jbowtie/gokogiri/xpath/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/jbowtie/gokogiri/xpath, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/jbowtie/gokogiri/xpath (exports: ; functions: Compile) // Package xpath is a stub of github.com/jbowtie/gokogiri/xpath, generated by depstubber. @@ -13,7 +14,7 @@ func Compile(_ string) *Expression { } type Expression struct { - Ptr *interface{} + Ptr interface{} } func (_ *Expression) Free() {} diff --git a/ql/test/query-tests/Security/CWE-643/vendor/github.com/santhosh-tekuri/xpathparser/stub.go b/ql/test/query-tests/Security/CWE-643/vendor/github.com/santhosh-tekuri/xpathparser/stub.go index 8f49df46c65..8bb9a6ced33 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/github.com/santhosh-tekuri/xpathparser/stub.go +++ b/ql/test/query-tests/Security/CWE-643/vendor/github.com/santhosh-tekuri/xpathparser/stub.go @@ -1,6 +1,7 @@ // Code generated by depstubber. DO NOT EDIT. // This is a simple stub for github.com/santhosh-tekuri/xpathparser, strictly for use in testing. +// See the LICENSE file for information about the licensing of the original library. // Source: github.com/santhosh-tekuri/xpathparser (exports: ; functions: Parse,MustParse) // Package xpathparser is a stub of github.com/santhosh-tekuri/xpathparser, generated by depstubber. @@ -14,8 +15,6 @@ func MustParse(_ string) Expr { return nil } -func Parse(_ string) (Expr, interface { - Error() string -}) { +func Parse(_ string) (Expr, error) { return nil, nil } diff --git a/ql/test/query-tests/Security/CWE-643/vendor/modules.txt b/ql/test/query-tests/Security/CWE-643/vendor/modules.txt index c51bc8971fe..ce1e1188bf6 100644 --- a/ql/test/query-tests/Security/CWE-643/vendor/modules.txt +++ b/ql/test/query-tests/Security/CWE-643/vendor/modules.txt @@ -13,30 +13,12 @@ github.com/antchfx/xmlquery # github.com/antchfx/xpath v1.1.5 ## explicit github.com/antchfx/xpath -# github.com/github/depstubber v0.0.0-20200413231600-392d5a70208e -## explicit -github.com/github/depstubber # github.com/go-xmlpath/xmlpath v0.0.0-20150820204837-860cbeca3ebc ## explicit github.com/go-xmlpath/xmlpath -# github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e -## explicit -github.com/golang/groupcache # github.com/jbowtie/gokogiri v0.0.0-20190301021639-37f655d3078f ## explicit github.com/jbowtie/gokogiri -# github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e -## explicit -github.com/niemeyer/pretty # github.com/santhosh-tekuri/xpathparser v1.0.0 ## explicit github.com/santhosh-tekuri/xpathparser -# golang.org/x/net v0.0.0-20200226121028-0de0cce0169b -## explicit -golang.org/x/net -# gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f -## explicit -gopkg.in/check.v1 -# gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc -## explicit -gopkg.in/xmlpath.v2 diff --git a/ql/test/query-tests/Security/CWE-918/RequestForgery.expected b/ql/test/query-tests/Security/CWE-918/RequestForgery.expected index 5b8fb08dce1..5709a91dcaf 100644 --- a/ql/test/query-tests/Security/CWE-918/RequestForgery.expected +++ b/ql/test/query-tests/Security/CWE-918/RequestForgery.expected @@ -13,6 +13,15 @@ edges | tst.go:36:2:36:2 | implicit dereference : URL | tst.go:36:2:36:2 | implicit dereference : URL | | tst.go:36:2:36:2 | implicit dereference : URL | tst.go:37:11:37:20 | call to String | | tst.go:36:2:36:2 | u [pointer] : URL | tst.go:36:2:36:2 | implicit dereference : URL | +| websocket.go:60:21:60:31 | call to Referer : string | websocket.go:65:27:65:40 | untrustedInput | +| websocket.go:74:21:74:31 | call to Referer : string | websocket.go:78:36:78:49 | untrustedInput | +| websocket.go:88:21:88:31 | call to Referer : string | websocket.go:91:31:91:44 | untrustedInput | +| websocket.go:107:21:107:31 | call to Referer : string | websocket.go:110:15:110:28 | untrustedInput | +| websocket.go:126:21:126:31 | call to Referer : string | websocket.go:129:38:129:51 | untrustedInput | +| websocket.go:154:21:154:31 | call to Referer : string | websocket.go:155:31:155:44 | untrustedInput | +| websocket.go:160:21:160:31 | call to Referer : string | websocket.go:162:31:162:44 | untrustedInput | +| websocket.go:195:21:195:31 | call to Referer : string | websocket.go:197:18:197:31 | untrustedInput | +| websocket.go:202:21:202:31 | call to Referer : string | websocket.go:204:11:204:24 | untrustedInput | nodes | RequestForgery.go:8:12:8:34 | call to FormValue : string | semmle.label | call to FormValue : string | | RequestForgery.go:11:24:11:65 | ...+... | semmle.label | ...+... | @@ -27,6 +36,24 @@ nodes | tst.go:36:2:36:2 | implicit dereference : URL | semmle.label | implicit dereference : URL | | tst.go:36:2:36:2 | u [pointer] : URL | semmle.label | u [pointer] : URL | | tst.go:37:11:37:20 | call to String | semmle.label | call to String | +| websocket.go:60:21:60:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:65:27:65:40 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:74:21:74:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:78:36:78:49 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:88:21:88:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:91:31:91:44 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:107:21:107:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:110:15:110:28 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:126:21:126:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:129:38:129:51 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:154:21:154:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:155:31:155:44 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:160:21:160:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:162:31:162:44 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:195:21:195:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:197:18:197:31 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:202:21:202:31 | call to Referer : string | semmle.label | call to Referer : string | +| websocket.go:204:11:204:24 | untrustedInput | semmle.label | untrustedInput | #select | RequestForgery.go:11:15:11:66 | call to Get | RequestForgery.go:8:12:8:34 | call to FormValue : string | RequestForgery.go:11:24:11:65 | ...+... | The $@ of this request depends on $@. | RequestForgery.go:11:24:11:65 | ...+... | URL | RequestForgery.go:8:12:8:34 | call to FormValue : string | a user-provided value | | tst.go:14:2:14:18 | call to Get | tst.go:10:13:10:35 | call to FormValue : string | tst.go:14:11:14:17 | tainted | The $@ of this request depends on $@. | tst.go:14:11:14:17 | tainted | URL | tst.go:10:13:10:35 | call to FormValue : string | a user-provided value | @@ -36,3 +63,12 @@ nodes | tst.go:27:2:27:30 | call to Get | tst.go:10:13:10:35 | call to FormValue : string | tst.go:27:11:27:29 | ...+... | The $@ of this request depends on $@. | tst.go:27:11:27:29 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue : string | a user-provided value | | tst.go:29:2:29:41 | call to Get | tst.go:10:13:10:35 | call to FormValue : string | tst.go:29:11:29:40 | ...+... | The $@ of this request depends on $@. | tst.go:29:11:29:40 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue : string | a user-provided value | | tst.go:37:2:37:21 | call to Get | tst.go:10:13:10:35 | call to FormValue : string | tst.go:37:11:37:20 | call to String | The $@ of this request depends on $@. | tst.go:37:11:37:20 | call to String | URL | tst.go:10:13:10:35 | call to FormValue : string | a user-provided value | +| websocket.go:65:12:65:53 | call to Dial | websocket.go:60:21:60:31 | call to Referer : string | websocket.go:65:27:65:40 | untrustedInput | The $@ of this request depends on $@. | websocket.go:65:27:65:40 | untrustedInput | WebSocket URL | websocket.go:60:21:60:31 | call to Referer : string | a user-provided value | +| websocket.go:79:13:79:40 | call to DialConfig | websocket.go:74:21:74:31 | call to Referer : string | websocket.go:78:36:78:49 | untrustedInput | The $@ of this request depends on $@. | websocket.go:78:36:78:49 | untrustedInput | WebSocket URL | websocket.go:74:21:74:31 | call to Referer : string | a user-provided value | +| websocket.go:91:3:91:50 | call to Dial | websocket.go:88:21:88:31 | call to Referer : string | websocket.go:91:31:91:44 | untrustedInput | The $@ of this request depends on $@. | websocket.go:91:31:91:44 | untrustedInput | WebSocket URL | websocket.go:88:21:88:31 | call to Referer : string | a user-provided value | +| websocket.go:110:3:110:39 | call to Dial | websocket.go:107:21:107:31 | call to Referer : string | websocket.go:110:15:110:28 | untrustedInput | The $@ of this request depends on $@. | websocket.go:110:15:110:28 | untrustedInput | WebSocket URL | websocket.go:107:21:107:31 | call to Referer : string | a user-provided value | +| websocket.go:129:3:129:62 | call to DialContext | websocket.go:126:21:126:31 | call to Referer : string | websocket.go:129:38:129:51 | untrustedInput | The $@ of this request depends on $@. | websocket.go:129:38:129:51 | untrustedInput | WebSocket URL | websocket.go:126:21:126:31 | call to Referer : string | a user-provided value | +| websocket.go:155:3:155:45 | call to Dial | websocket.go:154:21:154:31 | call to Referer : string | websocket.go:155:31:155:44 | untrustedInput | The $@ of this request depends on $@. | websocket.go:155:31:155:44 | untrustedInput | WebSocket URL | websocket.go:154:21:154:31 | call to Referer : string | a user-provided value | +| websocket.go:162:3:162:45 | call to Dial | websocket.go:160:21:160:31 | call to Referer : string | websocket.go:162:31:162:44 | untrustedInput | The $@ of this request depends on $@. | websocket.go:162:31:162:44 | untrustedInput | WebSocket URL | websocket.go:160:21:160:31 | call to Referer : string | a user-provided value | +| websocket.go:197:3:197:32 | call to BuildProxy | websocket.go:195:21:195:31 | call to Referer : string | websocket.go:197:18:197:31 | untrustedInput | The $@ of this request depends on $@. | websocket.go:197:18:197:31 | untrustedInput | WebSocket URL | websocket.go:195:21:195:31 | call to Referer : string | a user-provided value | +| websocket.go:204:3:204:25 | call to New | websocket.go:202:21:202:31 | call to Referer : string | websocket.go:204:11:204:24 | untrustedInput | The $@ of this request depends on $@. | websocket.go:204:11:204:24 | untrustedInput | WebSocket URL | websocket.go:202:21:202:31 | call to Referer : string | a user-provided value | diff --git a/ql/test/query-tests/Security/CWE-918/go.mod b/ql/test/query-tests/Security/CWE-918/go.mod new file mode 100644 index 00000000000..ce6c493a190 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/go.mod @@ -0,0 +1,11 @@ +module main + +go 1.14 + +require ( + github.com/gobwas/ws v1.0.3 + github.com/gorilla/websocket v1.4.2 + github.com/sacOO7/gowebsocket v0.0.0-20180719182212-1436bb906a4e + golang.org/x/net v0.0.0-20200421231249-e086a090c8fd + nhooyr.io/websocket v1.8.5 +) diff --git a/ql/test/query-tests/Security/CWE-918/vendor/github.com/gobwas/ws/LICENSE b/ql/test/query-tests/Security/CWE-918/vendor/github.com/gobwas/ws/LICENSE new file mode 100644 index 00000000000..d2611fddf55 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/github.com/gobwas/ws/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2018 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/query-tests/Security/CWE-918/vendor/github.com/gobwas/ws/stub.go b/ql/test/query-tests/Security/CWE-918/vendor/github.com/gobwas/ws/stub.go new file mode 100644 index 00000000000..0d00bc949fb --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/github.com/gobwas/ws/stub.go @@ -0,0 +1,54 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gobwas/ws, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gobwas/ws (exports: Dialer; functions: Dial) + +// Package ws is a stub of github.com/gobwas/ws, generated by depstubber. +package ws + +import ( + bufio "bufio" + context "context" + tls "crypto/tls" + io "io" + net "net" + url "net/url" + time "time" +) + +func Dial(_ context.Context, _ string) (net.Conn, *bufio.Reader, Handshake, error) { + return nil, nil, Handshake{}, nil +} + +type Dialer struct { + ReadBufferSize int + WriteBufferSize int + Timeout time.Duration + Protocols []string + Extensions []interface{} + Header HandshakeHeader + OnStatusError func(int, []byte, io.Reader) + OnHeader func([]byte, []byte) error + NetDial func(context.Context, string, string) (net.Conn, error) + TLSClient func(net.Conn, string) net.Conn + TLSConfig *tls.Config + WrapConn func(net.Conn) net.Conn +} + +func (_ Dialer) Dial(_ context.Context, _ string) (net.Conn, *bufio.Reader, Handshake, error) { + return nil, nil, Handshake{}, nil +} + +func (_ Dialer) Upgrade(_ io.ReadWriter, _ *url.URL) (*bufio.Reader, Handshake, error) { + return nil, Handshake{}, nil +} + +type Handshake struct { + Protocol string + Extensions []interface{} +} + +type HandshakeHeader interface { + WriteTo(_ io.Writer) (int64, error) +} diff --git a/ql/test/query-tests/Security/CWE-918/vendor/github.com/gorilla/websocket/LICENSE b/ql/test/query-tests/Security/CWE-918/vendor/github.com/gorilla/websocket/LICENSE new file mode 100644 index 00000000000..9171c972252 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/github.com/gorilla/websocket/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ql/test/query-tests/Security/CWE-918/vendor/github.com/gorilla/websocket/stub.go b/ql/test/query-tests/Security/CWE-918/vendor/github.com/gorilla/websocket/stub.go new file mode 100644 index 00000000000..0be1589cca9 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/github.com/gorilla/websocket/stub.go @@ -0,0 +1,135 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gorilla/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gorilla/websocket (exports: Dialer; functions: ) + +// Package websocket is a stub of github.com/gorilla/websocket, generated by depstubber. +package websocket + +import ( + context "context" + tls "crypto/tls" + io "io" + net "net" + http "net/http" + url "net/url" + time "time" +) + +type BufferPool interface { + Get() interface{} + Put(_ interface{}) +} + +type Conn struct{} + +func (_ *Conn) Close() error { + return nil +} + +func (_ *Conn) CloseHandler() func(int, string) error { + return nil +} + +func (_ *Conn) EnableWriteCompression(_ bool) {} + +func (_ *Conn) LocalAddr() net.Addr { + return nil +} + +func (_ *Conn) NextReader() (int, io.Reader, error) { + return 0, nil, nil +} + +func (_ *Conn) NextWriter(_ int) (io.WriteCloser, error) { + return nil, nil +} + +func (_ *Conn) PingHandler() func(string) error { + return nil +} + +func (_ *Conn) PongHandler() func(string) error { + return nil +} + +func (_ *Conn) ReadJSON(_ interface{}) error { + return nil +} + +func (_ *Conn) ReadMessage() (int, []byte, error) { + return 0, nil, nil +} + +func (_ *Conn) RemoteAddr() net.Addr { + return nil +} + +func (_ *Conn) SetCloseHandler(_ func(int, string) error) {} + +func (_ *Conn) SetCompressionLevel(_ int) error { + return nil +} + +func (_ *Conn) SetPingHandler(_ func(string) error) {} + +func (_ *Conn) SetPongHandler(_ func(string) error) {} + +func (_ *Conn) SetReadDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetReadLimit(_ int64) {} + +func (_ *Conn) SetWriteDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) Subprotocol() string { + return "" +} + +func (_ *Conn) UnderlyingConn() net.Conn { + return nil +} + +func (_ *Conn) WriteControl(_ int, _ []byte, _ time.Time) error { + return nil +} + +func (_ *Conn) WriteJSON(_ interface{}) error { + return nil +} + +func (_ *Conn) WriteMessage(_ int, _ []byte) error { + return nil +} + +func (_ *Conn) WritePreparedMessage(_ *PreparedMessage) error { + return nil +} + +type Dialer struct { + NetDial func(string, string) (net.Conn, error) + NetDialContext func(context.Context, string, string) (net.Conn, error) + Proxy func(*http.Request) (*url.URL, error) + TLSClientConfig *tls.Config + HandshakeTimeout time.Duration + ReadBufferSize int + WriteBufferSize int + WriteBufferPool BufferPool + Subprotocols []string + EnableCompression bool + Jar http.CookieJar +} + +func (_ *Dialer) Dial(_ string, _ http.Header) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +func (_ *Dialer) DialContext(_ context.Context, _ string, _ http.Header) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +type PreparedMessage struct{} diff --git a/ql/test/query-tests/Security/CWE-918/vendor/github.com/sacOO7/gowebsocket/LICENSE b/ql/test/query-tests/Security/CWE-918/vendor/github.com/sacOO7/gowebsocket/LICENSE new file mode 100644 index 00000000000..95ab2c9a687 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/github.com/sacOO7/gowebsocket/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Sachin Shinde + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ql/test/query-tests/Security/CWE-918/vendor/github.com/sacOO7/gowebsocket/stub.go b/ql/test/query-tests/Security/CWE-918/vendor/github.com/sacOO7/gowebsocket/stub.go new file mode 100644 index 00000000000..f1f20c86857 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/github.com/sacOO7/gowebsocket/stub.go @@ -0,0 +1,58 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/sacOO7/gowebsocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/sacOO7/gowebsocket (exports: ; functions: New,BuildProxy) + +// Package gowebsocket is a stub of github.com/sacOO7/gowebsocket, generated by depstubber. +package gowebsocket + +import ( + http "net/http" + url "net/url" +) + +func BuildProxy(_ string) func(*http.Request) (*url.URL, error) { + return nil +} + +type ConnectionOptions struct { + UseCompression bool + UseSSL bool + Proxy func(*http.Request) (*url.URL, error) + Subprotocols []string +} + +func New(_ string) Socket { + return Socket{} +} + +type Socket struct { + Conn interface{} + WebsocketDialer interface{} + Url string + ConnectionOptions ConnectionOptions + RequestHeader http.Header + OnConnected func(Socket) + OnTextMessage func(string, Socket) + OnBinaryMessage func([]byte, Socket) + OnConnectError func(error, Socket) + OnDisconnected func(error, Socket) + OnPingReceived func(string, Socket) + OnPongReceived func(string, Socket) + IsConnected bool +} + +func (_ Socket) EnableLogging() {} + +func (_ Socket) GetLogger() interface{} { + return nil +} + +func (_ *Socket) Close() {} + +func (_ *Socket) Connect() {} + +func (_ *Socket) SendBinary(_ []byte) {} + +func (_ *Socket) SendText(_ string) {} diff --git a/ql/test/query-tests/Security/CWE-918/vendor/golang.org/x/net/websocket/LICENSE b/ql/test/query-tests/Security/CWE-918/vendor/golang.org/x/net/websocket/LICENSE new file mode 100644 index 00000000000..6a66aea5eaf --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/golang.org/x/net/websocket/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ql/test/query-tests/Security/CWE-918/vendor/golang.org/x/net/websocket/stub.go b/ql/test/query-tests/Security/CWE-918/vendor/golang.org/x/net/websocket/stub.go new file mode 100644 index 00000000000..b860854e6e8 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/golang.org/x/net/websocket/stub.go @@ -0,0 +1,120 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for golang.org/x/net/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: golang.org/x/net/websocket (exports: ; functions: Dial,NewConfig,DialConfig) + +// Package websocket is a stub of golang.org/x/net/websocket, generated by depstubber. +package websocket + +import ( + tls "crypto/tls" + io "io" + net "net" + http "net/http" + url "net/url" + time "time" +) + +type Config struct { + Location *url.URL + Origin *url.URL + Protocol []string + Version int + TlsConfig *tls.Config + Header http.Header + Dialer *net.Dialer +} + +type Conn struct { + PayloadType byte + MaxPayloadBytes int +} + +func (_ Conn) HandleFrame(_ interface{}) (interface{}, error) { + return nil, nil +} + +func (_ Conn) HeaderReader() io.Reader { + return nil +} + +func (_ Conn) Len() int { + return 0 +} + +func (_ Conn) NewFrameReader() (interface{}, error) { + return nil, nil +} + +func (_ Conn) NewFrameWriter(_ byte) (interface{}, error) { + return nil, nil +} + +func (_ Conn) TrailerReader() io.Reader { + return nil +} + +func (_ Conn) WriteClose(_ int) error { + return nil +} + +func (_ *Conn) Close() error { + return nil +} + +func (_ *Conn) Config() *Config { + return nil +} + +func (_ *Conn) IsClientConn() bool { + return false +} + +func (_ *Conn) IsServerConn() bool { + return false +} + +func (_ *Conn) LocalAddr() net.Addr { + return nil +} + +func (_ *Conn) Read(_ []byte) (int, error) { + return 0, nil +} + +func (_ *Conn) RemoteAddr() net.Addr { + return nil +} + +func (_ *Conn) Request() *http.Request { + return nil +} + +func (_ *Conn) SetDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetReadDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) SetWriteDeadline(_ time.Time) error { + return nil +} + +func (_ *Conn) Write(_ []byte) (int, error) { + return 0, nil +} + +func Dial(_ string, _ string, _ string) (*Conn, error) { + return nil, nil +} + +func DialConfig(_ *Config) (*Conn, error) { + return nil, nil +} + +func NewConfig(_ string, _ string) (*Config, error) { + return nil, nil +} diff --git a/ql/test/query-tests/Security/CWE-918/vendor/modules.txt b/ql/test/query-tests/Security/CWE-918/vendor/modules.txt new file mode 100644 index 00000000000..319b30b771b --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/modules.txt @@ -0,0 +1,15 @@ +# github.com/gobwas/ws v1.0.3 +## explicit +github.com/gobwas/ws +# github.com/gorilla/websocket v1.4.2 +## explicit +github.com/gorilla/websocket +# github.com/sacOO7/gowebsocket v0.0.0-20180719182212-1436bb906a4e +## explicit +github.com/sacOO7/gowebsocket +# golang.org/x/net v0.0.0-20200421231249-e086a090c8fd +## explicit +golang.org/x/net +# nhooyr.io/websocket v1.8.5 +## explicit +nhooyr.io/websocket diff --git a/ql/test/query-tests/Security/CWE-918/vendor/nhooyr.io/websocket/LICENSE b/ql/test/query-tests/Security/CWE-918/vendor/nhooyr.io/websocket/LICENSE new file mode 100644 index 00000000000..b5b5fef31f0 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/nhooyr.io/websocket/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Anmol Sethi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/query-tests/Security/CWE-918/vendor/nhooyr.io/websocket/stub.go b/ql/test/query-tests/Security/CWE-918/vendor/nhooyr.io/websocket/stub.go new file mode 100644 index 00000000000..7bf2a208dac --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/vendor/nhooyr.io/websocket/stub.go @@ -0,0 +1,76 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for nhooyr.io/websocket, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: nhooyr.io/websocket (exports: ; functions: Dial) + +// Package websocket is a stub of nhooyr.io/websocket, generated by depstubber. +package websocket + +import ( + context "context" + io "io" + http "net/http" +) + +type CompressionMode int + +type Conn struct{} + +func (_ *Conn) Close(_ StatusCode, _ string) error { + return nil +} + +func (_ *Conn) CloseRead(_ context.Context) context.Context { + return nil +} + +func (_ *Conn) Ping(_ context.Context) error { + return nil +} + +func (_ *Conn) Read(_ context.Context) (MessageType, []byte, error) { + return 0, nil, nil +} + +func (_ *Conn) Reader(_ context.Context) (MessageType, io.Reader, error) { + return 0, nil, nil +} + +func (_ *Conn) SetReadLimit(_ int64) {} + +func (_ *Conn) Subprotocol() string { + return "" +} + +func (_ *Conn) Write(_ context.Context, _ MessageType, _ []byte) error { + return nil +} + +func (_ *Conn) Writer(_ context.Context, _ MessageType) (io.WriteCloser, error) { + return nil, nil +} + +func Dial(_ context.Context, _ string, _ *DialOptions) (*Conn, *http.Response, error) { + return nil, nil, nil +} + +type DialOptions struct { + HTTPClient *http.Client + HTTPHeader http.Header + Subprotocols []string + CompressionMode CompressionMode + CompressionThreshold int +} + +type MessageType int + +func (_ MessageType) String() string { + return "" +} + +type StatusCode int + +func (_ StatusCode) String() string { + return "" +} diff --git a/ql/test/query-tests/Security/CWE-918/websocket.go b/ql/test/query-tests/Security/CWE-918/websocket.go new file mode 100644 index 00000000000..db613fe5fa5 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-918/websocket.go @@ -0,0 +1,209 @@ +package main + +//go:generate depstubber -vendor github.com/gobwas/ws Dialer Dial +//go:generate depstubber -vendor github.com/gorilla/websocket Dialer +//go:generate depstubber -vendor github.com/sacOO7/gowebsocket "" New,BuildProxy +//go:generate depstubber -vendor golang.org/x/net/websocket "" Dial,NewConfig,DialConfig +//go:generate depstubber -vendor nhooyr.io/websocket "" Dial + +import ( + "context" + "fmt" + "log" + "net/http" + "regexp" + "strings" + + gobwas "github.com/gobwas/ws" + gorilla "github.com/gorilla/websocket" + sac "github.com/sacOO7/gowebsocket" + "golang.org/x/net/websocket" + nhooyr "nhooyr.io/websocket" +) + +func test() { + // x net websocket Dial good + http.HandleFunc("/ex0", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + origin := "http://localhost/" + + untrustedInputTrimmed := strings.TrimRight(untrustedInput, "\n\r") + if untrustedInputTrimmed == "ws://localhost:12345/ws" { + // good as input is checked against fixed set of urls. + ws, _ := websocket.Dial(untrustedInputTrimmed, "", origin) + var msg = make([]byte, 512) + var n int + n, _ = ws.Read(msg) + fmt.Printf("Received: %s.\n", msg[:n]) + } + }) + + // x net websocket DialConfig good + http.HandleFunc("/ex1", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + origin := "http://localhost/" + // good as input is tested against a regex + if m, _ := regexp.MatchString("ws://localhost:12345/*", untrustedInput); m { + config, _ := websocket.NewConfig(untrustedInput, origin) // good + ws2, _ := websocket.DialConfig(config) + var msg = make([]byte, 512) + var n int + n, _ = ws2.Read(msg) + fmt.Printf("Received: %s.\n", msg[:n]) + } + }) + + // x net websocket dial bad + http.HandleFunc("/ex2", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + origin := "http://localhost/" + + // bad as input is directly passed to dial function + ws, _ := websocket.Dial(untrustedInput, "", origin) + var msg = make([]byte, 512) + var n int + n, _ = ws.Read(msg) + fmt.Printf("Received: %s.\n", msg[:n]) + }) + + // x net websocket dialConfig bad + http.HandleFunc("/ex3", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + origin := "http://localhost/" + // bad as input is directly used + config, _ := websocket.NewConfig(untrustedInput, origin) // good + ws2, _ := websocket.DialConfig(config) + var msg = make([]byte, 512) + var n int + n, _ = ws2.Read(msg) + fmt.Printf("Received: %s.\n", msg[:n]) + }) + + // nhooyr websocket dial bad + http.HandleFunc("/ex4", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + // bad as input is used directly + nhooyr.Dial(context.TODO(), untrustedInput, nil) + w.WriteHeader(500) + }) + + // nhooyr websocket dial good + http.HandleFunc("/ex5", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + // good as input is tested againt regex + if m, _ := regexp.MatchString("ws://localhost:12345/*", untrustedInput); m { + nhooyr.Dial(context.TODO(), untrustedInput, nil) + } + }) + + // gorilla websocket Dialer.Dial bad + http.HandleFunc("/ex6", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + dialer := gorilla.Dialer{} + dialer.Dial(untrustedInput, r.Header) + }) + + // gorilla websocket Dialer.Dial good + http.HandleFunc("/ex7", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + if untrustedInput == "localhost" { + + dialer := gorilla.Dialer{} + dialer.Dial(untrustedInput, r.Header) + } + }) + + // gorilla websocket Dialer.DialContext bad + http.HandleFunc("/ex8", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + dialer := gorilla.Dialer{} + dialer.DialContext(context.TODO(), untrustedInput, r.Header) + }) + + // gorilla websocket Dialer.DialContext good + http.HandleFunc("/ex9", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + if untrustedInput == "localhost" { + + dialer := gorilla.Dialer{} + dialer.DialContext(context.TODO(), untrustedInput, r.Header) + } + }) + + // gobwas websocket Dial good + http.HandleFunc("/ex10", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + if untrustedInput == "localhost" { + gobwas.Dial(context.TODO(), untrustedInput) + } + }) + + // gobwas websocket Dial bad + http.HandleFunc("/ex11", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + gobwas.Dial(context.TODO(), untrustedInput) + }) + + // gobwas websocket Dialer.Dial bad + http.HandleFunc("/ex12", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + dialer := gobwas.Dialer{} + dialer.Dial(context.TODO(), untrustedInput) + }) + + // gobwas websocket Dialer.Dial good + http.HandleFunc("/ex12", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + if "localhost" == untrustedInput { + dialer := gobwas.Dialer{} + dialer.Dial(context.TODO(), untrustedInput) + } + }) + + // sac007 websocket New good + http.HandleFunc("/ex13", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + if "localhost" == untrustedInput { + sac.New(untrustedInput) + } + }) + + // sac007 websocket BuildProxy good + http.HandleFunc("/ex14", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + if "localhost" == untrustedInput { + sac.BuildProxy(untrustedInput) + } + }) + + // sac007 websocket BuildProxy bad + http.HandleFunc("/ex15", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + sac.BuildProxy(untrustedInput) + }) + + // sac007 websocket New bad + http.HandleFunc("/ex16", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + sac.New(untrustedInput) + }) + + log.Println(http.ListenAndServe(":80", nil)) + +} diff --git a/ql/test/query-tests/filters/ClassifyFiles/go.mod b/ql/test/query-tests/filters/ClassifyFiles/go.mod index 62ce60299b7..aa05800e200 100644 --- a/ql/test/query-tests/filters/ClassifyFiles/go.mod +++ b/ql/test/query-tests/filters/ClassifyFiles/go.mod @@ -3,10 +3,6 @@ module filters.ClassifyFiles go 1.14 require ( - github.com/github/depstubber v0.0.0-20200414033246-a63ca77a1581 // indirect github.com/onsi/ginkgo v1.12.0 github.com/onsi/gomega v1.9.0 - golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect - golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect ) diff --git a/ql/test/query-tests/filters/ClassifyFiles/vendor/modules.txt b/ql/test/query-tests/filters/ClassifyFiles/vendor/modules.txt index b17b7a8df2c..b7e13eca83b 100644 --- a/ql/test/query-tests/filters/ClassifyFiles/vendor/modules.txt +++ b/ql/test/query-tests/filters/ClassifyFiles/vendor/modules.txt @@ -1,18 +1,6 @@ -# github.com/github/depstubber v0.0.0-20200414033246-a63ca77a1581 -## explicit -github.com/github/depstubber # github.com/onsi/ginkgo v1.12.0 ## explicit github.com/onsi/ginkgo # github.com/onsi/gomega v1.9.0 ## explicit github.com/onsi/gomega -# golang.org/x/net v0.0.0-20200226121028-0de0cce0169b -## explicit -golang.org/x/net -# golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e -## explicit -golang.org/x/sync -# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 -## explicit -golang.org/x/xerrors diff --git a/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/go.dbscheme b/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/go.dbscheme new file mode 100644 index 00000000000..ee5c327face --- /dev/null +++ b/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/go.dbscheme @@ -0,0 +1,430 @@ +/** Auto-generated dbscheme; do not edit. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref, string simple: string ref, string ext: string ref, int fromSource: int ref); + +folders(unique int id: @folder, string name: string ref, string simple: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +comment_groups(unique int id: @comment_group); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @namedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int named: @namedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +@container = @file | @folder; + +@locatable = @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode + | @comment_group | @comment; + +@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @sliceexpr +| 14 = @typeassertexpr +| 15 = @callorconversionexpr +| 16 = @starexpr +| 17 = @keyvalueexpr +| 18 = @arraytypeexpr +| 19 = @structtypeexpr +| 20 = @functypeexpr +| 21 = @interfacetypeexpr +| 22 = @maptypeexpr +| 23 = @plusexpr +| 24 = @minusexpr +| 25 = @notexpr +| 26 = @complementexpr +| 27 = @derefexpr +| 28 = @addressexpr +| 29 = @arrowexpr +| 30 = @lorexpr +| 31 = @landexpr +| 32 = @eqlexpr +| 33 = @neqexpr +| 34 = @lssexpr +| 35 = @leqexpr +| 36 = @gtrexpr +| 37 = @geqexpr +| 38 = @addexpr +| 39 = @subexpr +| 40 = @orexpr +| 41 = @xorexpr +| 42 = @mulexpr +| 43 = @quoexpr +| 44 = @remexpr +| 45 = @shlexpr +| 46 = @shrexpr +| 47 = @andexpr +| 48 = @andnotexpr +| 49 = @sendchantypeexpr +| 50 = @recvchantypeexpr +| 51 = @sendrcvchantypeexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typespec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @arraytype +| 27 = @slicetype +| 28 = @structtype +| 29 = @pointertype +| 30 = @interfacetype +| 31 = @tupletype +| 32 = @signaturetype +| 33 = @maptype +| 34 = @sendchantype +| 35 = @recvchantype +| 36 = @sendrcvchantype +| 37 = @namedtype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @containertype | @structtype | @pointertype | @interfacetype | @tupletype | @signaturetype | @namedtype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/old.dbscheme b/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/old.dbscheme new file mode 100644 index 00000000000..f7fb4ff6229 --- /dev/null +++ b/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/old.dbscheme @@ -0,0 +1,420 @@ +/** Auto-generated dbscheme; do not edit. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref, string simple: string ref, string ext: string ref, int fromSource: int ref); + +folders(unique int id: @folder, string name: string ref, string simple: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +comment_groups(unique int id: @comment_group); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @namedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int named: @namedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +@container = @file | @folder; + +@locatable = @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode + | @comment_group | @comment; + +@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @sliceexpr +| 14 = @typeassertexpr +| 15 = @callorconversionexpr +| 16 = @starexpr +| 17 = @keyvalueexpr +| 18 = @arraytypeexpr +| 19 = @structtypeexpr +| 20 = @functypeexpr +| 21 = @interfacetypeexpr +| 22 = @maptypeexpr +| 23 = @plusexpr +| 24 = @minusexpr +| 25 = @notexpr +| 26 = @complementexpr +| 27 = @derefexpr +| 28 = @addressexpr +| 29 = @arrowexpr +| 30 = @lorexpr +| 31 = @landexpr +| 32 = @eqlexpr +| 33 = @neqexpr +| 34 = @lssexpr +| 35 = @leqexpr +| 36 = @gtrexpr +| 37 = @geqexpr +| 38 = @addexpr +| 39 = @subexpr +| 40 = @orexpr +| 41 = @xorexpr +| 42 = @mulexpr +| 43 = @quoexpr +| 44 = @remexpr +| 45 = @shlexpr +| 46 = @shrexpr +| 47 = @andexpr +| 48 = @andnotexpr +| 49 = @sendchantypeexpr +| 50 = @recvchantypeexpr +| 51 = @sendrcvchantypeexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typespec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @arraytype +| 27 = @slicetype +| 28 = @structtype +| 29 = @pointertype +| 30 = @interfacetype +| 31 = @tupletype +| 32 = @signaturetype +| 33 = @maptype +| 34 = @sendchantype +| 35 = @recvchantype +| 36 = @sendrcvchantype +| 37 = @namedtype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @containertype | @structtype | @pointertype | @interfacetype | @tupletype | @signaturetype | @namedtype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + diff --git a/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/upgrade.properties b/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/upgrade.properties new file mode 100644 index 00000000000..67fada3ac3b --- /dev/null +++ b/upgrades/f7fb4ff6229adffa2c2c4238ef72c82359d56be4/upgrade.properties @@ -0,0 +1,2 @@ +description: Add tables for extracting frontend errors. +compatibility: backwards