From 06f86dd22f33185c817cdc5127aba859ec65d05d Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Aug 2024 12:21:32 +0100 Subject: [PATCH 01/16] Convert squirrel sql-injection sinks to MaD (non-existent methods removed) Various non-existent methods were modeled, and I couldn't find any evidence that they used to exist. They aren't in the stubs or tests. I have removed them. --- .../github.com.mastermind.squirrel.model.yml | 51 +++++++++++++++++++ go/ql/lib/semmle/go/frameworks/SQL.qll | 44 +++------------- .../semmle/go/frameworks/SQL/main.go | 18 +++---- 3 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 go/ql/lib/ext/github.com.mastermind.squirrel.model.yml diff --git a/go/ql/lib/ext/github.com.mastermind.squirrel.model.yml b/go/ql/lib/ext/github.com.mastermind.squirrel.model.yml new file mode 100644 index 000000000000..a5f46d7c2149 --- /dev/null +++ b/go/ql/lib/ext/github.com.mastermind.squirrel.model.yml @@ -0,0 +1,51 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: packageGrouping + data: + - ["squirrel", "github.com/Masterminds/squirrel"] + - ["squirrel", "gopkg.in/Masterminds/squirrel"] + - ["squirrel", "github.com/lann/squirrel"] + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["group:squirrel", "", True, "Delete", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "", True, "Expr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "", True, "Insert", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "", True, "Select", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["group:squirrel", "", True, "Update", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + + - ["group:squirrel", "DeleteBuilder", True, "From", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "DeleteBuilder", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["group:squirrel", "DeleteBuilder", True, "Prefix", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "DeleteBuilder", True, "Suffix", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "DeleteBuilder", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + + - ["group:squirrel", "InsertBuilder", True, "Columns", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["group:squirrel", "InsertBuilder", True, "Into", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "InsertBuilder", True, "Options", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["group:squirrel", "InsertBuilder", True, "Prefix", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "InsertBuilder", True, "Suffix", "", "", "Argument[0]", "sql-injection", "manual"] + + - ["group:squirrel", "SelectBuilder", True, "CrossJoin", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "Column", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "Columns", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["group:squirrel", "SelectBuilder", True, "From", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "GroupBy", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "InnerJoin", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "LeftJoin", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "Options", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["group:squirrel", "SelectBuilder", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["group:squirrel", "SelectBuilder", True, "Prefix", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "RightJoin", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "Suffix", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "SelectBuilder", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + + - ["group:squirrel", "UpdateBuilder", True, "From", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "UpdateBuilder", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["group:squirrel", "UpdateBuilder", True, "Prefix", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "UpdateBuilder", True, "Set", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "UpdateBuilder", True, "Suffix", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "UpdateBuilder", True, "Table", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:squirrel", "UpdateBuilder", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index 1a6e22807812..73cd5d93947e 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -67,42 +67,14 @@ module SQL { */ abstract class Range extends DataFlow::Node { } - /** - * An argument to an API of the squirrel library that is directly interpreted as SQL without - * taking syntactic structure into account. - */ - private class SquirrelQueryString extends Range { - SquirrelQueryString() { - exists(Function fn | - exists(string sq | - sq = - package([ - "github.com/Masterminds/squirrel", "gopkg.in/Masterminds/squirrel", - "github.com/lann/squirrel" - ], "") - | - fn.hasQualifiedName(sq, ["Delete", "Expr", "Insert", "Select", "Update"]) - or - exists(Method m, string builder | m = fn | - builder = ["DeleteBuilder", "InsertBuilder", "SelectBuilder", "UpdateBuilder"] and - m.hasQualifiedName(sq, builder, - ["Columns", "From", "Options", "OrderBy", "Prefix", "Suffix", "Where"]) - or - builder = "InsertBuilder" and - m.hasQualifiedName(sq, builder, ["Replace", "Into"]) - or - builder = "SelectBuilder" and - m.hasQualifiedName(sq, builder, - ["CrossJoin", "GroupBy", "InnerJoin", "LeftJoin", "RightJoin"]) - or - builder = "UpdateBuilder" and - m.hasQualifiedName(sq, builder, ["Set", "Table"]) - ) - ) and - this = fn.getACall().getArgument(0) - | - this.getType().getUnderlyingType() instanceof StringType or - this.getType().getUnderlyingType().(SliceType).getElementType() instanceof StringType + private class DefaultQueryString extends Range { + DefaultQueryString() { + exists(DataFlow::ArgumentNode arg | sinkNode(arg, "sql-injection") | + not arg instanceof DataFlow::ImplicitVarargsSlice and + this = arg + or + arg instanceof DataFlow::ImplicitVarargsSlice and + this = arg.getCall().getAnImplicitVarargsArgument() ) } } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/main.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/main.go index dd21a91eedd8..0af6e0028066 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/main.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/main.go @@ -46,35 +46,35 @@ func squirrelTest(querypart string) { squirrel.Expr(querypart) // $ querystring=querypart deleteBuilder := squirrel.Delete(querypart) // $ querystring=querypart deleteBuilder.From(querypart) // $ querystring=querypart - deleteBuilder.OrderBy(querypart) // $ querystring=[]type{args} + deleteBuilder.OrderBy(querypart) // $ querystring=querypart deleteBuilder.Prefix(querypart) // $ querystring=querypart deleteBuilder.Suffix(querypart) // $ querystring=querypart deleteBuilder.Where(querypart) // $ querystring=querypart insertBuilder := squirrel.Insert(querypart) // $ querystring=querypart - insertBuilder.Columns(querypart) // $ querystring=[]type{args} - insertBuilder.Options(querypart) // $ querystring=[]type{args} + insertBuilder.Columns(querypart) // $ querystring=querypart + insertBuilder.Options(querypart) // $ querystring=querypart insertBuilder.Prefix(querypart) // $ querystring=querypart insertBuilder.Suffix(querypart) // $ querystring=querypart insertBuilder.Into(querypart) // $ querystring=querypart - selectBuilder := squirrel.Select(querypart) // $ querystring=[]type{args} - selectBuilder.Columns(querypart) // $ querystring=[]type{args} + selectBuilder := squirrel.Select(querypart) // $ querystring=querypart + selectBuilder.Columns(querypart) // $ querystring=querypart selectBuilder.From(querypart) // $ querystring=querypart - selectBuilder.Options(querypart) // $ querystring=[]type{args} - selectBuilder.OrderBy(querypart) // $ querystring=[]type{args} + selectBuilder.Options(querypart) // $ querystring=querypart + selectBuilder.OrderBy(querypart) // $ querystring=querypart selectBuilder.Prefix(querypart) // $ querystring=querypart selectBuilder.Suffix(querypart) // $ querystring=querypart selectBuilder.Where(querypart) // $ querystring=querypart selectBuilder.CrossJoin(querypart) // $ querystring=querypart - selectBuilder.GroupBy(querypart) // $ querystring=[]type{args} + selectBuilder.GroupBy(querypart) // $ querystring=querypart selectBuilder.InnerJoin(querypart) // $ querystring=querypart selectBuilder.LeftJoin(querypart) // $ querystring=querypart selectBuilder.RightJoin(querypart) // $ querystring=querypart updateBuilder := squirrel.Update(querypart) // $ querystring=querypart updateBuilder.From(querypart) // $ querystring=querypart - updateBuilder.OrderBy(querypart) // $ querystring=[]type{args} + updateBuilder.OrderBy(querypart) // $ querystring=querypart updateBuilder.Prefix(querypart) // $ querystring=querypart updateBuilder.Suffix(querypart) // $ querystring=querypart updateBuilder.Where(querypart) // $ querystring=querypart From ce0cb12c2933ade4d3e7776342180b56ee5e8cd4 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 12:25:24 +0100 Subject: [PATCH 02/16] Upgrade and convert gorqlite sql-injection sinks to MaD --- .../ext/github.com.rqlite.gorqlite.model.yml | 35 ++++++ go/ql/lib/semmle/go/frameworks/SQL.qll | 24 ---- .../SQL/gorqlite/QueryString.expected | 3 + .../go/frameworks/SQL/gorqlite/QueryString.ql | 60 +++++++++ .../semmle/go/frameworks/SQL/gorqlite/go.mod | 2 +- .../frameworks/SQL/gorqlite/gorqlite.expected | 6 - .../go/frameworks/SQL/gorqlite/gorqlite.go | 39 +++++- .../go/frameworks/SQL/gorqlite/gorqlite.ql | 4 - .../vendor/github.com/rqlite/gorqlite/stub.go | 115 +++++++++++++++++- .../SQL/gorqlite/vendor/modules.txt | 2 +- 10 files changed, 245 insertions(+), 45 deletions(-) create mode 100644 go/ql/lib/ext/github.com.rqlite.gorqlite.model.yml create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.expected create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.ql delete mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.expected delete mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.ql diff --git a/go/ql/lib/ext/github.com.rqlite.gorqlite.model.yml b/go/ql/lib/ext/github.com.rqlite.gorqlite.model.yml new file mode 100644 index 000000000000..62e24f2c920b --- /dev/null +++ b/go/ql/lib/ext/github.com.rqlite.gorqlite.model.yml @@ -0,0 +1,35 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: packageGrouping + data: + - ["gorqlite", "github.com/rqlite/gorqlite"] + - ["gorqlite", "github.com/raindog308/gorqlite"] + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["group:gorqlite", "Connection", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueryOne", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueryOneContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueryOneParameterized", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueryOneParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueryParameterized", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueryParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "Queue", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueueContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueueOne", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueueOneContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueueOneParameterized", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueueOneParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueueParameterized", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "QueueParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "Write", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "WriteContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "WriteOne", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "WriteOneContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "WriteOneParameterized", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "WriteOneParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "WriteParameterized", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorqlite", "Connection", True, "WriteParameterizedContext", "", "", "Argument[1]", "sql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index 73cd5d93947e..e6318df82f2d 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -85,11 +85,6 @@ module SQL { /** A string that might identify package `go-pg/pg/orm` or a specific version of it. */ private string gopgorm() { result = package("github.com/go-pg/pg", "orm") } - /** A string that might identify package `github.com/rqlite/gorqlite` or `github.com/raindog308/gorqlite` or a specific version of it. */ - private string gorqlite() { - result = package(["github.com/rqlite/gorqlite", "github.com/raindog308/gorqlite"], "") - } - /** A string that might identify package `github.com/gogf/gf/database/gdb` or a specific version of it. */ private string gogf() { result = package("github.com/gogf/gf", "database/gdb") } @@ -158,25 +153,6 @@ module SQL { } } - /** - * A string argument to an API of `github.com/rqlite/gorqlite`, or a specific version of it, that is directly interpreted as SQL without - * taking syntactic structure into account. - */ - private class GorqliteQueryString extends Range { - GorqliteQueryString() { - // func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, err error) - // func (conn *Connection) QueryOne(sqlStatement string) (qr QueryResult, err error) - // func (conn *Connection) Queue(sqlStatements []string) (seq int64, err error) - // func (conn *Connection) QueueOne(sqlStatement string) (seq int64, err error) - // func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, err error) - // func (conn *Connection) WriteOne(sqlStatement string) (wr WriteResult, err error) - exists(Method m, string name | m.hasQualifiedName(gorqlite(), "Connection", name) | - name = ["Query", "QueryOne", "Queue", "QueueOne", "Write", "WriteOne"] and - this = m.getACall().getArgument(0) - ) - } - } - /** * A string argument to an API of `github.com/gogf/gf/database/gdb`, or a specific version of it, that is directly interpreted as SQL without * taking syntactic structure into account. diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.expected new file mode 100644 index 000000000000..db33d6d2504a --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.expected @@ -0,0 +1,3 @@ +testFailures +invalidModelRow +failures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.ql new file mode 100644 index 000000000000..eeb43a82fadd --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/QueryString.ql @@ -0,0 +1,60 @@ +import go +import semmle.go.dataflow.ExternalFlow +import ModelValidation +import TestUtilities.InlineExpectationsTest + +module SqlTest implements TestSig { + string getARelevantTag() { result = "query" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "query" and + exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() | + q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = q.toString() and + value = qs.toString() + ) + } +} + +module QueryString implements TestSig { + string getARelevantTag() { result = "querystring" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "querystring" and + element = "" and + exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) | + qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + value = qs.toString() + ) + } +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit } + + predicate isSink(DataFlow::Node n) { + n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument() + } +} + +module Flow = TaintTracking::Global; + +module TaintFlow implements TestSig { + string getARelevantTag() { result = "flowfrom" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "flowfrom" and + element = "" and + exists(DataFlow::Node fromNode, DataFlow::Node toNode | + toNode + .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + Flow::flow(fromNode, toNode) and + value = fromNode.asExpr().(StringLit).getValue() + ) + } +} + +import MakeTest> diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/go.mod b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/go.mod index 1f243775658a..826ed0eb1c05 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/go.mod +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/go.mod @@ -2,4 +2,4 @@ module main go 1.18 -require github.com/rqlite/gorqlite v0.0.0-20220528150909-c4e99ae96be6 +require github.com/rqlite/gorqlite v0.0.0-20240808172217-12ae7d03ef19 diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.expected deleted file mode 100644 index cbd8166ea5e1..000000000000 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.expected +++ /dev/null @@ -1,6 +0,0 @@ -| gorqlite.go:11:13:11:16 | sqls | -| gorqlite.go:12:13:12:16 | sqls | -| gorqlite.go:13:13:13:16 | sqls | -| gorqlite.go:14:16:14:18 | sql | -| gorqlite.go:15:16:15:18 | sql | -| gorqlite.go:16:16:16:18 | sql | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.go index 9b60c6684e67..ebd6e5fd9f3b 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.go @@ -1,20 +1,49 @@ package main -//go:generate depstubber -vendor github.com/rqlite/gorqlite Connection Open +//go:generate depstubber -vendor github.com/rqlite/gorqlite Connection,ParameterizedStatement Open import ( + "context" + "github.com/rqlite/gorqlite" ) -func gorqlitetest(sql string, sqls []string) { +func gorqlitetest(sql string, sqls []string, param_sql gorqlite.ParameterizedStatement, param_sqls []gorqlite.ParameterizedStatement, ctx context.Context) { conn, _ := gorqlite.Open("dbUrl") - conn.Query(sqls) // $ querystring=sqls - conn.Queue(sqls) // $ querystring=sqls - conn.Write(sqls) // $ querystring=sqls + + conn.Query(sqls) // $ querystring=sqls + conn.Queue(sqls) // $ querystring=sqls + conn.Write(sqls) // $ querystring=sqls + conn.QueryOne(sql) // $ querystring=sql conn.QueueOne(sql) // $ querystring=sql conn.WriteOne(sql) // $ querystring=sql + + conn.QueryParameterized(param_sqls) // $ querystring=param_sqls + conn.QueueParameterized(param_sqls) // $ querystring=param_sqls + conn.WriteParameterized(param_sqls) // $ querystring=param_sqls + + conn.QueryOneParameterized(param_sql) // $ querystring=param_sql + conn.QueueOneParameterized(param_sql) // $ querystring=param_sql + conn.WriteOneParameterized(param_sql) // $ querystring=param_sql + + conn.QueryContext(ctx, sqls) // $ querystring=sqls + conn.QueueContext(ctx, sqls) // $ querystring=sqls + conn.WriteContext(ctx, sqls) // $ querystring=sqls + + conn.QueryOneContext(ctx, sql) // $ querystring=sql + conn.QueueOneContext(ctx, sql) // $ querystring=sql + conn.WriteOneContext(ctx, sql) // $ querystring=sql + + conn.QueryParameterizedContext(ctx, param_sqls) // $ querystring=param_sqls + conn.QueueParameterizedContext(ctx, param_sqls) // $ querystring=param_sqls + conn.WriteParameterizedContext(ctx, param_sqls) // $ querystring=param_sqls + + conn.QueryOneParameterizedContext(ctx, param_sql) // $ querystring=param_sql + conn.QueueOneParameterizedContext(ctx, param_sql) // $ querystring=param_sql + conn.WriteOneParameterizedContext(ctx, param_sql) // $ querystring=param_sql } + func main() { return } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.ql deleted file mode 100644 index 7b56fd974419..000000000000 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/gorqlite.ql +++ /dev/null @@ -1,4 +0,0 @@ -import go - -from SQL::QueryString qs -select qs diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/github.com/rqlite/gorqlite/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/github.com/rqlite/gorqlite/stub.go index f6f4ca18ec12..0572097582e0 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/github.com/rqlite/gorqlite/stub.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/github.com/rqlite/gorqlite/stub.go @@ -2,11 +2,15 @@ // This is a simple stub for github.com/rqlite/gorqlite, strictly for use in testing. // See the LICENSE file for information about the licensing of the original library. -// Source: github.com/rqlite/gorqlite (exports: Connection; functions: Open) +// Source: github.com/rqlite/gorqlite (exports: Connection,ParameterizedStatement; functions: Open) // Package gorqlite is a stub of github.com/rqlite/gorqlite, generated by depstubber. package gorqlite +import ( + context "context" +) + type Connection struct { ID string } @@ -29,19 +33,83 @@ func (_ *Connection) Query(_ []string) ([]QueryResult, error) { return nil, nil } +func (_ *Connection) QueryContext(_ context.Context, _ []string) ([]QueryResult, error) { + return nil, nil +} + func (_ *Connection) QueryOne(_ string) (QueryResult, error) { return QueryResult{}, nil } +func (_ *Connection) QueryOneContext(_ context.Context, _ string) (QueryResult, error) { + return QueryResult{}, nil +} + +func (_ *Connection) QueryOneParameterized(_ ParameterizedStatement) (QueryResult, error) { + return QueryResult{}, nil +} + +func (_ *Connection) QueryOneParameterizedContext(_ context.Context, _ ParameterizedStatement) (QueryResult, error) { + return QueryResult{}, nil +} + +func (_ *Connection) QueryParameterized(_ []ParameterizedStatement) ([]QueryResult, error) { + return nil, nil +} + +func (_ *Connection) QueryParameterizedContext(_ context.Context, _ []ParameterizedStatement) ([]QueryResult, error) { + return nil, nil +} + func (_ *Connection) Queue(_ []string) (int64, error) { return 0, nil } +func (_ *Connection) QueueContext(_ context.Context, _ []string) (int64, error) { + return 0, nil +} + func (_ *Connection) QueueOne(_ string) (int64, error) { return 0, nil } -func (_ *Connection) SetConsistencyLevel(_ string) error { +func (_ *Connection) QueueOneContext(_ context.Context, _ string) (int64, error) { + return 0, nil +} + +func (_ *Connection) QueueOneParameterized(_ ParameterizedStatement) (int64, error) { + return 0, nil +} + +func (_ *Connection) QueueOneParameterizedContext(_ context.Context, _ ParameterizedStatement) (int64, error) { + return 0, nil +} + +func (_ *Connection) QueueParameterized(_ []ParameterizedStatement) (int64, error) { + return 0, nil +} + +func (_ *Connection) QueueParameterizedContext(_ context.Context, _ []ParameterizedStatement) (int64, error) { + return 0, nil +} + +func (_ *Connection) Request(_ []string) ([]RequestResult, error) { + return nil, nil +} + +func (_ *Connection) RequestContext(_ context.Context, _ []string) ([]RequestResult, error) { + return nil, nil +} + +func (_ *Connection) RequestParameterized(_ []ParameterizedStatement) ([]RequestResult, error) { + return nil, nil +} + +func (_ *Connection) RequestParameterizedContext(_ context.Context, _ []ParameterizedStatement) ([]RequestResult, error) { + return nil, nil +} + +func (_ *Connection) SetConsistencyLevel(_ interface{}) error { return nil } @@ -53,12 +121,41 @@ func (_ *Connection) Write(_ []string) ([]WriteResult, error) { return nil, nil } +func (_ *Connection) WriteContext(_ context.Context, _ []string) ([]WriteResult, error) { + return nil, nil +} + func (_ *Connection) WriteOne(_ string) (WriteResult, error) { return WriteResult{}, nil } -func Open(_ string) (Connection, error) { - return Connection{}, nil +func (_ *Connection) WriteOneContext(_ context.Context, _ string) (WriteResult, error) { + return WriteResult{}, nil +} + +func (_ *Connection) WriteOneParameterized(_ ParameterizedStatement) (WriteResult, error) { + return WriteResult{}, nil +} + +func (_ *Connection) WriteOneParameterizedContext(_ context.Context, _ ParameterizedStatement) (WriteResult, error) { + return WriteResult{}, nil +} + +func (_ *Connection) WriteParameterized(_ []ParameterizedStatement) ([]WriteResult, error) { + return nil, nil +} + +func (_ *Connection) WriteParameterizedContext(_ context.Context, _ []ParameterizedStatement) ([]WriteResult, error) { + return nil, nil +} + +func Open(_ string) (*Connection, error) { + return nil, nil +} + +type ParameterizedStatement struct { + Query string + Arguments []interface{} } type QueryResult struct { @@ -90,10 +187,20 @@ func (_ *QueryResult) Scan(_ ...interface{}) error { return nil } +func (_ *QueryResult) Slice() ([]interface{}, error) { + return nil, nil +} + func (_ *QueryResult) Types() []string { return nil } +type RequestResult struct { + Err error + Query *QueryResult + Write *WriteResult +} + type WriteResult struct { Err error Timing float64 diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/modules.txt b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/modules.txt index f5e5b9989ede..dafb7c6d1a98 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/modules.txt +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gorqlite/vendor/modules.txt @@ -1,3 +1,3 @@ -# github.com/rqlite/gorqlite v0.0.0-20220528150909-c4e99ae96be6 +# github.com/rqlite/gorqlite v0.0.0-20240808172217-12ae7d03ef19 ## explicit github.com/rqlite/gorqlite From db559f75b69fceefea0ad9e64a65031182b601af Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 12:50:27 +0100 Subject: [PATCH 03/16] Convert gogf/gf sql-injection sinks to MaD --- .../github.com.gogf.gf.database.gdb.model.yml | 57 ++++++++++++++++++ go/ql/lib/semmle/go/frameworks/SQL.qll | 43 ------------- .../frameworks/SQL/gogf/QueryString.expected | 3 + .../go/frameworks/SQL/gogf/QueryString.ql | 60 +++++++++++++++++++ .../go/frameworks/SQL/gogf/gogf.expected | 47 --------------- .../semmle/go/frameworks/SQL/gogf/gogf.go | 38 ++++++------ .../semmle/go/frameworks/SQL/gogf/gogf.ql | 4 -- 7 files changed, 140 insertions(+), 112 deletions(-) create mode 100644 go/ql/lib/ext/github.com.gogf.gf.database.gdb.model.yml create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.expected create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.ql delete mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.expected delete mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.ql diff --git a/go/ql/lib/ext/github.com.gogf.gf.database.gdb.model.yml b/go/ql/lib/ext/github.com.gogf.gf.database.gdb.model.yml new file mode 100644 index 000000000000..030656c6eb8a --- /dev/null +++ b/go/ql/lib/ext/github.com.gogf.gf.database.gdb.model.yml @@ -0,0 +1,57 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + # These models are for v1. Some of them hold for v2, but we should model v2 properly. + - ["github.com/gogf/gf/database/gdb", "Core", True, "DoCommit", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "DoExec", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "DoGetAll", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "DoQuery", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "DoPrepare", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "GetAll", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "GetArray", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "GetCount", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "GetOne", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "GetScan", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "GetStruct", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "GetStructs", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "GetValue", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Core", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "DoCommit", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "DoExec", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "DoGetAll", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "DoQuery", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "DoPrepare", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "GetAll", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "GetArray", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "GetCount", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "GetOne", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "GetScan", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "GetStruct", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "GetStructs", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "GetValue", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "DB", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoCommit", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoExec", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoGetAll", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoQuery", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "DoPrepare", "", "", "Argument[2]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetAll", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetArray", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetCount", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetOne", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetScan", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetStruct", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetStructs", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "GetValue", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/gogf/gf/database/gdb", "Tx", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index e6318df82f2d..f960c214d84b 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -85,9 +85,6 @@ module SQL { /** A string that might identify package `go-pg/pg/orm` or a specific version of it. */ private string gopgorm() { result = package("github.com/go-pg/pg", "orm") } - /** A string that might identify package `github.com/gogf/gf/database/gdb` or a specific version of it. */ - private string gogf() { result = package("github.com/gogf/gf", "database/gdb") } - /** * A string argument to an API of `go-pg/pg` that is directly interpreted as SQL without * taking syntactic structure into account. @@ -152,46 +149,6 @@ module SQL { ) } } - - /** - * A string argument to an API of `github.com/gogf/gf/database/gdb`, or a specific version of it, that is directly interpreted as SQL without - * taking syntactic structure into account. - */ - private class GogfQueryString extends Range { - GogfQueryString() { - exists(Method m, string name | m.implements(gogf(), ["DB", "Core", "TX"], name) | - // func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) - // func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) - // func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) - // func (c *Core) GetCount(sql string, args ...interface{}) (int, error) - // func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) - // func (c *Core) GetValue(sql string, args ...interface{}) (Value, error) - // func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) - // func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) - // func (c *Core) Raw(rawSql string, args ...interface{}) *Model - name = - [ - "Query", "Exec", "Prepare", "GetAll", "GetOne", "GetValue", "GetArray", "GetCount", - "Raw" - ] and - this = m.getACall().getArgument(0) - or - // func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) error - // func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) error - // func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{}) error - name = ["GetScan", "GetStruct", "GetStructs"] and - this = m.getACall().getArgument(1) - or - // func (c *Core) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) - // func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) - // func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) - // func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) - // func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) - name = ["DoGetAll", "DoQuery", "DoExec", "DoCommit", "DoPrepare"] and - this = m.getACall().getArgument(2) - ) - } - } } /** A model for sinks of GORM. */ diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.expected new file mode 100644 index 000000000000..db33d6d2504a --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.expected @@ -0,0 +1,3 @@ +testFailures +invalidModelRow +failures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.ql new file mode 100644 index 000000000000..eeb43a82fadd --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/QueryString.ql @@ -0,0 +1,60 @@ +import go +import semmle.go.dataflow.ExternalFlow +import ModelValidation +import TestUtilities.InlineExpectationsTest + +module SqlTest implements TestSig { + string getARelevantTag() { result = "query" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "query" and + exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() | + q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = q.toString() and + value = qs.toString() + ) + } +} + +module QueryString implements TestSig { + string getARelevantTag() { result = "querystring" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "querystring" and + element = "" and + exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) | + qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + value = qs.toString() + ) + } +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit } + + predicate isSink(DataFlow::Node n) { + n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument() + } +} + +module Flow = TaintTracking::Global; + +module TaintFlow implements TestSig { + string getARelevantTag() { result = "flowfrom" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "flowfrom" and + element = "" and + exists(DataFlow::Node fromNode, DataFlow::Node toNode | + toNode + .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + Flow::flow(fromNode, toNode) and + value = fromNode.asExpr().(StringLit).getValue() + ) + } +} + +import MakeTest> diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.expected deleted file mode 100644 index f4e3e4f15b0a..000000000000 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.expected +++ /dev/null @@ -1,47 +0,0 @@ -| gogf.go:12:9:12:11 | sql | -| gogf.go:13:11:13:13 | sql | -| gogf.go:14:13:14:15 | sql | -| gogf.go:15:13:15:15 | sql | -| gogf.go:16:11:16:13 | sql | -| gogf.go:17:13:17:15 | sql | -| gogf.go:18:12:18:14 | sql | -| gogf.go:19:10:19:12 | sql | -| gogf.go:20:8:20:10 | sql | -| gogf.go:21:17:21:19 | sql | -| gogf.go:22:19:22:21 | sql | -| gogf.go:23:20:23:22 | sql | -| gogf.go:24:23:24:25 | sql | -| gogf.go:25:21:25:23 | sql | -| gogf.go:26:23:26:25 | sql | -| gogf.go:27:22:27:24 | sql | -| gogf.go:28:24:28:26 | sql | -| gogf.go:32:9:32:11 | sql | -| gogf.go:33:11:33:13 | sql | -| gogf.go:34:13:34:15 | sql | -| gogf.go:35:13:35:15 | sql | -| gogf.go:36:11:36:13 | sql | -| gogf.go:37:13:37:15 | sql | -| gogf.go:38:12:38:14 | sql | -| gogf.go:39:10:39:12 | sql | -| gogf.go:40:8:40:10 | sql | -| gogf.go:41:17:41:19 | sql | -| gogf.go:42:23:42:25 | sql | -| gogf.go:43:21:43:23 | sql | -| gogf.go:44:23:44:25 | sql | -| gogf.go:45:22:45:24 | sql | -| gogf.go:46:24:46:26 | sql | -| gogf.go:51:9:51:11 | sql | -| gogf.go:52:11:52:13 | sql | -| gogf.go:53:13:53:15 | sql | -| gogf.go:54:13:54:15 | sql | -| gogf.go:55:11:55:13 | sql | -| gogf.go:56:13:56:15 | sql | -| gogf.go:57:12:57:14 | sql | -| gogf.go:58:10:58:12 | sql | -| gogf.go:59:8:59:10 | sql | -| gogf.go:60:17:60:19 | sql | -| gogf.go:61:23:61:25 | sql | -| gogf.go:62:21:62:23 | sql | -| gogf.go:63:23:63:25 | sql | -| gogf.go:64:22:64:24 | sql | -| gogf.go:65:24:65:26 | sql | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.go index d0eceaf862cf..e2db8016cf0d 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.go @@ -4,11 +4,13 @@ package main //go:generate depstubber -vendor github.com/gogf/gf/database/gdb DB,Core,TX "" import ( + "context" + "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/frame/g" ) -func gogfCoreTest(sql string, c *gdb.Core) { +func gogfCoreTest(sql string, c *gdb.Core, ctx context.Context) { c.Exec(sql, nil) // $ querystring=sql c.GetAll(sql, nil) // $ querystring=sql c.GetArray(sql, nil) // $ querystring=sql @@ -21,14 +23,14 @@ func gogfCoreTest(sql string, c *gdb.Core) { c.GetScan(nil, sql, nil) // $ querystring=sql c.GetStruct(nil, sql, nil) // $ querystring=sql c.GetStructs(nil, sql, nil) // $ querystring=sql - c.DoCommit(nil, nil, sql, nil) // $ querystring=sql - c.DoExec(nil, nil, sql, nil) // $ querystring=sql - c.DoGetAll(nil, nil, sql, nil) // $ querystring=sql - c.DoQuery(nil, nil, sql, nil) // $ querystring=sql - c.DoPrepare(nil, nil, sql) // $ querystring=sql + c.DoCommit(ctx, nil, sql, nil) // $ querystring=sql + c.DoExec(ctx, nil, sql, nil) // $ querystring=sql + c.DoGetAll(ctx, nil, sql, nil) // $ querystring=sql + c.DoQuery(ctx, nil, sql, nil) // $ querystring=sql + c.DoPrepare(ctx, nil, sql) // $ querystring=sql } -func gogfDbtest(sql string, c gdb.DB) { +func gogfDbtest(sql string, c gdb.DB, ctx context.Context) { c.Exec(sql, nil) // $ querystring=sql c.GetAll(sql, nil) // $ querystring=sql c.GetArray(sql, nil) // $ querystring=sql @@ -39,14 +41,14 @@ func gogfDbtest(sql string, c gdb.DB) { c.Query(sql, nil) // $ querystring=sql c.Raw(sql, nil) // $ querystring=sql c.GetScan(nil, sql, nil) // $ querystring=sql - c.DoCommit(nil, nil, sql, nil) // $ querystring=sql - c.DoExec(nil, nil, sql, nil) // $ querystring=sql - c.DoGetAll(nil, nil, sql, nil) // $ querystring=sql - c.DoQuery(nil, nil, sql, nil) // $ querystring=sql - c.DoPrepare(nil, nil, sql) // $ querystring=sql + c.DoCommit(ctx, nil, sql, nil) // $ querystring=sql + c.DoExec(ctx, nil, sql, nil) // $ querystring=sql + c.DoGetAll(ctx, nil, sql, nil) // $ querystring=sql + c.DoQuery(ctx, nil, sql, nil) // $ querystring=sql + c.DoPrepare(ctx, nil, sql) // $ querystring=sql } -func gogfGTest(sql string) { +func gogfGTest(sql string, ctx context.Context) { c := g.DB("ad") c.Exec(sql, nil) // $ querystring=sql c.GetAll(sql, nil) // $ querystring=sql @@ -58,11 +60,11 @@ func gogfGTest(sql string) { c.Query(sql, nil) // $ querystring=sql c.Raw(sql, nil) // $ querystring=sql c.GetScan(nil, sql, nil) // $ querystring=sql - c.DoCommit(nil, nil, sql, nil) // $ querystring=sql - c.DoExec(nil, nil, sql, nil) // $ querystring=sql - c.DoGetAll(nil, nil, sql, nil) // $ querystring=sql - c.DoQuery(nil, nil, sql, nil) // $ querystring=sql - c.DoPrepare(nil, nil, sql) // $ querystring=sql + c.DoCommit(ctx, nil, sql, nil) // $ querystring=sql + c.DoExec(ctx, nil, sql, nil) // $ querystring=sql + c.DoGetAll(ctx, nil, sql, nil) // $ querystring=sql + c.DoQuery(ctx, nil, sql, nil) // $ querystring=sql + c.DoPrepare(ctx, nil, sql) // $ querystring=sql } func main() { diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.ql deleted file mode 100644 index 7b56fd974419..000000000000 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/gogf/gogf.ql +++ /dev/null @@ -1,4 +0,0 @@ -import go - -from SQL::QueryString qs -select qs From 7ad63fc3e6df9c212add21742de11337ff525afc Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 12:57:42 +0100 Subject: [PATCH 04/16] Convert sqlx sql-injection sinks to MaD --- .../lib/ext/github.com.jmoiron.sqlx.model.yml | 17 ++++++ go/ql/lib/semmle/go/frameworks/SQL.qll | 14 ----- .../frameworks/SQL/Sqlx/QueryString.expected | 3 + .../go/frameworks/SQL/Sqlx/QueryString.ql | 60 +++++++++++++++++++ .../go/frameworks/SQL/Sqlx/sqlx.expected | 12 ---- .../semmle/go/frameworks/SQL/Sqlx/sqlx.go | 24 ++++---- .../semmle/go/frameworks/SQL/Sqlx/sqlx.ql | 4 -- 7 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 go/ql/lib/ext/github.com.jmoiron.sqlx.model.yml create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.expected create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.ql delete mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected delete mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql diff --git a/go/ql/lib/ext/github.com.jmoiron.sqlx.model.yml b/go/ql/lib/ext/github.com.jmoiron.sqlx.model.yml new file mode 100644 index 000000000000..8c9d19b4b85b --- /dev/null +++ b/go/ql/lib/ext/github.com.jmoiron.sqlx.model.yml @@ -0,0 +1,17 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["github.com/jmoiron/sqlx", "DB", True, "Get", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "DB", True, "MustExec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "DB", True, "NamedExec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "DB", True, "NamedQuery", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "DB", True, "Queryx", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "DB", True, "Select", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "Tx", True, "Get", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "Tx", True, "MustExec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "Tx", True, "NamedExec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "Tx", True, "NamedQuery", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "Tx", True, "Queryx", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/jmoiron/sqlx", "Tx", True, "Select", "", "", "Argument[1]", "sql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index f960c214d84b..304c7f82d871 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -165,20 +165,6 @@ module SQL { ) } } - - /** A model for sinks of github.com/jmoiron/sqlx. */ - private class SqlxSink extends SQL::QueryString::Range { - SqlxSink() { - exists(Method meth, string name, int n | - meth.hasQualifiedName(package("github.com/jmoiron/sqlx", ""), ["DB", "Tx"], name) and - this = meth.getACall().getArgument(n) - | - name = ["Select", "Get"] and n = 1 - or - name = ["MustExec", "Queryx", "NamedExec", "NamedQuery"] and n = 0 - ) - } - } } /** diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.expected new file mode 100644 index 000000000000..db33d6d2504a --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.expected @@ -0,0 +1,3 @@ +testFailures +invalidModelRow +failures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.ql new file mode 100644 index 000000000000..eeb43a82fadd --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/QueryString.ql @@ -0,0 +1,60 @@ +import go +import semmle.go.dataflow.ExternalFlow +import ModelValidation +import TestUtilities.InlineExpectationsTest + +module SqlTest implements TestSig { + string getARelevantTag() { result = "query" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "query" and + exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() | + q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = q.toString() and + value = qs.toString() + ) + } +} + +module QueryString implements TestSig { + string getARelevantTag() { result = "querystring" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "querystring" and + element = "" and + exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) | + qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + value = qs.toString() + ) + } +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit } + + predicate isSink(DataFlow::Node n) { + n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument() + } +} + +module Flow = TaintTracking::Global; + +module TaintFlow implements TestSig { + string getARelevantTag() { result = "flowfrom" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "flowfrom" and + element = "" and + exists(DataFlow::Node fromNode, DataFlow::Node toNode | + toNode + .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + Flow::flow(fromNode, toNode) and + value = fromNode.asExpr().(StringLit).getValue() + ) + } +} + +import MakeTest> diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected deleted file mode 100644 index 0540a78fb343..000000000000 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected +++ /dev/null @@ -1,12 +0,0 @@ -| sqlx.go:15:17:15:25 | untrusted | -| sqlx.go:16:14:16:22 | untrusted | -| sqlx.go:17:14:17:22 | untrusted | -| sqlx.go:18:12:18:20 | untrusted | -| sqlx.go:19:15:19:23 | untrusted | -| sqlx.go:20:16:20:24 | untrusted | -| sqlx.go:23:17:23:25 | untrusted | -| sqlx.go:24:14:24:22 | untrusted | -| sqlx.go:25:14:25:22 | untrusted | -| sqlx.go:26:12:26:20 | untrusted | -| sqlx.go:27:15:27:23 | untrusted | -| sqlx.go:28:16:28:24 | untrusted | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go index edc29c4d4eef..e4e8d43395bd 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go @@ -12,19 +12,19 @@ func main() { db := sqlx.DB{} untrusted := getUntrustedString() - db.Select(nil, untrusted) - db.Get(nil, untrusted) - db.MustExec(untrusted) - db.Queryx(untrusted) - db.NamedExec(untrusted, nil) - db.NamedQuery(untrusted, nil) + db.Select(nil, untrusted) // $ querystring=untrusted + db.Get(nil, untrusted) // $ querystring=untrusted + db.MustExec(untrusted) // $ querystring=untrusted + db.Queryx(untrusted) // $ querystring=untrusted + db.NamedExec(untrusted, nil) // $ querystring=untrusted + db.NamedQuery(untrusted, nil) // $ querystring=untrusted tx := sqlx.Tx{} - tx.Select(nil, untrusted) - tx.Get(nil, untrusted) - tx.MustExec(untrusted) - tx.Queryx(untrusted) - tx.NamedExec(untrusted, nil) - tx.NamedQuery(untrusted, nil) + tx.Select(nil, untrusted) // $ querystring=untrusted + tx.Get(nil, untrusted) // $ querystring=untrusted + tx.MustExec(untrusted) // $ querystring=untrusted + tx.Queryx(untrusted) // $ querystring=untrusted + tx.NamedExec(untrusted, nil) // $ querystring=untrusted + tx.NamedQuery(untrusted, nil) // $ querystring=untrusted } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql deleted file mode 100644 index 7b56fd974419..000000000000 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql +++ /dev/null @@ -1,4 +0,0 @@ -import go - -from SQL::QueryString qs -select qs From ba310417a8ce94daa2240219335003518acdca33 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 15:24:56 +0100 Subject: [PATCH 05/16] Convert Gorm sql-injection sinks to MaD --- go/ql/lib/ext/gorm.io.gorm.model.yml | 25 ++++++++ go/ql/lib/semmle/go/frameworks/SQL.qll | 15 ----- .../frameworks/SQL/Gorm/QueryString.expected | 3 + .../go/frameworks/SQL/Gorm/QueryString.ql | 60 +++++++++++++++++++ .../go/frameworks/SQL/Gorm/gorm.expected | 25 -------- .../semmle/go/frameworks/SQL/Gorm/gorm.go | 51 ++++++++-------- .../semmle/go/frameworks/SQL/Gorm/gorm.ql | 5 -- 7 files changed, 113 insertions(+), 71 deletions(-) create mode 100644 go/ql/lib/ext/gorm.io.gorm.model.yml create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.expected create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.ql delete mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected delete mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql diff --git a/go/ql/lib/ext/gorm.io.gorm.model.yml b/go/ql/lib/ext/gorm.io.gorm.model.yml new file mode 100644 index 000000000000..bfcf1fa66a76 --- /dev/null +++ b/go/ql/lib/ext/gorm.io.gorm.model.yml @@ -0,0 +1,25 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: packageGrouping + data: + - ["gorm", "gorm.io/gorm"] + - ["gorm", "github.com/jinzhu/gorm"] + - ["gorm", "github.com/go-gorm/gorm"] + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["group:gorm", "DB", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Order", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Not", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Or", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Select", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Table", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Group", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Joins", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Distinct", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:gorm", "DB", True, "Pluck", "", "", "Argument[0]", "sql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index 304c7f82d871..72003985d77f 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -150,21 +150,6 @@ module SQL { } } } - - /** A model for sinks of GORM. */ - private class GormSink extends SQL::QueryString::Range { - GormSink() { - exists(Method meth, string package, string name | - meth.hasQualifiedName(package, "DB", name) and - this = meth.getACall().getSyntacticArgument(0) and - package = Gorm::packagePath() and - name in [ - "Where", "Raw", "Order", "Not", "Or", "Select", "Table", "Group", "Having", "Joins", - "Exec", "Distinct", "Pluck" - ] - ) - } - } } /** diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.expected new file mode 100644 index 000000000000..db33d6d2504a --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.expected @@ -0,0 +1,3 @@ +testFailures +invalidModelRow +failures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.ql new file mode 100644 index 000000000000..eeb43a82fadd --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/QueryString.ql @@ -0,0 +1,60 @@ +import go +import semmle.go.dataflow.ExternalFlow +import ModelValidation +import TestUtilities.InlineExpectationsTest + +module SqlTest implements TestSig { + string getARelevantTag() { result = "query" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "query" and + exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() | + q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = q.toString() and + value = qs.toString() + ) + } +} + +module QueryString implements TestSig { + string getARelevantTag() { result = "querystring" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "querystring" and + element = "" and + exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) | + qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + value = qs.toString() + ) + } +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit } + + predicate isSink(DataFlow::Node n) { + n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument() + } +} + +module Flow = TaintTracking::Global; + +module TaintFlow implements TestSig { + string getARelevantTag() { result = "flowfrom" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "flowfrom" and + element = "" and + exists(DataFlow::Node fromNode, DataFlow::Node toNode | + toNode + .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + Flow::flow(fromNode, toNode) and + value = fromNode.asExpr().(StringLit).getValue() + ) + } +} + +import MakeTest> diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected deleted file mode 100644 index ca70ed07c33d..000000000000 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected +++ /dev/null @@ -1,25 +0,0 @@ -| gorm.go:20:12:20:20 | untrusted | github.com/jinzhu/gorm | DB | Where | -| gorm.go:21:10:21:18 | untrusted | github.com/jinzhu/gorm | DB | Raw | -| gorm.go:22:10:22:18 | untrusted | github.com/jinzhu/gorm | DB | Not | -| gorm.go:23:12:23:20 | untrusted | github.com/jinzhu/gorm | DB | Order | -| gorm.go:24:9:24:17 | untrusted | github.com/jinzhu/gorm | DB | Or | -| gorm.go:25:13:25:21 | untrusted | github.com/jinzhu/gorm | DB | Select | -| gorm.go:26:12:26:20 | untrusted | github.com/jinzhu/gorm | DB | Table | -| gorm.go:27:12:27:20 | untrusted | github.com/jinzhu/gorm | DB | Group | -| gorm.go:28:13:28:21 | untrusted | github.com/jinzhu/gorm | DB | Having | -| gorm.go:29:12:29:20 | untrusted | github.com/jinzhu/gorm | DB | Joins | -| gorm.go:30:11:30:19 | untrusted | github.com/jinzhu/gorm | DB | Exec | -| gorm.go:31:12:31:20 | untrusted | github.com/jinzhu/gorm | DB | Pluck | -| gorm.go:34:12:34:20 | untrusted | gorm.io/gorm | DB | Where | -| gorm.go:35:10:35:18 | untrusted | gorm.io/gorm | DB | Raw | -| gorm.go:36:10:36:18 | untrusted | gorm.io/gorm | DB | Not | -| gorm.go:37:12:37:20 | untrusted | gorm.io/gorm | DB | Order | -| gorm.go:38:9:38:17 | untrusted | gorm.io/gorm | DB | Or | -| gorm.go:39:13:39:21 | untrusted | gorm.io/gorm | DB | Select | -| gorm.go:40:12:40:20 | untrusted | gorm.io/gorm | DB | Table | -| gorm.go:41:12:41:20 | untrusted | gorm.io/gorm | DB | Group | -| gorm.go:42:13:42:21 | untrusted | gorm.io/gorm | DB | Having | -| gorm.go:43:12:43:20 | untrusted | gorm.io/gorm | DB | Joins | -| gorm.go:44:11:44:19 | untrusted | gorm.io/gorm | DB | Exec | -| gorm.go:45:15:45:23 | untrusted | gorm.io/gorm | DB | Distinct | -| gorm.go:46:12:46:20 | untrusted | gorm.io/gorm | DB | Pluck | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go index bee8edbf7af8..2d736e2407c9 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go @@ -13,36 +13,35 @@ func getUntrustedString() string { } func main() { - untrusted := getUntrustedString() db1 := gorm1.DB{} - db1.Where(untrusted) - db1.Raw(untrusted) - db1.Not(untrusted) - db1.Order(untrusted) - db1.Or(untrusted) - db1.Select(untrusted) - db1.Table(untrusted) - db1.Group(untrusted) - db1.Having(untrusted) - db1.Joins(untrusted) - db1.Exec(untrusted) - db1.Pluck(untrusted, nil) + db1.Where(untrusted) // $ querystring=untrusted + db1.Raw(untrusted) // $ querystring=untrusted + db1.Not(untrusted) // $ querystring=untrusted + db1.Order(untrusted) // $ querystring=untrusted + db1.Or(untrusted) // $ querystring=untrusted + db1.Select(untrusted) // $ querystring=untrusted + db1.Table(untrusted) // $ querystring=untrusted + db1.Group(untrusted) // $ querystring=untrusted + db1.Having(untrusted) // $ querystring=untrusted + db1.Joins(untrusted) // $ querystring=untrusted + db1.Exec(untrusted) // $ querystring=untrusted + db1.Pluck(untrusted, nil) // $ querystring=untrusted db2 := gorm2.DB{} - db2.Where(untrusted) - db2.Raw(untrusted) - db2.Not(untrusted) - db2.Order(untrusted) - db2.Or(untrusted) - db2.Select(untrusted) - db2.Table(untrusted) - db2.Group(untrusted) - db2.Having(untrusted) - db2.Joins(untrusted) - db2.Exec(untrusted) - db2.Distinct(untrusted) - db2.Pluck(untrusted, nil) + db2.Where(untrusted) // $ querystring=untrusted + db2.Raw(untrusted) // $ querystring=untrusted + db2.Not(untrusted) // $ querystring=untrusted + db2.Order(untrusted) // $ querystring=untrusted + db2.Or(untrusted) // $ querystring=untrusted + db2.Select(untrusted) // $ querystring=untrusted + db2.Table(untrusted) // $ querystring=untrusted + db2.Group(untrusted) // $ querystring=untrusted + db2.Having(untrusted) // $ querystring=untrusted + db2.Joins(untrusted) // $ querystring=untrusted + db2.Exec(untrusted) // $ querystring=untrusted + db2.Distinct(untrusted) // $ querystring=untrusted + db2.Pluck(untrusted, nil) // $ querystring=untrusted } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql deleted file mode 100644 index e08b506deaf1..000000000000 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql +++ /dev/null @@ -1,5 +0,0 @@ -import go - -from SQL::QueryString qs, Method meth, string a, string b, string c -where meth.hasQualifiedName(a, b, c) and qs = meth.getACall().getSyntacticArgument(0) -select qs, a, b, c From 3b2b7d7d1c3ead1ba09002795cac87c90e3e4c99 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 15:38:07 +0100 Subject: [PATCH 06/16] Convert Xorm sql-injection sinks to MaD --- go/ql/lib/ext/xorm.io.xorm.model.yml | 53 ++++++++++++++++++++++++++ go/ql/lib/semmle/go/frameworks/SQL.qll | 22 ----------- 2 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 go/ql/lib/ext/xorm.io.xorm.model.yml diff --git a/go/ql/lib/ext/xorm.io.xorm.model.yml b/go/ql/lib/ext/xorm.io.xorm.model.yml new file mode 100644 index 000000000000..fd73b7c9a669 --- /dev/null +++ b/go/ql/lib/ext/xorm.io.xorm.model.yml @@ -0,0 +1,53 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: packageGrouping + data: + - ["xorm", "xorm.io/xorm"] + - ["xorm", "github.com/go-xorm/xorm"] + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["group:xorm", "Engine", True, "Alias", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "And", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "GroupBy", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "In", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Join", "", "", "Argument[0..2]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "NotIn", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Or", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "QueryString", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "QueryInterface", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Select", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "SetExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "SQL", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Sum", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Sums", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "SumInt", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "SumsInt", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:xorm", "Engine", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Alias", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "And", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "GroupBy", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "In", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Join", "", "", "Argument[0..2]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "NotIn", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Or", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "QueryString", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "QueryInterface", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Select", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "SetExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "SQL", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Sum", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Sums", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "SumInt", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "SumsInt", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:xorm", "Session", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index 72003985d77f..f4367da7854c 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -168,28 +168,6 @@ module Gorm { module Xorm { /** Gets the package name for Xorm. */ string packagePath() { result = package(["xorm.io/xorm", "github.com/go-xorm/xorm"], "") } - - /** A model for sinks of XORM. */ - private class XormSink extends SQL::QueryString::Range { - XormSink() { - exists(Method meth, string type, string name, int n | - meth.hasQualifiedName(Xorm::packagePath(), type, name) and - this = meth.getACall().getSyntacticArgument(n) and - type = ["Engine", "Session"] - | - name = - [ - "Query", "Exec", "QueryString", "QueryInterface", "SQL", "Where", "And", "Or", "Alias", - "NotIn", "In", "Select", "SetExpr", "OrderBy", "Having", "GroupBy" - ] and - n = 0 - or - name = ["SumInt", "Sum", "Sums", "SumsInt"] and n = 1 - or - name = "Join" and n = [0, 1, 2] - ) - } - } } /** From 3eb5b2669b2f5a8f020288184932c2e72379d2ad Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 16:10:11 +0100 Subject: [PATCH 07/16] Convert Bun sql-injection sinks to MaD --- .../lib/ext/github.com.uptrace.bun.model.yml | 68 +++++++++++++++++++ go/ql/lib/semmle/go/frameworks/SQL.qll | 43 +----------- .../frameworks/SQL/bun/QueryString.expected | 3 + .../go/frameworks/SQL/bun/QueryString.ql | 60 ++++++++++++++++ .../semmle/go/frameworks/SQL/bun/bun.go | 48 ++++++------- 5 files changed, 158 insertions(+), 64 deletions(-) create mode 100644 go/ql/lib/ext/github.com.uptrace.bun.model.yml create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.expected create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.ql diff --git a/go/ql/lib/ext/github.com.uptrace.bun.model.yml b/go/ql/lib/ext/github.com.uptrace.bun.model.yml new file mode 100644 index 000000000000..eb060b4f9ccb --- /dev/null +++ b/go/ql/lib/ext/github.com.uptrace.bun.model.yml @@ -0,0 +1,68 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["github.com/uptrace/bun", "", True, "NewRawQuery", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "AddColumnQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "AddColumnQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "AddColumnQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "NewRaw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "Conn", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "CreateIndexQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "CreateIndexQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "CreateIndexQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "CreateIndexQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "CreateIndexQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "CreateTableQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "CreateTableQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "CreateTableQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "NewRaw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DB", True, "Raw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DeleteQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DeleteQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DeleteQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DropColumnQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DropColumnQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DropColumnQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DropTableQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "DropTableQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "InsertQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "InsertQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "InsertQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "InsertQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "InsertQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "MergeQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "MergeQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "RawQuery", True, "NewRaw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "ColumnExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "DistinctOn", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "For", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "GroupExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "Having", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "OrderExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "SelectQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "TruncateTableQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "UpdateQuery", True, "ModelTableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "UpdateQuery", True, "TableExpr", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "UpdateQuery", True, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + - ["github.com/uptrace/bun", "UpdateQuery", True, "WhereOr", "", "", "Argument[0]", "sql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index f4367da7854c..c2a550602e1c 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -171,45 +171,8 @@ module Xorm { } /** + * DEPRECATED + * * Provides classes for working with the [Bun](https://bun.uptrace.dev/) package. */ -module Bun { - /** Gets the package name for Bun package. */ - private string packagePath() { result = package("github.com/uptrace/bun", "") } - - /** A model for sinks of Bun. */ - private class BunSink extends SQL::QueryString::Range { - BunSink() { - exists(Function f, string m, int arg | this = f.getACall().getArgument(arg) | - f.hasQualifiedName(packagePath(), m) and - m = "NewRawQuery" and - arg = 1 - ) - or - exists(Method f, string tp, string m, int arg | this = f.getACall().getArgument(arg) | - f.hasQualifiedName(packagePath(), tp, m) and - ( - tp = ["DB", "Conn"] and - m = ["ExecContext", "PrepareContext", "QueryContext", "QueryRowContext"] and - arg = 1 - or - tp = ["DB", "Conn"] and - m = ["Exec", "NewRaw", "Prepare", "Query", "QueryRow", "Raw"] and - arg = 0 - or - tp.matches("%Query") and - m = - [ - "ColumnExpr", "DistinctOn", "For", "GroupExpr", "Having", "ModelTableExpr", - "OrderExpr", "TableExpr", "Where", "WhereOr" - ] and - arg = 0 - or - tp = "RawQuery" and - m = "NewRaw" and - arg = 0 - ) - ) - } - } -} +deprecated module Bun { } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.expected new file mode 100644 index 000000000000..105b7026d0c4 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.expected @@ -0,0 +1,3 @@ +failures +invalidModelRow +testFailures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.ql new file mode 100644 index 000000000000..eeb43a82fadd --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/QueryString.ql @@ -0,0 +1,60 @@ +import go +import semmle.go.dataflow.ExternalFlow +import ModelValidation +import TestUtilities.InlineExpectationsTest + +module SqlTest implements TestSig { + string getARelevantTag() { result = "query" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "query" and + exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() | + q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = q.toString() and + value = qs.toString() + ) + } +} + +module QueryString implements TestSig { + string getARelevantTag() { result = "querystring" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "querystring" and + element = "" and + exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) | + qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + value = qs.toString() + ) + } +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit } + + predicate isSink(DataFlow::Node n) { + n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument() + } +} + +module Flow = TaintTracking::Global; + +module TaintFlow implements TestSig { + string getARelevantTag() { result = "flowfrom" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "flowfrom" and + element = "" and + exists(DataFlow::Node fromNode, DataFlow::Node toNode | + toNode + .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + Flow::flow(fromNode, toNode) and + value = fromNode.asExpr().(StringLit).getValue() + ) + } +} + +import MakeTest> diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go index 8ce4e5b0826b..44a2e2c2fce4 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go @@ -22,28 +22,28 @@ func main() { panic(err) } db := bun.NewDB(sqlite, sqlitedialect.New()) - bun.NewRawQuery(db, untrusted) - - db.ExecContext(ctx, untrusted) - db.PrepareContext(ctx, untrusted) - db.QueryContext(ctx, untrusted) - db.QueryRowContext(ctx, untrusted) - - db.Exec(untrusted) - db.NewRaw(untrusted) - db.Prepare(untrusted) - db.Query(untrusted) - db.QueryRow(untrusted) - db.Raw(untrusted) - - db.NewSelect().ColumnExpr(untrusted) - db.NewSelect().DistinctOn(untrusted) - db.NewSelect().For(untrusted) - db.NewSelect().GroupExpr(untrusted) - db.NewSelect().Having(untrusted) - db.NewSelect().ModelTableExpr(untrusted) - db.NewSelect().OrderExpr(untrusted) - db.NewSelect().TableExpr(untrusted) - db.NewSelect().Where(untrusted) - db.NewSelect().WhereOr(untrusted) + bun.NewRawQuery(db, untrusted) // $ querystring=untrusted + + db.ExecContext(ctx, untrusted) // $ querystring=untrusted + db.PrepareContext(ctx, untrusted) // $ querystring=untrusted + db.QueryContext(ctx, untrusted) // $ querystring=untrusted + db.QueryRowContext(ctx, untrusted) // $ querystring=untrusted + + db.Exec(untrusted) // $ querystring=untrusted + db.NewRaw(untrusted) // $ querystring=untrusted + db.Prepare(untrusted) // $ querystring=untrusted + db.Query(untrusted) // $ querystring=untrusted + db.QueryRow(untrusted) // $ querystring=untrusted + db.Raw(untrusted) // $ querystring=untrusted + + db.NewSelect().ColumnExpr(untrusted) // $ querystring=untrusted + db.NewSelect().DistinctOn(untrusted) // $ querystring=untrusted + db.NewSelect().For(untrusted) // $ querystring=untrusted + db.NewSelect().GroupExpr(untrusted) // $ querystring=untrusted + db.NewSelect().Having(untrusted) // $ querystring=untrusted + db.NewSelect().ModelTableExpr(untrusted) // $ querystring=untrusted + db.NewSelect().OrderExpr(untrusted) // $ querystring=untrusted + db.NewSelect().TableExpr(untrusted) // $ querystring=untrusted + db.NewSelect().Where(untrusted) // $ querystring=untrusted + db.NewSelect().WhereOr(untrusted) // $ querystring=untrusted } From e1bdc7f5a703c5d0061b9bc925828fc8dff092b5 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 16:34:50 +0100 Subject: [PATCH 08/16] Update Beego orm tests --- .../frameworks/BeegoOrm/QueryString.expected | 3 + .../go/frameworks/BeegoOrm/QueryString.ql | 60 ++++++ .../frameworks/BeegoOrm/SqlInjection.expected | 197 +++++++++--------- .../go/frameworks/BeegoOrm/StoredXss.expected | 190 ++++++++--------- .../semmle/go/frameworks/BeegoOrm/test.go | 73 +++---- 5 files changed, 295 insertions(+), 228 deletions(-) create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/QueryString.expected create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/QueryString.ql diff --git a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/QueryString.expected new file mode 100644 index 000000000000..105b7026d0c4 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/QueryString.expected @@ -0,0 +1,3 @@ +failures +invalidModelRow +testFailures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/QueryString.ql new file mode 100644 index 000000000000..eeb43a82fadd --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/QueryString.ql @@ -0,0 +1,60 @@ +import go +import semmle.go.dataflow.ExternalFlow +import ModelValidation +import TestUtilities.InlineExpectationsTest + +module SqlTest implements TestSig { + string getARelevantTag() { result = "query" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "query" and + exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() | + q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = q.toString() and + value = qs.toString() + ) + } +} + +module QueryString implements TestSig { + string getARelevantTag() { result = "querystring" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "querystring" and + element = "" and + exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) | + qs.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + value = qs.toString() + ) + } +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit } + + predicate isSink(DataFlow::Node n) { + n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument() + } +} + +module Flow = TaintTracking::Global; + +module TaintFlow implements TestSig { + string getARelevantTag() { result = "flowfrom" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "flowfrom" and + element = "" and + exists(DataFlow::Node fromNode, DataFlow::Node toNode | + toNode + .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + Flow::flow(fromNode, toNode) and + value = fromNode.asExpr().(StringLit).getValue() + ) + } +} + +import MakeTest> diff --git a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected index efd77de851c5..6cc1e09486b7 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected @@ -1,107 +1,108 @@ #select -| test.go:12:11:12:19 | untrusted | test.go:10:15:10:41 | call to UserAgent | test.go:12:11:12:19 | untrusted | This query depends on a $@. | test.go:10:15:10:41 | call to UserAgent | user-provided value | -| test.go:13:23:13:31 | untrusted | test.go:10:15:10:41 | call to UserAgent | test.go:13:23:13:31 | untrusted | This query depends on a $@. | test.go:10:15:10:41 | call to UserAgent | user-provided value | -| test.go:14:14:14:22 | untrusted | test.go:10:15:10:41 | call to UserAgent | test.go:14:14:14:22 | untrusted | This query depends on a $@. | test.go:10:15:10:41 | call to UserAgent | user-provided value | -| test.go:15:26:15:34 | untrusted | test.go:10:15:10:41 | call to UserAgent | test.go:15:26:15:34 | untrusted | This query depends on a $@. | test.go:10:15:10:41 | call to UserAgent | user-provided value | -| test.go:16:12:16:20 | untrusted | test.go:10:15:10:41 | call to UserAgent | test.go:16:12:16:20 | untrusted | This query depends on a $@. | test.go:10:15:10:41 | call to UserAgent | user-provided value | -| test.go:17:24:17:32 | untrusted | test.go:10:15:10:41 | call to UserAgent | test.go:17:24:17:32 | untrusted | This query depends on a $@. | test.go:10:15:10:41 | call to UserAgent | user-provided value | -| test.go:18:15:18:23 | untrusted | test.go:10:15:10:41 | call to UserAgent | test.go:18:15:18:23 | untrusted | This query depends on a $@. | test.go:10:15:10:41 | call to UserAgent | user-provided value | -| test.go:19:27:19:35 | untrusted | test.go:10:15:10:41 | call to UserAgent | test.go:19:27:19:35 | untrusted | This query depends on a $@. | test.go:10:15:10:41 | call to UserAgent | user-provided value | -| test.go:26:12:26:20 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:26:12:26:20 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:27:10:27:18 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:27:10:27:18 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:28:15:28:23 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:28:15:28:23 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:29:14:29:22 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:29:14:29:22 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:30:15:30:23 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:30:15:30:23 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:31:8:31:16 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:31:8:31:16 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:32:11:32:19 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:32:11:32:19 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:33:9:33:17 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:33:9:33:17 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:34:8:34:16 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:34:8:34:16 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:35:8:35:16 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:35:8:35:16 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:36:13:36:21 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:36:13:36:21 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:37:13:37:21 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:37:13:37:21 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:38:12:38:20 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:38:12:38:20 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:39:12:39:20 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:39:12:39:20 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:40:9:40:17 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:40:9:40:17 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:41:12:41:20 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:41:12:41:20 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:42:16:42:24 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:42:16:42:24 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:42:27:42:35 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:42:27:42:35 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:43:12:43:20 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:43:12:43:20 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:44:14:44:22 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:44:14:44:22 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:44:25:44:33 | untrusted | test.go:24:15:24:41 | call to UserAgent | test.go:44:25:44:33 | untrusted | This query depends on a $@. | test.go:24:15:24:41 | call to UserAgent | user-provided value | -| test.go:49:12:49:20 | untrusted | test.go:48:15:48:41 | call to UserAgent | test.go:49:12:49:20 | untrusted | This query depends on a $@. | test.go:48:15:48:41 | call to UserAgent | user-provided value | -| test.go:56:31:56:39 | untrusted | test.go:54:15:54:41 | call to UserAgent | test.go:56:31:56:39 | untrusted | This query depends on a $@. | test.go:54:15:54:41 | call to UserAgent | user-provided value | -| test.go:62:19:62:27 | untrusted | test.go:60:15:60:41 | call to UserAgent | test.go:62:19:62:27 | untrusted | This query depends on a $@. | test.go:60:15:60:41 | call to UserAgent | user-provided value | +| test.go:13:11:13:19 | untrusted | test.go:11:15:11:41 | call to UserAgent | test.go:13:11:13:19 | untrusted | This query depends on a $@. | test.go:11:15:11:41 | call to UserAgent | user-provided value | +| test.go:14:23:14:31 | untrusted | test.go:11:15:11:41 | call to UserAgent | test.go:14:23:14:31 | untrusted | This query depends on a $@. | test.go:11:15:11:41 | call to UserAgent | user-provided value | +| test.go:15:14:15:22 | untrusted | test.go:11:15:11:41 | call to UserAgent | test.go:15:14:15:22 | untrusted | This query depends on a $@. | test.go:11:15:11:41 | call to UserAgent | user-provided value | +| test.go:16:26:16:34 | untrusted | test.go:11:15:11:41 | call to UserAgent | test.go:16:26:16:34 | untrusted | This query depends on a $@. | test.go:11:15:11:41 | call to UserAgent | user-provided value | +| test.go:17:12:17:20 | untrusted | test.go:11:15:11:41 | call to UserAgent | test.go:17:12:17:20 | untrusted | This query depends on a $@. | test.go:11:15:11:41 | call to UserAgent | user-provided value | +| test.go:18:24:18:32 | untrusted | test.go:11:15:11:41 | call to UserAgent | test.go:18:24:18:32 | untrusted | This query depends on a $@. | test.go:11:15:11:41 | call to UserAgent | user-provided value | +| test.go:19:15:19:23 | untrusted | test.go:11:15:11:41 | call to UserAgent | test.go:19:15:19:23 | untrusted | This query depends on a $@. | test.go:11:15:11:41 | call to UserAgent | user-provided value | +| test.go:20:27:20:35 | untrusted | test.go:11:15:11:41 | call to UserAgent | test.go:20:27:20:35 | untrusted | This query depends on a $@. | test.go:11:15:11:41 | call to UserAgent | user-provided value | +| test.go:28:12:28:20 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:28:12:28:20 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:29:10:29:18 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:29:10:29:18 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:30:15:30:23 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:30:15:30:23 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:31:14:31:22 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:31:14:31:22 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:32:15:32:23 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:32:15:32:23 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:33:8:33:16 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:33:8:33:16 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:34:11:34:19 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:34:11:34:19 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:35:9:35:17 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:35:9:35:17 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:36:8:36:16 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:36:8:36:16 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:37:8:37:16 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:37:8:37:16 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:38:13:38:21 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:38:13:38:21 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:39:13:39:21 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:39:13:39:21 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:40:12:40:20 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:40:12:40:20 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:41:12:41:20 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:41:12:41:20 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:42:9:42:17 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:42:9:42:17 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:43:12:43:20 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:43:12:43:20 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:44:16:44:24 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:44:16:44:24 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:44:27:44:36 | untrusted2 | test.go:26:16:26:42 | call to UserAgent | test.go:44:27:44:36 | untrusted2 | This query depends on a $@. | test.go:26:16:26:42 | call to UserAgent | user-provided value | +| test.go:45:12:45:20 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:45:12:45:20 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:46:14:46:22 | untrusted | test.go:25:15:25:41 | call to UserAgent | test.go:46:14:46:22 | untrusted | This query depends on a $@. | test.go:25:15:25:41 | call to UserAgent | user-provided value | +| test.go:46:25:46:34 | untrusted2 | test.go:26:16:26:42 | call to UserAgent | test.go:46:25:46:34 | untrusted2 | This query depends on a $@. | test.go:26:16:26:42 | call to UserAgent | user-provided value | +| test.go:52:12:52:20 | untrusted | test.go:50:15:50:41 | call to UserAgent | test.go:52:12:52:20 | untrusted | This query depends on a $@. | test.go:50:15:50:41 | call to UserAgent | user-provided value | +| test.go:59:31:59:39 | untrusted | test.go:57:15:57:41 | call to UserAgent | test.go:59:31:59:39 | untrusted | This query depends on a $@. | test.go:57:15:57:41 | call to UserAgent | user-provided value | +| test.go:65:19:65:27 | untrusted | test.go:63:15:63:41 | call to UserAgent | test.go:65:19:65:27 | untrusted | This query depends on a $@. | test.go:63:15:63:41 | call to UserAgent | user-provided value | edges -| test.go:10:15:10:41 | call to UserAgent | test.go:12:11:12:19 | untrusted | provenance | Src:MaD:1 | -| test.go:10:15:10:41 | call to UserAgent | test.go:13:23:13:31 | untrusted | provenance | Src:MaD:1 | -| test.go:10:15:10:41 | call to UserAgent | test.go:14:14:14:22 | untrusted | provenance | Src:MaD:1 | -| test.go:10:15:10:41 | call to UserAgent | test.go:15:26:15:34 | untrusted | provenance | Src:MaD:1 | -| test.go:10:15:10:41 | call to UserAgent | test.go:16:12:16:20 | untrusted | provenance | Src:MaD:1 | -| test.go:10:15:10:41 | call to UserAgent | test.go:17:24:17:32 | untrusted | provenance | Src:MaD:1 | -| test.go:10:15:10:41 | call to UserAgent | test.go:18:15:18:23 | untrusted | provenance | Src:MaD:1 | -| test.go:10:15:10:41 | call to UserAgent | test.go:19:27:19:35 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:26:12:26:20 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:27:10:27:18 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:28:15:28:23 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:29:14:29:22 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:30:15:30:23 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:31:8:31:16 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:32:11:32:19 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:33:9:33:17 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:34:8:34:16 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:35:8:35:16 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:36:13:36:21 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:37:13:37:21 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:38:12:38:20 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:39:12:39:20 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:40:9:40:17 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:41:12:41:20 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:42:16:42:24 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:42:27:42:35 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:43:12:43:20 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:44:14:44:22 | untrusted | provenance | Src:MaD:1 | -| test.go:24:15:24:41 | call to UserAgent | test.go:44:25:44:33 | untrusted | provenance | Src:MaD:1 | -| test.go:48:15:48:41 | call to UserAgent | test.go:49:12:49:20 | untrusted | provenance | Src:MaD:1 | -| test.go:54:15:54:41 | call to UserAgent | test.go:56:31:56:39 | untrusted | provenance | Src:MaD:1 | -| test.go:60:15:60:41 | call to UserAgent | test.go:62:19:62:27 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:13:11:13:19 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:14:23:14:31 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:15:14:15:22 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:16:26:16:34 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:17:12:17:20 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:18:24:18:32 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:19:15:19:23 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:20:27:20:35 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:28:12:28:20 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:29:10:29:18 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:30:15:30:23 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:31:14:31:22 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:32:15:32:23 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:33:8:33:16 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:34:11:34:19 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:35:9:35:17 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:36:8:36:16 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:37:8:37:16 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:38:13:38:21 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:39:13:39:21 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:40:12:40:20 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:41:12:41:20 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:42:9:42:17 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:43:12:43:20 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:44:16:44:24 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:45:12:45:20 | untrusted | provenance | Src:MaD:1 | +| test.go:25:15:25:41 | call to UserAgent | test.go:46:14:46:22 | untrusted | provenance | Src:MaD:1 | +| test.go:26:16:26:42 | call to UserAgent | test.go:44:27:44:36 | untrusted2 | provenance | Src:MaD:1 | +| test.go:26:16:26:42 | call to UserAgent | test.go:46:25:46:34 | untrusted2 | provenance | Src:MaD:1 | +| test.go:50:15:50:41 | call to UserAgent | test.go:52:12:52:20 | untrusted | provenance | Src:MaD:1 | +| test.go:57:15:57:41 | call to UserAgent | test.go:59:31:59:39 | untrusted | provenance | Src:MaD:1 | +| test.go:63:15:63:41 | call to UserAgent | test.go:65:19:65:27 | untrusted | provenance | Src:MaD:1 | models | 1 | Source: net/http; Request; true; UserAgent; ; ; ReturnValue; remote; manual | nodes -| test.go:10:15:10:41 | call to UserAgent | semmle.label | call to UserAgent | -| test.go:12:11:12:19 | untrusted | semmle.label | untrusted | -| test.go:13:23:13:31 | untrusted | semmle.label | untrusted | -| test.go:14:14:14:22 | untrusted | semmle.label | untrusted | -| test.go:15:26:15:34 | untrusted | semmle.label | untrusted | -| test.go:16:12:16:20 | untrusted | semmle.label | untrusted | -| test.go:17:24:17:32 | untrusted | semmle.label | untrusted | -| test.go:18:15:18:23 | untrusted | semmle.label | untrusted | -| test.go:19:27:19:35 | untrusted | semmle.label | untrusted | -| test.go:24:15:24:41 | call to UserAgent | semmle.label | call to UserAgent | -| test.go:26:12:26:20 | untrusted | semmle.label | untrusted | -| test.go:27:10:27:18 | untrusted | semmle.label | untrusted | -| test.go:28:15:28:23 | untrusted | semmle.label | untrusted | -| test.go:29:14:29:22 | untrusted | semmle.label | untrusted | +| test.go:11:15:11:41 | call to UserAgent | semmle.label | call to UserAgent | +| test.go:13:11:13:19 | untrusted | semmle.label | untrusted | +| test.go:14:23:14:31 | untrusted | semmle.label | untrusted | +| test.go:15:14:15:22 | untrusted | semmle.label | untrusted | +| test.go:16:26:16:34 | untrusted | semmle.label | untrusted | +| test.go:17:12:17:20 | untrusted | semmle.label | untrusted | +| test.go:18:24:18:32 | untrusted | semmle.label | untrusted | +| test.go:19:15:19:23 | untrusted | semmle.label | untrusted | +| test.go:20:27:20:35 | untrusted | semmle.label | untrusted | +| test.go:25:15:25:41 | call to UserAgent | semmle.label | call to UserAgent | +| test.go:26:16:26:42 | call to UserAgent | semmle.label | call to UserAgent | +| test.go:28:12:28:20 | untrusted | semmle.label | untrusted | +| test.go:29:10:29:18 | untrusted | semmle.label | untrusted | | test.go:30:15:30:23 | untrusted | semmle.label | untrusted | -| test.go:31:8:31:16 | untrusted | semmle.label | untrusted | -| test.go:32:11:32:19 | untrusted | semmle.label | untrusted | -| test.go:33:9:33:17 | untrusted | semmle.label | untrusted | -| test.go:34:8:34:16 | untrusted | semmle.label | untrusted | -| test.go:35:8:35:16 | untrusted | semmle.label | untrusted | -| test.go:36:13:36:21 | untrusted | semmle.label | untrusted | -| test.go:37:13:37:21 | untrusted | semmle.label | untrusted | -| test.go:38:12:38:20 | untrusted | semmle.label | untrusted | -| test.go:39:12:39:20 | untrusted | semmle.label | untrusted | -| test.go:40:9:40:17 | untrusted | semmle.label | untrusted | +| test.go:31:14:31:22 | untrusted | semmle.label | untrusted | +| test.go:32:15:32:23 | untrusted | semmle.label | untrusted | +| test.go:33:8:33:16 | untrusted | semmle.label | untrusted | +| test.go:34:11:34:19 | untrusted | semmle.label | untrusted | +| test.go:35:9:35:17 | untrusted | semmle.label | untrusted | +| test.go:36:8:36:16 | untrusted | semmle.label | untrusted | +| test.go:37:8:37:16 | untrusted | semmle.label | untrusted | +| test.go:38:13:38:21 | untrusted | semmle.label | untrusted | +| test.go:39:13:39:21 | untrusted | semmle.label | untrusted | +| test.go:40:12:40:20 | untrusted | semmle.label | untrusted | | test.go:41:12:41:20 | untrusted | semmle.label | untrusted | -| test.go:42:16:42:24 | untrusted | semmle.label | untrusted | -| test.go:42:27:42:35 | untrusted | semmle.label | untrusted | +| test.go:42:9:42:17 | untrusted | semmle.label | untrusted | | test.go:43:12:43:20 | untrusted | semmle.label | untrusted | -| test.go:44:14:44:22 | untrusted | semmle.label | untrusted | -| test.go:44:25:44:33 | untrusted | semmle.label | untrusted | -| test.go:48:15:48:41 | call to UserAgent | semmle.label | call to UserAgent | -| test.go:49:12:49:20 | untrusted | semmle.label | untrusted | -| test.go:54:15:54:41 | call to UserAgent | semmle.label | call to UserAgent | -| test.go:56:31:56:39 | untrusted | semmle.label | untrusted | -| test.go:60:15:60:41 | call to UserAgent | semmle.label | call to UserAgent | -| test.go:62:19:62:27 | untrusted | semmle.label | untrusted | +| test.go:44:16:44:24 | untrusted | semmle.label | untrusted | +| test.go:44:27:44:36 | untrusted2 | semmle.label | untrusted2 | +| test.go:45:12:45:20 | untrusted | semmle.label | untrusted | +| test.go:46:14:46:22 | untrusted | semmle.label | untrusted | +| test.go:46:25:46:34 | untrusted2 | semmle.label | untrusted2 | +| test.go:50:15:50:41 | call to UserAgent | semmle.label | call to UserAgent | +| test.go:52:12:52:20 | untrusted | semmle.label | untrusted | +| test.go:57:15:57:41 | call to UserAgent | semmle.label | call to UserAgent | +| test.go:59:31:59:39 | untrusted | semmle.label | untrusted | +| test.go:63:15:63:41 | call to UserAgent | semmle.label | call to UserAgent | +| test.go:65:19:65:27 | untrusted | semmle.label | untrusted | subpaths diff --git a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/StoredXss.expected b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/StoredXss.expected index 363a42b91d7d..861e3e97ed14 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/StoredXss.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/StoredXss.expected @@ -1,111 +1,111 @@ edges -| test.go:77:13:77:16 | &... | test.go:78:13:78:29 | type conversion | provenance | | -| test.go:77:13:77:16 | &... | test.go:79:13:79:43 | type conversion | provenance | | -| test.go:82:22:82:26 | &... | test.go:83:13:83:30 | type conversion | provenance | | -| test.go:86:21:86:25 | &... | test.go:87:13:87:30 | type conversion | provenance | | -| test.go:92:20:92:36 | call to Value | test.go:92:13:92:37 | type conversion | provenance | | -| test.go:93:20:93:39 | call to RawValue | test.go:93:13:93:49 | type conversion | provenance | | -| test.go:94:20:94:37 | call to String | test.go:94:13:94:38 | type conversion | provenance | | +| test.go:80:13:80:16 | &... | test.go:81:13:81:29 | type conversion | provenance | | +| test.go:80:13:80:16 | &... | test.go:82:13:82:43 | type conversion | provenance | | +| test.go:85:22:85:26 | &... | test.go:86:13:86:30 | type conversion | provenance | | +| test.go:89:21:89:25 | &... | test.go:90:13:90:30 | type conversion | provenance | | | test.go:95:20:95:36 | call to Value | test.go:95:13:95:37 | type conversion | provenance | | | test.go:96:20:96:39 | call to RawValue | test.go:96:13:96:49 | type conversion | provenance | | | test.go:97:20:97:37 | call to String | test.go:97:13:97:38 | type conversion | provenance | | -| test.go:98:20:98:37 | call to Value | test.go:98:13:98:38 | type conversion | provenance | | -| test.go:99:20:99:40 | call to RawValue | test.go:99:13:99:50 | type conversion | provenance | | -| test.go:100:20:100:38 | call to String | test.go:100:13:100:39 | type conversion | provenance | | -| test.go:106:9:106:13 | &... | test.go:107:13:107:33 | type conversion | provenance | | -| test.go:110:9:110:12 | &... | test.go:111:13:111:29 | type conversion | provenance | | -| test.go:114:12:114:19 | &... | test.go:115:13:115:48 | type conversion | provenance | | -| test.go:118:16:118:24 | &... | test.go:119:13:119:43 | type conversion | provenance | | -| test.go:122:16:122:23 | &... | test.go:123:13:123:39 | type conversion | provenance | | -| test.go:126:15:126:24 | &... | test.go:127:13:127:47 | type conversion | provenance | | -| test.go:130:18:130:30 | &... | test.go:131:13:131:38 | type conversion | provenance | | -| test.go:137:12:137:19 | &... | test.go:138:13:138:48 | type conversion | provenance | | -| test.go:141:16:141:24 | &... | test.go:142:13:142:43 | type conversion | provenance | | -| test.go:145:16:145:23 | &... | test.go:146:13:146:39 | type conversion | provenance | | -| test.go:149:15:149:24 | &... | test.go:150:13:150:47 | type conversion | provenance | | -| test.go:153:18:153:30 | &... | test.go:154:13:154:38 | type conversion | provenance | | -| test.go:157:14:157:22 | &... | test.go:158:13:158:28 | type conversion | provenance | | -| test.go:161:15:161:24 | &... | test.go:162:13:162:32 | type conversion | provenance | | +| test.go:98:20:98:36 | call to Value | test.go:98:13:98:37 | type conversion | provenance | | +| test.go:99:20:99:39 | call to RawValue | test.go:99:13:99:49 | type conversion | provenance | | +| test.go:100:20:100:37 | call to String | test.go:100:13:100:38 | type conversion | provenance | | +| test.go:101:20:101:37 | call to Value | test.go:101:13:101:38 | type conversion | provenance | | +| test.go:102:20:102:40 | call to RawValue | test.go:102:13:102:50 | type conversion | provenance | | +| test.go:103:20:103:38 | call to String | test.go:103:13:103:39 | type conversion | provenance | | +| test.go:109:9:109:13 | &... | test.go:110:13:110:33 | type conversion | provenance | | +| test.go:113:9:113:12 | &... | test.go:114:13:114:29 | type conversion | provenance | | +| test.go:117:12:117:19 | &... | test.go:118:13:118:48 | type conversion | provenance | | +| test.go:121:16:121:24 | &... | test.go:122:13:122:43 | type conversion | provenance | | +| test.go:125:16:125:23 | &... | test.go:126:13:126:39 | type conversion | provenance | | +| test.go:129:15:129:24 | &... | test.go:130:13:130:47 | type conversion | provenance | | +| test.go:133:18:133:30 | &... | test.go:134:13:134:38 | type conversion | provenance | | +| test.go:140:12:140:19 | &... | test.go:141:13:141:48 | type conversion | provenance | | +| test.go:144:16:144:24 | &... | test.go:145:13:145:43 | type conversion | provenance | | +| test.go:148:16:148:23 | &... | test.go:149:13:149:39 | type conversion | provenance | | +| test.go:152:15:152:24 | &... | test.go:153:13:153:47 | type conversion | provenance | | +| test.go:156:18:156:30 | &... | test.go:157:13:157:38 | type conversion | provenance | | +| test.go:160:14:160:22 | &... | test.go:161:13:161:28 | type conversion | provenance | | +| test.go:164:15:164:24 | &... | test.go:165:13:165:32 | type conversion | provenance | | nodes -| test.go:77:13:77:16 | &... | semmle.label | &... | -| test.go:78:13:78:29 | type conversion | semmle.label | type conversion | -| test.go:79:13:79:43 | type conversion | semmle.label | type conversion | -| test.go:82:22:82:26 | &... | semmle.label | &... | -| test.go:83:13:83:30 | type conversion | semmle.label | type conversion | -| test.go:86:21:86:25 | &... | semmle.label | &... | -| test.go:87:13:87:30 | type conversion | semmle.label | type conversion | -| test.go:92:13:92:37 | type conversion | semmle.label | type conversion | -| test.go:92:20:92:36 | call to Value | semmle.label | call to Value | -| test.go:93:13:93:49 | type conversion | semmle.label | type conversion | -| test.go:93:20:93:39 | call to RawValue | semmle.label | call to RawValue | -| test.go:94:13:94:38 | type conversion | semmle.label | type conversion | -| test.go:94:20:94:37 | call to String | semmle.label | call to String | +| test.go:80:13:80:16 | &... | semmle.label | &... | +| test.go:81:13:81:29 | type conversion | semmle.label | type conversion | +| test.go:82:13:82:43 | type conversion | semmle.label | type conversion | +| test.go:85:22:85:26 | &... | semmle.label | &... | +| test.go:86:13:86:30 | type conversion | semmle.label | type conversion | +| test.go:89:21:89:25 | &... | semmle.label | &... | +| test.go:90:13:90:30 | type conversion | semmle.label | type conversion | | test.go:95:13:95:37 | type conversion | semmle.label | type conversion | | test.go:95:20:95:36 | call to Value | semmle.label | call to Value | | test.go:96:13:96:49 | type conversion | semmle.label | type conversion | | test.go:96:20:96:39 | call to RawValue | semmle.label | call to RawValue | | test.go:97:13:97:38 | type conversion | semmle.label | type conversion | | test.go:97:20:97:37 | call to String | semmle.label | call to String | -| test.go:98:13:98:38 | type conversion | semmle.label | type conversion | -| test.go:98:20:98:37 | call to Value | semmle.label | call to Value | -| test.go:99:13:99:50 | type conversion | semmle.label | type conversion | -| test.go:99:20:99:40 | call to RawValue | semmle.label | call to RawValue | -| test.go:100:13:100:39 | type conversion | semmle.label | type conversion | -| test.go:100:20:100:38 | call to String | semmle.label | call to String | -| test.go:106:9:106:13 | &... | semmle.label | &... | -| test.go:107:13:107:33 | type conversion | semmle.label | type conversion | -| test.go:110:9:110:12 | &... | semmle.label | &... | -| test.go:111:13:111:29 | type conversion | semmle.label | type conversion | -| test.go:114:12:114:19 | &... | semmle.label | &... | -| test.go:115:13:115:48 | type conversion | semmle.label | type conversion | -| test.go:118:16:118:24 | &... | semmle.label | &... | -| test.go:119:13:119:43 | type conversion | semmle.label | type conversion | -| test.go:122:16:122:23 | &... | semmle.label | &... | -| test.go:123:13:123:39 | type conversion | semmle.label | type conversion | -| test.go:126:15:126:24 | &... | semmle.label | &... | -| test.go:127:13:127:47 | type conversion | semmle.label | type conversion | -| test.go:130:18:130:30 | &... | semmle.label | &... | -| test.go:131:13:131:38 | type conversion | semmle.label | type conversion | -| test.go:137:12:137:19 | &... | semmle.label | &... | -| test.go:138:13:138:48 | type conversion | semmle.label | type conversion | -| test.go:141:16:141:24 | &... | semmle.label | &... | -| test.go:142:13:142:43 | type conversion | semmle.label | type conversion | -| test.go:145:16:145:23 | &... | semmle.label | &... | -| test.go:146:13:146:39 | type conversion | semmle.label | type conversion | -| test.go:149:15:149:24 | &... | semmle.label | &... | -| test.go:150:13:150:47 | type conversion | semmle.label | type conversion | -| test.go:153:18:153:30 | &... | semmle.label | &... | -| test.go:154:13:154:38 | type conversion | semmle.label | type conversion | -| test.go:157:14:157:22 | &... | semmle.label | &... | -| test.go:158:13:158:28 | type conversion | semmle.label | type conversion | -| test.go:161:15:161:24 | &... | semmle.label | &... | -| test.go:162:13:162:32 | type conversion | semmle.label | type conversion | +| test.go:98:13:98:37 | type conversion | semmle.label | type conversion | +| test.go:98:20:98:36 | call to Value | semmle.label | call to Value | +| test.go:99:13:99:49 | type conversion | semmle.label | type conversion | +| test.go:99:20:99:39 | call to RawValue | semmle.label | call to RawValue | +| test.go:100:13:100:38 | type conversion | semmle.label | type conversion | +| test.go:100:20:100:37 | call to String | semmle.label | call to String | +| test.go:101:13:101:38 | type conversion | semmle.label | type conversion | +| test.go:101:20:101:37 | call to Value | semmle.label | call to Value | +| test.go:102:13:102:50 | type conversion | semmle.label | type conversion | +| test.go:102:20:102:40 | call to RawValue | semmle.label | call to RawValue | +| test.go:103:13:103:39 | type conversion | semmle.label | type conversion | +| test.go:103:20:103:38 | call to String | semmle.label | call to String | +| test.go:109:9:109:13 | &... | semmle.label | &... | +| test.go:110:13:110:33 | type conversion | semmle.label | type conversion | +| test.go:113:9:113:12 | &... | semmle.label | &... | +| test.go:114:13:114:29 | type conversion | semmle.label | type conversion | +| test.go:117:12:117:19 | &... | semmle.label | &... | +| test.go:118:13:118:48 | type conversion | semmle.label | type conversion | +| test.go:121:16:121:24 | &... | semmle.label | &... | +| test.go:122:13:122:43 | type conversion | semmle.label | type conversion | +| test.go:125:16:125:23 | &... | semmle.label | &... | +| test.go:126:13:126:39 | type conversion | semmle.label | type conversion | +| test.go:129:15:129:24 | &... | semmle.label | &... | +| test.go:130:13:130:47 | type conversion | semmle.label | type conversion | +| test.go:133:18:133:30 | &... | semmle.label | &... | +| test.go:134:13:134:38 | type conversion | semmle.label | type conversion | +| test.go:140:12:140:19 | &... | semmle.label | &... | +| test.go:141:13:141:48 | type conversion | semmle.label | type conversion | +| test.go:144:16:144:24 | &... | semmle.label | &... | +| test.go:145:13:145:43 | type conversion | semmle.label | type conversion | +| test.go:148:16:148:23 | &... | semmle.label | &... | +| test.go:149:13:149:39 | type conversion | semmle.label | type conversion | +| test.go:152:15:152:24 | &... | semmle.label | &... | +| test.go:153:13:153:47 | type conversion | semmle.label | type conversion | +| test.go:156:18:156:30 | &... | semmle.label | &... | +| test.go:157:13:157:38 | type conversion | semmle.label | type conversion | +| test.go:160:14:160:22 | &... | semmle.label | &... | +| test.go:161:13:161:28 | type conversion | semmle.label | type conversion | +| test.go:164:15:164:24 | &... | semmle.label | &... | +| test.go:165:13:165:32 | type conversion | semmle.label | type conversion | subpaths #select -| test.go:78:13:78:29 | type conversion | test.go:77:13:77:16 | &... | test.go:78:13:78:29 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:77:13:77:16 | &... | stored value | -| test.go:79:13:79:43 | type conversion | test.go:77:13:77:16 | &... | test.go:79:13:79:43 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:77:13:77:16 | &... | stored value | -| test.go:83:13:83:30 | type conversion | test.go:82:22:82:26 | &... | test.go:83:13:83:30 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:82:22:82:26 | &... | stored value | -| test.go:87:13:87:30 | type conversion | test.go:86:21:86:25 | &... | test.go:87:13:87:30 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:86:21:86:25 | &... | stored value | -| test.go:92:13:92:37 | type conversion | test.go:92:20:92:36 | call to Value | test.go:92:13:92:37 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:92:20:92:36 | call to Value | stored value | -| test.go:93:13:93:49 | type conversion | test.go:93:20:93:39 | call to RawValue | test.go:93:13:93:49 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:93:20:93:39 | call to RawValue | stored value | -| test.go:94:13:94:38 | type conversion | test.go:94:20:94:37 | call to String | test.go:94:13:94:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:94:20:94:37 | call to String | stored value | +| test.go:81:13:81:29 | type conversion | test.go:80:13:80:16 | &... | test.go:81:13:81:29 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:80:13:80:16 | &... | stored value | +| test.go:82:13:82:43 | type conversion | test.go:80:13:80:16 | &... | test.go:82:13:82:43 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:80:13:80:16 | &... | stored value | +| test.go:86:13:86:30 | type conversion | test.go:85:22:85:26 | &... | test.go:86:13:86:30 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:85:22:85:26 | &... | stored value | +| test.go:90:13:90:30 | type conversion | test.go:89:21:89:25 | &... | test.go:90:13:90:30 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:89:21:89:25 | &... | stored value | | test.go:95:13:95:37 | type conversion | test.go:95:20:95:36 | call to Value | test.go:95:13:95:37 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:95:20:95:36 | call to Value | stored value | | test.go:96:13:96:49 | type conversion | test.go:96:20:96:39 | call to RawValue | test.go:96:13:96:49 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:96:20:96:39 | call to RawValue | stored value | | test.go:97:13:97:38 | type conversion | test.go:97:20:97:37 | call to String | test.go:97:13:97:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:97:20:97:37 | call to String | stored value | -| test.go:98:13:98:38 | type conversion | test.go:98:20:98:37 | call to Value | test.go:98:13:98:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:98:20:98:37 | call to Value | stored value | -| test.go:99:13:99:50 | type conversion | test.go:99:20:99:40 | call to RawValue | test.go:99:13:99:50 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:99:20:99:40 | call to RawValue | stored value | -| test.go:100:13:100:39 | type conversion | test.go:100:20:100:38 | call to String | test.go:100:13:100:39 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:100:20:100:38 | call to String | stored value | -| test.go:107:13:107:33 | type conversion | test.go:106:9:106:13 | &... | test.go:107:13:107:33 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:106:9:106:13 | &... | stored value | -| test.go:111:13:111:29 | type conversion | test.go:110:9:110:12 | &... | test.go:111:13:111:29 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:110:9:110:12 | &... | stored value | -| test.go:115:13:115:48 | type conversion | test.go:114:12:114:19 | &... | test.go:115:13:115:48 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:114:12:114:19 | &... | stored value | -| test.go:119:13:119:43 | type conversion | test.go:118:16:118:24 | &... | test.go:119:13:119:43 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:118:16:118:24 | &... | stored value | -| test.go:123:13:123:39 | type conversion | test.go:122:16:122:23 | &... | test.go:123:13:123:39 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:122:16:122:23 | &... | stored value | -| test.go:127:13:127:47 | type conversion | test.go:126:15:126:24 | &... | test.go:127:13:127:47 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:126:15:126:24 | &... | stored value | -| test.go:131:13:131:38 | type conversion | test.go:130:18:130:30 | &... | test.go:131:13:131:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:130:18:130:30 | &... | stored value | -| test.go:138:13:138:48 | type conversion | test.go:137:12:137:19 | &... | test.go:138:13:138:48 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:137:12:137:19 | &... | stored value | -| test.go:142:13:142:43 | type conversion | test.go:141:16:141:24 | &... | test.go:142:13:142:43 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:141:16:141:24 | &... | stored value | -| test.go:146:13:146:39 | type conversion | test.go:145:16:145:23 | &... | test.go:146:13:146:39 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:145:16:145:23 | &... | stored value | -| test.go:150:13:150:47 | type conversion | test.go:149:15:149:24 | &... | test.go:150:13:150:47 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:149:15:149:24 | &... | stored value | -| test.go:154:13:154:38 | type conversion | test.go:153:18:153:30 | &... | test.go:154:13:154:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:153:18:153:30 | &... | stored value | -| test.go:158:13:158:28 | type conversion | test.go:157:14:157:22 | &... | test.go:158:13:158:28 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:157:14:157:22 | &... | stored value | -| test.go:162:13:162:32 | type conversion | test.go:161:15:161:24 | &... | test.go:162:13:162:32 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:161:15:161:24 | &... | stored value | +| test.go:98:13:98:37 | type conversion | test.go:98:20:98:36 | call to Value | test.go:98:13:98:37 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:98:20:98:36 | call to Value | stored value | +| test.go:99:13:99:49 | type conversion | test.go:99:20:99:39 | call to RawValue | test.go:99:13:99:49 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:99:20:99:39 | call to RawValue | stored value | +| test.go:100:13:100:38 | type conversion | test.go:100:20:100:37 | call to String | test.go:100:13:100:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:100:20:100:37 | call to String | stored value | +| test.go:101:13:101:38 | type conversion | test.go:101:20:101:37 | call to Value | test.go:101:13:101:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:101:20:101:37 | call to Value | stored value | +| test.go:102:13:102:50 | type conversion | test.go:102:20:102:40 | call to RawValue | test.go:102:13:102:50 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:102:20:102:40 | call to RawValue | stored value | +| test.go:103:13:103:39 | type conversion | test.go:103:20:103:38 | call to String | test.go:103:13:103:39 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:103:20:103:38 | call to String | stored value | +| test.go:110:13:110:33 | type conversion | test.go:109:9:109:13 | &... | test.go:110:13:110:33 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:109:9:109:13 | &... | stored value | +| test.go:114:13:114:29 | type conversion | test.go:113:9:113:12 | &... | test.go:114:13:114:29 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:113:9:113:12 | &... | stored value | +| test.go:118:13:118:48 | type conversion | test.go:117:12:117:19 | &... | test.go:118:13:118:48 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:117:12:117:19 | &... | stored value | +| test.go:122:13:122:43 | type conversion | test.go:121:16:121:24 | &... | test.go:122:13:122:43 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:121:16:121:24 | &... | stored value | +| test.go:126:13:126:39 | type conversion | test.go:125:16:125:23 | &... | test.go:126:13:126:39 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:125:16:125:23 | &... | stored value | +| test.go:130:13:130:47 | type conversion | test.go:129:15:129:24 | &... | test.go:130:13:130:47 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:129:15:129:24 | &... | stored value | +| test.go:134:13:134:38 | type conversion | test.go:133:18:133:30 | &... | test.go:134:13:134:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:133:18:133:30 | &... | stored value | +| test.go:141:13:141:48 | type conversion | test.go:140:12:140:19 | &... | test.go:141:13:141:48 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:140:12:140:19 | &... | stored value | +| test.go:145:13:145:43 | type conversion | test.go:144:16:144:24 | &... | test.go:145:13:145:43 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:144:16:144:24 | &... | stored value | +| test.go:149:13:149:39 | type conversion | test.go:148:16:148:23 | &... | test.go:149:13:149:39 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:148:16:148:23 | &... | stored value | +| test.go:153:13:153:47 | type conversion | test.go:152:15:152:24 | &... | test.go:153:13:153:47 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:152:15:152:24 | &... | stored value | +| test.go:157:13:157:38 | type conversion | test.go:156:18:156:30 | &... | test.go:157:13:157:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:156:18:156:30 | &... | stored value | +| test.go:161:13:161:28 | type conversion | test.go:160:14:160:22 | &... | test.go:161:13:161:28 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:160:14:160:22 | &... | stored value | +| test.go:165:13:165:32 | type conversion | test.go:164:15:164:24 | &... | test.go:165:13:165:32 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:164:15:164:24 | &... | stored value | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/test.go b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/test.go index 6e590a122768..cce152e57ef8 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/test.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/test.go @@ -1,65 +1,68 @@ package test import ( - "github.com/astaxie/beego/orm" "net/http" + + "github.com/astaxie/beego/orm" ) // BAD: using untrusted data in SQL queries func testDbMethods(bdb *orm.DB, untrustedSource *http.Request) { untrusted := untrustedSource.UserAgent() - bdb.Exec(untrusted) - bdb.ExecContext(nil, untrusted) - bdb.Prepare(untrusted) - bdb.PrepareContext(nil, untrusted) - bdb.Query(untrusted) - bdb.QueryContext(nil, untrusted) - bdb.QueryRow(untrusted) - bdb.QueryRowContext(nil, untrusted) + bdb.Exec(untrusted) // $ querystring=untrusted + bdb.ExecContext(nil, untrusted) // $ querystring=untrusted + bdb.Prepare(untrusted) // $ querystring=untrusted + bdb.PrepareContext(nil, untrusted) // $ querystring=untrusted + bdb.Query(untrusted) // $ querystring=untrusted + bdb.QueryContext(nil, untrusted) // $ querystring=untrusted + bdb.QueryRow(untrusted) // $ querystring=untrusted + bdb.QueryRowContext(nil, untrusted) // $ querystring=untrusted } // BAD: using untrusted data to build SQL queries (QueryBuilder does not sanitize its arguments) func testQueryBuilderMethods(qb orm.QueryBuilder, untrustedSource *http.Request) { untrusted := untrustedSource.UserAgent() - - qb.Select(untrusted) - qb.From(untrusted) - qb.InnerJoin(untrusted) - qb.LeftJoin(untrusted) - qb.RightJoin(untrusted) - qb.On(untrusted) - qb.Where(untrusted) - qb.And(untrusted) - qb.Or(untrusted) - qb.In(untrusted) - qb.OrderBy(untrusted) - qb.GroupBy(untrusted) - qb.Having(untrusted) - qb.Update(untrusted) - qb.Set(untrusted) - qb.Delete(untrusted) - qb.InsertInto(untrusted, untrusted) - qb.Values(untrusted) - qb.Subquery(untrusted, untrusted) + untrusted2 := untrustedSource.UserAgent() + + qb.Select(untrusted) // $ querystring=untrusted + qb.From(untrusted) // $ querystring=untrusted + qb.InnerJoin(untrusted) // $ querystring=untrusted + qb.LeftJoin(untrusted) // $ querystring=untrusted + qb.RightJoin(untrusted) // $ querystring=untrusted + qb.On(untrusted) // $ querystring=untrusted + qb.Where(untrusted) // $ querystring=untrusted + qb.And(untrusted) // $ querystring=untrusted + qb.Or(untrusted) // $ querystring=untrusted + qb.In(untrusted) // $ querystring=untrusted + qb.OrderBy(untrusted) // $ querystring=untrusted + qb.GroupBy(untrusted) // $ querystring=untrusted + qb.Having(untrusted) // $ querystring=untrusted + qb.Update(untrusted) // $ querystring=untrusted + qb.Set(untrusted) // $ querystring=untrusted + qb.Delete(untrusted) // $ querystring=untrusted + qb.InsertInto(untrusted, untrusted2) // $ querystring=untrusted querystring=untrusted2 + qb.Values(untrusted) // $ querystring=untrusted + qb.Subquery(untrusted, untrusted2) // $ querystring=untrusted querystring=untrusted2 } func testOrmerRaw(ormer orm.Ormer, untrustedSource *http.Request) { untrusted := untrustedSource.UserAgent() - ormer.Raw(untrusted) // BAD: using an untrusted string as a query - ormer.Raw("FROM ? SELECT ?", untrusted, untrusted) // GOOD: untrusted string used in argument context + untrusted2 := untrustedSource.UserAgent() + ormer.Raw(untrusted, untrusted2) // $ querystring=untrusted // BAD: using an untrusted string as a query + ormer.Raw("FROM ? SELECT ?", untrusted, untrusted2) // $ querystring="FROM ? SELECT ?" // GOOD: untrusted string used in argument context } func testFilterRaw(querySeter orm.QuerySeter, untrustedSource *http.Request) { untrusted := untrustedSource.UserAgent() - querySeter.FilterRaw(untrusted, "safe") // GOOD: untrusted used as a column name - querySeter.FilterRaw("safe", untrusted) // BAD: untrusted used as a SQL fragment + querySeter.FilterRaw(untrusted, "safe") // $ querystring="safe" // GOOD: untrusted used as a column name + querySeter.FilterRaw("safe", untrusted) // $ querystring=untrusted // BAD: untrusted used as a SQL fragment } func testConditionRaw(cond orm.Condition, untrustedSource *http.Request) { untrusted := untrustedSource.UserAgent() - cond.Raw(untrusted, "safe") // GOOD: untrusted used as a column name - cond.Raw("safe", untrusted) // BAD: untrusted used as a SQL fragment + cond.Raw(untrusted, "safe") // $ querystring="safe" // GOOD: untrusted used as a column name + cond.Raw("safe", untrusted) // $ querystring=untrusted // BAD: untrusted used as a SQL fragment } type SubStruct struct { From ad213579a1eb099e3c76e86a735dd560f95322b3 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 16:46:02 +0100 Subject: [PATCH 09/16] Convert Beego orm sql-injection sinks to MaD --- ...ithub.com.beego.beego.client.orm.model.yml | 42 +++++++++ go/ql/lib/semmle/go/frameworks/BeegoOrm.qll | 51 ----------- .../frameworks/BeegoOrm/SqlInjection.expected | 87 ++++++++++++------- 3 files changed, 96 insertions(+), 84 deletions(-) create mode 100644 go/ql/lib/ext/github.com.beego.beego.client.orm.model.yml diff --git a/go/ql/lib/ext/github.com.beego.beego.client.orm.model.yml b/go/ql/lib/ext/github.com.beego.beego.client.orm.model.yml new file mode 100644 index 000000000000..9e8849423eb0 --- /dev/null +++ b/go/ql/lib/ext/github.com.beego.beego.client.orm.model.yml @@ -0,0 +1,42 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: packageGrouping + data: + - ["beego-orm", "github.com/beego/beego/client/orm"] + - ["beego-orm", "github.com/astaxie/beego/orm"] + - ["beego-orm", "github.com/beego/beego/orm"] + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["group:beego-orm", "Condition", False, "Raw", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:beego-orm", "DB", False, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "DB", False, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:beego-orm", "DB", False, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "DB", False, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:beego-orm", "DB", False, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "DB", False, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:beego-orm", "DB", False, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "DB", False, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["group:beego-orm", "Ormer", False, "Raw", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "And", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Delete", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "From", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "GroupBy", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Having", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "In", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "InnerJoin", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "InsertInto", "", "", "Argument[0..1]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "LeftJoin", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "On", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Or", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "OrderBy", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "RightJoin", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Select", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Set", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Subquery", "", "", "Argument[0..1]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Update", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Values", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QueryBuilder", False, "Where", "", "", "Argument[0]", "sql-injection", "manual"] + - ["group:beego-orm", "QuerySeter", False, "FilterRaw", "", "", "Argument[1]", "sql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll b/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll index c1de0cf42442..925b0f19fa3f 100644 --- a/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll +++ b/go/ql/lib/semmle/go/frameworks/BeegoOrm.qll @@ -14,57 +14,6 @@ module BeegoOrm { /** Gets the package name `github.com/astaxie/beego/orm`. */ string packagePath() { result = package("github.com/astaxie/beego", "orm") } - private class DbSink extends SQL::QueryString::Range { - DbSink() { - exists(Method m, string methodName, int argNum | - m.hasQualifiedName(packagePath(), "DB", methodName) and - ( - methodName = ["Exec", "Prepare", "Query", "QueryRow"] and - argNum = 0 - or - methodName = ["ExecContext", "PrepareContext", "QueryContext", "QueryRowContext"] and - argNum = 1 - ) - | - this = m.getACall().getArgument(argNum) - ) - } - } - - private class QueryBuilderSink extends SQL::QueryString::Range { - // Note this class doesn't do any escaping, unlike the true ORM part of the package - QueryBuilderSink() { - exists(Method impl | impl.implements(packagePath(), "QueryBuilder", _) | - this = impl.getACall().getASyntacticArgument() - ) and - this.getType().getUnderlyingType() instanceof StringType - } - } - - private class OrmerRawSink extends SQL::QueryString::Range { - OrmerRawSink() { - exists(Method impl | impl.implements(packagePath(), "Ormer", "Raw") | - this = impl.getACall().getArgument(0) - ) - } - } - - private class QuerySeterFilterRawSink extends SQL::QueryString::Range { - QuerySeterFilterRawSink() { - exists(Method impl | impl.implements(packagePath(), "QuerySeter", "FilterRaw") | - this = impl.getACall().getArgument(1) - ) - } - } - - private class ConditionRawSink extends SQL::QueryString::Range { - ConditionRawSink() { - exists(Method impl | impl.implements(packagePath(), "Condition", "Raw") | - this = impl.getACall().getArgument(1) - ) - } - } - private class OrmerSource extends StoredXss::Source { OrmerSource() { exists(Method impl | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected index 6cc1e09486b7..992c1387103a 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/SqlInjection.expected @@ -32,40 +32,61 @@ | test.go:59:31:59:39 | untrusted | test.go:57:15:57:41 | call to UserAgent | test.go:59:31:59:39 | untrusted | This query depends on a $@. | test.go:57:15:57:41 | call to UserAgent | user-provided value | | test.go:65:19:65:27 | untrusted | test.go:63:15:63:41 | call to UserAgent | test.go:65:19:65:27 | untrusted | This query depends on a $@. | test.go:63:15:63:41 | call to UserAgent | user-provided value | edges -| test.go:11:15:11:41 | call to UserAgent | test.go:13:11:13:19 | untrusted | provenance | Src:MaD:1 | -| test.go:11:15:11:41 | call to UserAgent | test.go:14:23:14:31 | untrusted | provenance | Src:MaD:1 | -| test.go:11:15:11:41 | call to UserAgent | test.go:15:14:15:22 | untrusted | provenance | Src:MaD:1 | -| test.go:11:15:11:41 | call to UserAgent | test.go:16:26:16:34 | untrusted | provenance | Src:MaD:1 | -| test.go:11:15:11:41 | call to UserAgent | test.go:17:12:17:20 | untrusted | provenance | Src:MaD:1 | -| test.go:11:15:11:41 | call to UserAgent | test.go:18:24:18:32 | untrusted | provenance | Src:MaD:1 | -| test.go:11:15:11:41 | call to UserAgent | test.go:19:15:19:23 | untrusted | provenance | Src:MaD:1 | -| test.go:11:15:11:41 | call to UserAgent | test.go:20:27:20:35 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:28:12:28:20 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:29:10:29:18 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:30:15:30:23 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:31:14:31:22 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:32:15:32:23 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:33:8:33:16 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:34:11:34:19 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:35:9:35:17 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:36:8:36:16 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:37:8:37:16 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:38:13:38:21 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:39:13:39:21 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:40:12:40:20 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:41:12:41:20 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:42:9:42:17 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:43:12:43:20 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:44:16:44:24 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:45:12:45:20 | untrusted | provenance | Src:MaD:1 | -| test.go:25:15:25:41 | call to UserAgent | test.go:46:14:46:22 | untrusted | provenance | Src:MaD:1 | -| test.go:26:16:26:42 | call to UserAgent | test.go:44:27:44:36 | untrusted2 | provenance | Src:MaD:1 | -| test.go:26:16:26:42 | call to UserAgent | test.go:46:25:46:34 | untrusted2 | provenance | Src:MaD:1 | -| test.go:50:15:50:41 | call to UserAgent | test.go:52:12:52:20 | untrusted | provenance | Src:MaD:1 | -| test.go:57:15:57:41 | call to UserAgent | test.go:59:31:59:39 | untrusted | provenance | Src:MaD:1 | -| test.go:63:15:63:41 | call to UserAgent | test.go:65:19:65:27 | untrusted | provenance | Src:MaD:1 | +| test.go:11:15:11:41 | call to UserAgent | test.go:13:11:13:19 | untrusted | provenance | Src:MaD:22 Sink:MaD:2 | +| test.go:11:15:11:41 | call to UserAgent | test.go:14:23:14:31 | untrusted | provenance | Src:MaD:22 Sink:MaD:3 | +| test.go:11:15:11:41 | call to UserAgent | test.go:15:14:15:22 | untrusted | provenance | Src:MaD:22 Sink:MaD:4 | +| test.go:11:15:11:41 | call to UserAgent | test.go:16:26:16:34 | untrusted | provenance | Src:MaD:22 Sink:MaD:5 | +| test.go:11:15:11:41 | call to UserAgent | test.go:17:12:17:20 | untrusted | provenance | Src:MaD:22 Sink:MaD:6 | +| test.go:11:15:11:41 | call to UserAgent | test.go:18:24:18:32 | untrusted | provenance | Src:MaD:22 Sink:MaD:7 | +| test.go:11:15:11:41 | call to UserAgent | test.go:19:15:19:23 | untrusted | provenance | Src:MaD:22 Sink:MaD:8 | +| test.go:11:15:11:41 | call to UserAgent | test.go:20:27:20:35 | untrusted | provenance | Src:MaD:22 Sink:MaD:9 | +| test.go:25:15:25:41 | call to UserAgent | test.go:28:12:28:20 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:29:10:29:18 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:30:15:30:23 | untrusted | provenance | Src:MaD:22 Sink:MaD:13 | +| test.go:25:15:25:41 | call to UserAgent | test.go:31:14:31:22 | untrusted | provenance | Src:MaD:22 Sink:MaD:15 | +| test.go:25:15:25:41 | call to UserAgent | test.go:32:15:32:23 | untrusted | provenance | Src:MaD:22 Sink:MaD:18 | +| test.go:25:15:25:41 | call to UserAgent | test.go:33:8:33:16 | untrusted | provenance | Src:MaD:22 Sink:MaD:16 | +| test.go:25:15:25:41 | call to UserAgent | test.go:34:11:34:19 | untrusted | provenance | Src:MaD:22 Sink:MaD:20 | +| test.go:25:15:25:41 | call to UserAgent | test.go:35:9:35:17 | untrusted | provenance | Src:MaD:22 Sink:MaD:11 | +| test.go:25:15:25:41 | call to UserAgent | test.go:36:8:36:16 | untrusted | provenance | Src:MaD:22 Sink:MaD:17 | +| test.go:25:15:25:41 | call to UserAgent | test.go:37:8:37:16 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:38:13:38:21 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:39:13:39:21 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:40:12:40:20 | untrusted | provenance | Src:MaD:22 Sink:MaD:12 | +| test.go:25:15:25:41 | call to UserAgent | test.go:41:12:41:20 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:42:9:42:17 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:43:12:43:20 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:44:16:44:24 | untrusted | provenance | Src:MaD:22 Sink:MaD:14 | +| test.go:25:15:25:41 | call to UserAgent | test.go:45:12:45:20 | untrusted | provenance | Src:MaD:22 | +| test.go:25:15:25:41 | call to UserAgent | test.go:46:14:46:22 | untrusted | provenance | Src:MaD:22 Sink:MaD:19 | +| test.go:26:16:26:42 | call to UserAgent | test.go:44:27:44:36 | untrusted2 | provenance | Src:MaD:22 | +| test.go:26:16:26:42 | call to UserAgent | test.go:46:25:46:34 | untrusted2 | provenance | Src:MaD:22 Sink:MaD:19 | +| test.go:50:15:50:41 | call to UserAgent | test.go:52:12:52:20 | untrusted | provenance | Src:MaD:22 Sink:MaD:10 | +| test.go:57:15:57:41 | call to UserAgent | test.go:59:31:59:39 | untrusted | provenance | Src:MaD:22 Sink:MaD:21 | +| test.go:63:15:63:41 | call to UserAgent | test.go:65:19:65:27 | untrusted | provenance | Src:MaD:22 Sink:MaD:1 | models -| 1 | Source: net/http; Request; true; UserAgent; ; ; ReturnValue; remote; manual | +| 1 | Sink: group:beego-orm; Condition; false; Raw; ; ; Argument[1]; sql-injection; manual | +| 2 | Sink: group:beego-orm; DB; false; Exec; ; ; Argument[0]; sql-injection; manual | +| 3 | Sink: group:beego-orm; DB; false; ExecContext; ; ; Argument[1]; sql-injection; manual | +| 4 | Sink: group:beego-orm; DB; false; Prepare; ; ; Argument[0]; sql-injection; manual | +| 5 | Sink: group:beego-orm; DB; false; PrepareContext; ; ; Argument[1]; sql-injection; manual | +| 6 | Sink: group:beego-orm; DB; false; Query; ; ; Argument[0]; sql-injection; manual | +| 7 | Sink: group:beego-orm; DB; false; QueryContext; ; ; Argument[1]; sql-injection; manual | +| 8 | Sink: group:beego-orm; DB; false; QueryRow; ; ; Argument[0]; sql-injection; manual | +| 9 | Sink: group:beego-orm; DB; false; QueryRowContext; ; ; Argument[1]; sql-injection; manual | +| 10 | Sink: group:beego-orm; Ormer; false; Raw; ; ; Argument[0]; sql-injection; manual | +| 11 | Sink: group:beego-orm; QueryBuilder; false; And; ; ; Argument[0]; sql-injection; manual | +| 12 | Sink: group:beego-orm; QueryBuilder; false; Having; ; ; Argument[0]; sql-injection; manual | +| 13 | Sink: group:beego-orm; QueryBuilder; false; InnerJoin; ; ; Argument[0]; sql-injection; manual | +| 14 | Sink: group:beego-orm; QueryBuilder; false; InsertInto; ; ; Argument[0..1]; sql-injection; manual | +| 15 | Sink: group:beego-orm; QueryBuilder; false; LeftJoin; ; ; Argument[0]; sql-injection; manual | +| 16 | Sink: group:beego-orm; QueryBuilder; false; On; ; ; Argument[0]; sql-injection; manual | +| 17 | Sink: group:beego-orm; QueryBuilder; false; Or; ; ; Argument[0]; sql-injection; manual | +| 18 | Sink: group:beego-orm; QueryBuilder; false; RightJoin; ; ; Argument[0]; sql-injection; manual | +| 19 | Sink: group:beego-orm; QueryBuilder; false; Subquery; ; ; Argument[0..1]; sql-injection; manual | +| 20 | Sink: group:beego-orm; QueryBuilder; false; Where; ; ; Argument[0]; sql-injection; manual | +| 21 | Sink: group:beego-orm; QuerySeter; false; FilterRaw; ; ; Argument[1]; sql-injection; manual | +| 22 | Source: net/http; Request; true; UserAgent; ; ; ReturnValue; remote; manual | nodes | test.go:11:15:11:41 | call to UserAgent | semmle.label | call to UserAgent | | test.go:13:11:13:19 | untrusted | semmle.label | untrusted | From 501bb3eb56ace85259119ccf8cb4aeeaddbf1634 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 16:57:26 +0100 Subject: [PATCH 10/16] Convert database/sql sql-injection sinks to MaD --- go/ql/lib/ext/database.sql.model.yml | 28 +++++++++++++++++++ .../go/frameworks/stdlib/DatabaseSql.qll | 20 +------------ .../frameworks/XNetHtml/SqlInjection.expected | 9 +++--- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/go/ql/lib/ext/database.sql.model.yml b/go/ql/lib/ext/database.sql.model.yml index e1083f6e49a6..b8fb85bb893a 100644 --- a/go/ql/lib/ext/database.sql.model.yml +++ b/go/ql/lib/ext/database.sql.model.yml @@ -1,4 +1,32 @@ extensions: + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["database/sql", "Conn", False, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "Conn", False, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "Conn", False, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "Conn", False, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "Conn", False, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "Conn", False, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "Conn", False, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "Conn", False, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "DB", False, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "DB", False, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "DB", False, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "DB", False, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "DB", False, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "DB", False, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "DB", False, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "DB", False, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "Tx", False, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "Tx", False, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "Tx", False, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "Tx", False, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "Tx", False, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "Tx", False, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql", "Tx", False, "QueryRow", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql", "Tx", False, "QueryRowContext", "", "", "Argument[1]", "sql-injection", "manual"] - addsTo: pack: codeql/go-all extensible: summaryModel diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll b/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll index 845225af5bd2..5f53ea2de8cd 100644 --- a/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll +++ b/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll @@ -26,7 +26,7 @@ module DatabaseSql { override DataFlow::Node getAResult() { result = this.getResult(0) } override SQL::QueryString getAQueryString() { - result = this.getAnArgument() + result = this.getASyntacticArgument() or // attempt to resolve a `QueryString` for `Stmt`s using local data flow. t = "Stmt" and @@ -34,24 +34,6 @@ module DatabaseSql { } } - /** A query string used in an API function of the `database/sql` package. */ - private class QueryString extends SQL::QueryString::Range { - QueryString() { - exists(Method meth, string base, string t, string m, int n | - t = ["DB", "Tx", "Conn"] and - meth.hasQualifiedName("database/sql", t, m) and - this = meth.getACall().getArgument(n) - | - base = ["Exec", "Prepare", "Query", "QueryRow"] and - ( - m = base and n = 0 - or - m = base + "Context" and n = 1 - ) - ) - } - } - /** A query in the standard `database/sql/driver` package. */ private class DriverQuery extends SQL::Query::Range, DataFlow::MethodCallNode { DriverQuery() { diff --git a/go/ql/test/library-tests/semmle/go/frameworks/XNetHtml/SqlInjection.expected b/go/ql/test/library-tests/semmle/go/frameworks/XNetHtml/SqlInjection.expected index 446695b259b2..b7a66a6a903c 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/XNetHtml/SqlInjection.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/XNetHtml/SqlInjection.expected @@ -1,11 +1,12 @@ #select | test.go:57:11:57:41 | call to EscapeString | test.go:56:2:56:42 | ... := ...[0] | test.go:57:11:57:41 | call to EscapeString | This query depends on a $@. | test.go:56:2:56:42 | ... := ...[0] | user-provided value | edges -| test.go:56:2:56:42 | ... := ...[0] | test.go:57:29:57:40 | selection of Value | provenance | Src:MaD:2 | -| test.go:57:29:57:40 | selection of Value | test.go:57:11:57:41 | call to EscapeString | provenance | MaD:1 | +| test.go:56:2:56:42 | ... := ...[0] | test.go:57:29:57:40 | selection of Value | provenance | Src:MaD:3 | +| test.go:57:29:57:40 | selection of Value | test.go:57:11:57:41 | call to EscapeString | provenance | MaD:2 Sink:MaD:1 | models -| 1 | Summary: golang.org/x/net/html; ; false; EscapeString; ; ; Argument[0]; ReturnValue; taint; manual | -| 2 | Source: net/http; Request; true; Cookie; ; ; ReturnValue[0]; remote; manual | +| 1 | Sink: database/sql; DB; false; Query; ; ; Argument[0]; sql-injection; manual | +| 2 | Summary: golang.org/x/net/html; ; false; EscapeString; ; ; Argument[0]; ReturnValue; taint; manual | +| 3 | Source: net/http; Request; true; Cookie; ; ; ReturnValue[0]; remote; manual | nodes | test.go:56:2:56:42 | ... := ...[0] | semmle.label | ... := ...[0] | | test.go:57:11:57:41 | call to EscapeString | semmle.label | call to EscapeString | From c7859ecebffa91477c8da437bcf3f95b8c407298 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 21:29:41 +0100 Subject: [PATCH 11/16] Improve SQL tests Separate the tests for Squirrel and database/sql. Add tests for database/sql/driver. --- .../go/frameworks/SQL/database-sql-driver.go | 29 ++++++++ .../semmle/go/frameworks/SQL/database-sql.go | 67 +++++++++++++++++++ .../frameworks/SQL/{main.go => squirrel.go} | 62 ----------------- 3 files changed, 96 insertions(+), 62 deletions(-) create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/database-sql-driver.go create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/SQL/database-sql.go rename go/ql/test/library-tests/semmle/go/frameworks/SQL/{main.go => squirrel.go} (59%) diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/database-sql-driver.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/database-sql-driver.go new file mode 100644 index 000000000000..e69df493b846 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/database-sql-driver.go @@ -0,0 +1,29 @@ +package main + +import ( + "context" + "database/sql/driver" +) + +var ( + driver1 string + driver2 string + driver3 string + driver4 string + driver5 string + driver6 string +) + +func testDriver(ctx context.Context, execer driver.Execer, + execerContext driver.ExecerContext, + conn driver.Conn, + connContext driver.ConnPrepareContext, + queryer driver.Queryer, + queryerContext driver.QueryerContext) { + execer.Exec(driver1, nil) // $ query=driver1 + execerContext.ExecContext(ctx, driver2, nil) // $ query=driver2 + conn.Prepare(driver3) // $ querystring=driver3 + connContext.PrepareContext(ctx, driver4) // $ querystring=driver4 + queryer.Query(driver5, nil) // $ query=driver5 + queryerContext.QueryContext(ctx, driver6, nil) // $ query=driver6 +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/database-sql.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/database-sql.go new file mode 100644 index 000000000000..80fc869bb044 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/database-sql.go @@ -0,0 +1,67 @@ +package main + +//go:generate depstubber -vendor github.com/Masterminds/squirrel DeleteBuilder,InsertBuilder,SelectBuilder,UpdateBuilder Delete,Expr,Insert,Select,Update + +import ( + "context" + "database/sql" +) + +var ( + query1 string + query2 string + query3 string + query4 string + query5 string + query6 string + query7 string + query8 string + query11 string + query12 string + query13 string + query14 string + query15 string + query16 string + query17 string + query18 string + query21 string + query22 string + query23 string +) + +func test(db *sql.DB, ctx context.Context) { + db.Exec(query1) // $ query=query1 + db.ExecContext(ctx, query2) // $ query=query2 + db.Prepare(query3) // $ querystring=query3 + db.PrepareContext(ctx, query4) // $ querystring=query4 + db.Query(query5) // $ query=query5 + db.QueryContext(ctx, query6) // $ query=query6 + db.QueryRow(query7) // $ query=query7 + db.QueryRowContext(ctx, query8) // $ query=query8 +} + +func test2(tx *sql.Tx, query string, ctx context.Context) { + tx.Exec(query11) // $ query=query11 + tx.ExecContext(ctx, query12) // $ query=query12 + tx.Prepare(query13) // $ querystring=query13 + tx.PrepareContext(ctx, query14) // $ querystring=query14 + tx.Query(query15) // $ query=query15 + tx.QueryContext(ctx, query16) // $ query=query16 + tx.QueryRow(query17) // $ query=query17 + tx.QueryRowContext(ctx, query18) // $ query=query18 +} + +func test3(db *sql.DB, ctx context.Context) { + stmt1, _ := db.Prepare(query21) // $ SPURIOUS: querystring=query21 + stmt1.Exec() // $ MISSING: query=query21 + stmt2, _ := db.PrepareContext(ctx, query22) // $ SPURIOUS: querystring=query22 + stmt2.ExecContext(ctx) // $ MISSING: query=query22 + stmt3, _ := db.Prepare(query23) // $ SPURIOUS: querystring=query23 + runQuery(stmt3) +} + +func runQuery(stmt *sql.Stmt) { + stmt.Exec() // $ MISSING: query=query23 +} + +func main() {} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/main.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/squirrel.go similarity index 59% rename from go/ql/test/library-tests/semmle/go/frameworks/SQL/main.go rename to go/ql/test/library-tests/semmle/go/frameworks/SQL/squirrel.go index 0af6e0028066..3629b8087cbf 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/main.go +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/squirrel.go @@ -3,45 +3,9 @@ package main //go:generate depstubber -vendor github.com/Masterminds/squirrel DeleteBuilder,InsertBuilder,SelectBuilder,UpdateBuilder Delete,Expr,Insert,Select,Update import ( - "context" - "database/sql" - "github.com/Masterminds/squirrel" ) -var ( - query1 string - query2 string - query3 string - query4 string - query5 string - query6 string - query7 string - query8 string - query11 string - query12 string - query13 string - query14 string - query15 string - query16 string - query17 string - query18 string - query21 string - query22 string - query23 string -) - -func test(db *sql.DB, ctx context.Context) { - db.Exec(query1) // $ query=query1 - db.ExecContext(ctx, query2) // $ query=query2 - db.Prepare(query3) // $ querystring=query3 - db.PrepareContext(ctx, query4) // $ querystring=query4 - db.Query(query5) // $ query=query5 - db.QueryContext(ctx, query6) // $ query=query6 - db.QueryRow(query7) // $ query=query7 - db.QueryRowContext(ctx, query8) // $ query=query8 -} - func squirrelTest(querypart string) { squirrel.Expr(querypart) // $ querystring=querypart deleteBuilder := squirrel.Delete(querypart) // $ querystring=querypart @@ -81,29 +45,3 @@ func squirrelTest(querypart string) { updateBuilder.Set(querypart, "") // $ querystring=querypart updateBuilder.Table(querypart) // $ querystring=querypart } - -func test2(tx *sql.Tx, query string, ctx context.Context) { - tx.Exec(query11) // $ query=query11 - tx.ExecContext(ctx, query12) // $ query=query12 - tx.Prepare(query13) // $ querystring=query13 - tx.PrepareContext(ctx, query14) // $ querystring=query14 - tx.Query(query15) // $ query=query15 - tx.QueryContext(ctx, query16) // $ query=query16 - tx.QueryRow(query17) // $ query=query17 - tx.QueryRowContext(ctx, query18) // $ query=query18 -} - -func test3(db *sql.DB, ctx context.Context) { - stmt1, _ := db.Prepare(query21) // $ SPURIOUS: querystring=query21 - stmt1.Exec() // $ MISSING: query=query21 - stmt2, _ := db.PrepareContext(ctx, query22) // $ SPURIOUS: querystring=query22 - stmt2.ExecContext(ctx) // $ MISSING: query=query22 - stmt3, _ := db.Prepare(query23) // $ SPURIOUS: querystring=query23 - runQuery(stmt3) -} - -func runQuery(stmt *sql.Stmt) { - stmt.Exec() // $ MISSING: query=query23 -} - -func main() {} From 652dd88c363c96c806d7517f39812749bbd2304b Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 21:35:07 +0100 Subject: [PATCH 12/16] Convert database/sql/driver sql-injection sinks to MaD --- go/ql/lib/ext/database.sql.driver.model.yml | 10 ++++++++ .../go/frameworks/stdlib/DatabaseSql.qll | 25 +------------------ 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/go/ql/lib/ext/database.sql.driver.model.yml b/go/ql/lib/ext/database.sql.driver.model.yml index c2d780bb7c86..50b699e4371f 100644 --- a/go/ql/lib/ext/database.sql.driver.model.yml +++ b/go/ql/lib/ext/database.sql.driver.model.yml @@ -1,4 +1,14 @@ extensions: + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["database/sql/driver", "Execer", False, "Exec", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql/driver", "ExecerContext", False, "ExecContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql/driver", "Conn", False, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql/driver", "ConnPrepareContext", False, "PrepareContext", "", "", "Argument[1]", "sql-injection", "manual"] + - ["database/sql/driver", "Queryer", False, "Query", "", "", "Argument[0]", "sql-injection", "manual"] + - ["database/sql/driver", "QueryerContext", False, "QueryContext", "", "", "Argument[1]", "sql-injection", "manual"] - addsTo: pack: codeql/go-all extensible: summaryModel diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll b/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll index 5f53ea2de8cd..f41326887961 100644 --- a/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll +++ b/go/ql/lib/semmle/go/frameworks/stdlib/DatabaseSql.qll @@ -60,36 +60,13 @@ module DatabaseSql { override DataFlow::Node getAResult() { result = this.getResult(0) } override SQL::QueryString getAQueryString() { - result = this.getAnArgument() + result = this.getASyntacticArgument() or this.getTarget().hasQualifiedName("database/sql/driver", "Stmt") and result = this.getReceiver().getAPredecessor*().(DataFlow::MethodCallNode).getAnArgument() } } - /** A query string used in an API function of the standard `database/sql/driver` package. */ - private class DriverQueryString extends SQL::QueryString::Range { - DriverQueryString() { - exists(Method meth, int n | - ( - meth.hasQualifiedName("database/sql/driver", "Execer", "Exec") and n = 0 - or - meth.hasQualifiedName("database/sql/driver", "ExecerContext", "ExecContext") and n = 1 - or - meth.hasQualifiedName("database/sql/driver", "Conn", "Prepare") and n = 0 - or - meth.hasQualifiedName("database/sql/driver", "ConnPrepareContext", "PrepareContext") and - n = 1 - or - meth.hasQualifiedName("database/sql/driver", "Queryer", "Query") and n = 0 - or - meth.hasQualifiedName("database/sql/driver", "QueryerContext", "QueryContext") and n = 1 - ) and - this = meth.getACall().getArgument(n) - ) - } - } - // These are expressed using TaintTracking::FunctionModel because varargs functions don't work with Models-as-Data sumamries yet. private class SqlMethodModels extends TaintTracking::FunctionModel, Method { FunctionInput inp; From ced000ae460f425697954eb238bc9a8c56cd64d7 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 16 Aug 2024 11:02:41 +0100 Subject: [PATCH 13/16] Add `Argument.getACorrespondingSyntacticArgument` --- go/ql/lib/semmle/go/Concepts.qll | 18 +++++----------- go/ql/lib/semmle/go/concepts/HTTP.qll | 8 +------ .../go/dataflow/internal/DataFlowNodes.qll | 21 ++++++++++++++----- go/ql/lib/semmle/go/frameworks/SQL.qll | 6 +----- .../go/frameworks/SystemCommandExecutors.qll | 6 +----- 5 files changed, 24 insertions(+), 35 deletions(-) diff --git a/go/ql/lib/semmle/go/Concepts.qll b/go/ql/lib/semmle/go/Concepts.qll index bb9ca12d0f71..8fd38a56e1c5 100644 --- a/go/ql/lib/semmle/go/Concepts.qll +++ b/go/ql/lib/semmle/go/Concepts.qll @@ -121,11 +121,7 @@ private class DefaultFileSystemAccess extends FileSystemAccess::Range, DataFlow: } override DataFlow::Node getAPathArgument() { - not pathArgument instanceof DataFlow::ImplicitVarargsSlice and - result = pathArgument - or - pathArgument instanceof DataFlow::ImplicitVarargsSlice and - result = this.getAnImplicitVarargsArgument() + result = pathArgument.getACorrespondingSyntacticArgument() } } @@ -378,19 +374,15 @@ module LoggerCall { } private class DefaultLoggerCall extends LoggerCall::Range, DataFlow::CallNode { - DataFlow::ArgumentNode messageComponent; + DataFlow::ArgumentNode messageArgument; DefaultLoggerCall() { - sinkNode(messageComponent, "log-injection") and - this = messageComponent.getCall() + sinkNode(messageArgument, "log-injection") and + this = messageArgument.getCall() } override DataFlow::Node getAMessageComponent() { - not messageComponent instanceof DataFlow::ImplicitVarargsSlice and - result = messageComponent - or - messageComponent instanceof DataFlow::ImplicitVarargsSlice and - result = this.getAnImplicitVarargsArgument() + result = messageArgument.getACorrespondingSyntacticArgument() } } diff --git a/go/ql/lib/semmle/go/concepts/HTTP.qll b/go/ql/lib/semmle/go/concepts/HTTP.qll index 50ded3fb59c5..9bf5b6a7ad84 100644 --- a/go/ql/lib/semmle/go/concepts/HTTP.qll +++ b/go/ql/lib/semmle/go/concepts/HTTP.qll @@ -332,13 +332,7 @@ module Http { ) } - override DataFlow::Node getUrl() { - not url instanceof DataFlow::ImplicitVarargsSlice and - result = url - or - url instanceof DataFlow::ImplicitVarargsSlice and - result = this.getAnImplicitVarargsArgument() - } + override DataFlow::Node getUrl() { result = url.getACorrespondingSyntacticArgument() } override Http::ResponseWriter getResponseWriter() { rw = -1 and result.getANode() = this.getReceiver() diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index 947491b44bbc..6b088f44e73a 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll @@ -841,11 +841,7 @@ module Public { or preupd = getAWrittenNode() or - ( - preupd instanceof ArgumentNode and not preupd instanceof ImplicitVarargsSlice - or - preupd = any(CallNode c).getAnImplicitVarargsArgument() - ) and + preupd = any(ArgumentNode arg).getACorrespondingSyntacticArgument() and mutableType(preupd.getType()) ) and ( @@ -889,6 +885,21 @@ module Public { * Gets this argument's position. */ int getPosition() { result = i } + + /** + * Gets a data-flow node for a syntactic argument corresponding this this + * argument. If this argument is not an implicit varargs slice then this + * will just be the argument itself. If this argument is an implicit + * varargs slice then this will be a data-flow node that for an argument + * that is stored in the implicit varargs slice. + */ + Node getACorrespondingSyntacticArgument() { + not this instanceof DataFlow::ImplicitVarargsSlice and + result = this + or + this instanceof DataFlow::ImplicitVarargsSlice and + result = c.getAnImplicitVarargsArgument() + } } /** diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index c2a550602e1c..b97df15a37d0 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -70,11 +70,7 @@ module SQL { private class DefaultQueryString extends Range { DefaultQueryString() { exists(DataFlow::ArgumentNode arg | sinkNode(arg, "sql-injection") | - not arg instanceof DataFlow::ImplicitVarargsSlice and - this = arg - or - arg instanceof DataFlow::ImplicitVarargsSlice and - this = arg.getCall().getAnImplicitVarargsArgument() + this = arg.getACorrespondingSyntacticArgument() ) } } diff --git a/go/ql/lib/semmle/go/frameworks/SystemCommandExecutors.qll b/go/ql/lib/semmle/go/frameworks/SystemCommandExecutors.qll index 7c7ab8c18351..1bd7054d5c3a 100644 --- a/go/ql/lib/semmle/go/frameworks/SystemCommandExecutors.qll +++ b/go/ql/lib/semmle/go/frameworks/SystemCommandExecutors.qll @@ -16,11 +16,7 @@ private class DefaultSystemCommandExecution extends SystemCommandExecution::Rang } override DataFlow::Node getCommandName() { - not commandName instanceof DataFlow::ImplicitVarargsSlice and - result = commandName - or - commandName instanceof DataFlow::ImplicitVarargsSlice and - result = this.getAnImplicitVarargsArgument() + result = commandName.getACorrespondingSyntacticArgument() } } From 86e9f15929fe5b362cc5be7759a8c98f882dc0e5 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 15 Aug 2024 21:52:15 +0100 Subject: [PATCH 14/16] Accept MaD sinks with kind `nosql-injection` --- go/ql/lib/semmle/go/frameworks/NoSQL.qll | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/go/ql/lib/semmle/go/frameworks/NoSQL.qll b/go/ql/lib/semmle/go/frameworks/NoSQL.qll index 9209a52fb99e..c2469fc02ac1 100644 --- a/go/ql/lib/semmle/go/frameworks/NoSQL.qll +++ b/go/ql/lib/semmle/go/frameworks/NoSQL.qll @@ -24,6 +24,14 @@ module NoSql { */ abstract class Range extends DataFlow::Node { } + private class DefaultQueryString extends Range { + DefaultQueryString() { + exists(DataFlow::ArgumentNode arg | sinkNode(arg, "nosql-injection") | + this = arg.getACorrespondingSyntacticArgument() + ) + } + } + /** * 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) From ec9d88b364b9f0ed8a1445386c08b0b4e1770843 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 16 Aug 2024 07:58:37 +0100 Subject: [PATCH 15/16] Convert mongodb nosql-injection sinks to MaD --- ...o.mongodb.org.mongo-driver.mongo.model.yml | 19 +++ go/ql/lib/semmle/go/frameworks/NoSQL.qll | 154 +++++++++--------- .../Security/CWE-089/SqlInjection.expected | 134 ++++++++------- 3 files changed, 170 insertions(+), 137 deletions(-) create mode 100644 go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml diff --git a/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml b/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml new file mode 100644 index 000000000000..ae07bd3a2139 --- /dev/null +++ b/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml @@ -0,0 +1,19 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "CountDocuments", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "DeleteMany", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "DeleteOne", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Distinct", "", "", "Argument[2]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Find", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "FindOne", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "FindOneAndDelete", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "FindOneAndReplace", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "FindOneAndUpdate", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "ReplaceOne", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "UpdateMany", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "UpdateOne", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Watch", "", "", "Argument[1]", "nosql-injection", "manual"] + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Aggregate", "", "", "Argument[1]", "nosql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/NoSQL.qll b/go/ql/lib/semmle/go/frameworks/NoSQL.qll index c2469fc02ac1..873b8230f1bf 100644 --- a/go/ql/lib/semmle/go/frameworks/NoSQL.qll +++ b/go/ql/lib/semmle/go/frameworks/NoSQL.qll @@ -31,84 +31,82 @@ module NoSql { ) } } - - /** - * 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(package("go.mongodb.org/mongo-driver", "mongo"), "Collection", - methodName) and - this = meth.getACall().getArgument(n) - ) - } - } + // /** + // * 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(package("go.mongodb.org/mongo-driver", "mongo"), "Collection", + // methodName) and + // this = meth.getACall().getArgument(n) + // ) + // } + // } } /** diff --git a/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected b/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected index 0da4f893d873..2741f6545e9b 100644 --- a/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected +++ b/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected @@ -25,53 +25,53 @@ | mongoDB.go:80:22:80:27 | filter | mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:80:22:80:27 | filter | This query depends on a $@. | mongoDB.go:40:20:40:30 | call to Referer | user-provided value | | mongoDB.go:81:18:81:25 | pipeline | mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:81:18:81:25 | pipeline | This query depends on a $@. | mongoDB.go:40:20:40:30 | call to Referer | user-provided value | edges -| SqlInjection.go:10:7:11:30 | []type{args} [array] | SqlInjection.go:10:7:11:30 | call to Sprintf | provenance | MaD:2 | -| SqlInjection.go:10:7:11:30 | call to Sprintf | SqlInjection.go:12:11:12:11 | q | provenance | | -| SqlInjection.go:11:3:11:9 | selection of URL | SqlInjection.go:11:3:11:17 | call to Query | provenance | Src:MaD:9 MaD:10 | +| SqlInjection.go:10:7:11:30 | []type{args} [array] | SqlInjection.go:10:7:11:30 | call to Sprintf | provenance | MaD:4 | +| SqlInjection.go:10:7:11:30 | call to Sprintf | SqlInjection.go:12:11:12:11 | q | provenance | Sink:MaD:1 | +| SqlInjection.go:11:3:11:9 | selection of URL | SqlInjection.go:11:3:11:17 | call to Query | provenance | Src:MaD:25 MaD:26 | | SqlInjection.go:11:3:11:17 | call to Query | SqlInjection.go:11:3:11:29 | index expression | provenance | | | SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | []type{args} [array] | provenance | | | SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | call to Sprintf | provenance | FunctionModel | | issue48.go:17:2:17:33 | ... := ...[0] | issue48.go:18:17:18:17 | b | provenance | | -| issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | ... := ...[0] | provenance | Src:MaD:6 MaD:3 | -| issue48.go:18:17:18:17 | b | issue48.go:18:20:18:39 | &... | provenance | MaD:1 | +| issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | ... := ...[0] | provenance | Src:MaD:22 MaD:19 | +| issue48.go:18:17:18:17 | b | issue48.go:18:20:18:39 | &... | provenance | MaD:3 | | issue48.go:18:20:18:39 | &... | issue48.go:21:3:21:33 | index expression | provenance | | -| issue48.go:20:8:21:34 | []type{args} [array] | issue48.go:20:8:21:34 | call to Sprintf | provenance | MaD:2 | -| issue48.go:20:8:21:34 | call to Sprintf | issue48.go:22:11:22:12 | q3 | provenance | | +| issue48.go:20:8:21:34 | []type{args} [array] | issue48.go:20:8:21:34 | call to Sprintf | provenance | MaD:4 | +| issue48.go:20:8:21:34 | call to Sprintf | issue48.go:22:11:22:12 | q3 | provenance | Sink:MaD:1 | | issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | []type{args} [array] | provenance | | | issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | call to Sprintf | provenance | FunctionModel | | issue48.go:27:2:27:34 | ... := ...[0] | issue48.go:28:17:28:18 | b2 | provenance | | -| issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | ... := ...[0] | provenance | Src:MaD:6 MaD:3 | -| issue48.go:28:17:28:18 | b2 | issue48.go:28:21:28:41 | &... | provenance | MaD:1 | +| issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | ... := ...[0] | provenance | Src:MaD:22 MaD:19 | +| issue48.go:28:17:28:18 | b2 | issue48.go:28:21:28:41 | &... | provenance | MaD:3 | | issue48.go:28:21:28:41 | &... | issue48.go:31:3:31:31 | selection of Category | provenance | | -| issue48.go:30:8:31:32 | []type{args} [array] | issue48.go:30:8:31:32 | call to Sprintf | provenance | MaD:2 | -| issue48.go:30:8:31:32 | call to Sprintf | issue48.go:32:11:32:12 | q4 | provenance | | +| issue48.go:30:8:31:32 | []type{args} [array] | issue48.go:30:8:31:32 | call to Sprintf | provenance | MaD:4 | +| issue48.go:30:8:31:32 | call to Sprintf | issue48.go:32:11:32:12 | q4 | provenance | Sink:MaD:1 | | issue48.go:31:3:31:31 | selection of Category | issue48.go:30:8:31:32 | []type{args} [array] | provenance | | | issue48.go:31:3:31:31 | selection of Category | issue48.go:30:8:31:32 | call to Sprintf | provenance | FunctionModel | -| issue48.go:37:17:37:50 | type conversion | issue48.go:37:53:37:73 | &... | provenance | MaD:1 | -| issue48.go:37:24:37:30 | selection of URL | issue48.go:37:24:37:38 | call to Query | provenance | Src:MaD:9 MaD:10 | +| issue48.go:37:17:37:50 | type conversion | issue48.go:37:53:37:73 | &... | provenance | MaD:3 | +| issue48.go:37:24:37:30 | selection of URL | issue48.go:37:24:37:38 | call to Query | provenance | Src:MaD:25 MaD:26 | | issue48.go:37:24:37:38 | call to Query | issue48.go:37:17:37:50 | type conversion | provenance | | | issue48.go:37:53:37:73 | &... | issue48.go:40:3:40:31 | selection of Category | provenance | | -| issue48.go:39:8:40:32 | []type{args} [array] | issue48.go:39:8:40:32 | call to Sprintf | provenance | MaD:2 | -| issue48.go:39:8:40:32 | call to Sprintf | issue48.go:41:11:41:12 | q5 | provenance | | +| issue48.go:39:8:40:32 | []type{args} [array] | issue48.go:39:8:40:32 | call to Sprintf | provenance | MaD:4 | +| issue48.go:39:8:40:32 | call to Sprintf | issue48.go:41:11:41:12 | q5 | provenance | Sink:MaD:1 | | issue48.go:40:3:40:31 | selection of Category | issue48.go:39:8:40:32 | []type{args} [array] | provenance | | | issue48.go:40:3:40:31 | selection of Category | issue48.go:39:8:40:32 | call to Sprintf | provenance | FunctionModel | -| main.go:11:11:11:16 | selection of Form | main.go:11:11:11:28 | index expression | provenance | Src:MaD:7 | -| main.go:15:11:15:84 | []type{args} [array] | main.go:15:11:15:84 | call to Sprintf | provenance | MaD:2 | -| main.go:15:63:15:67 | selection of URL | main.go:15:63:15:75 | call to Query | provenance | Src:MaD:9 MaD:10 | +| main.go:11:11:11:16 | selection of Form | main.go:11:11:11:28 | index expression | provenance | Src:MaD:23 Sink:MaD:1 | +| main.go:15:11:15:84 | []type{args} [array] | main.go:15:11:15:84 | call to Sprintf | provenance | MaD:4 Sink:MaD:2 | +| main.go:15:63:15:67 | selection of URL | main.go:15:63:15:75 | call to Query | provenance | Src:MaD:25 MaD:26 | | main.go:15:63:15:75 | call to Query | main.go:15:63:15:83 | index expression | provenance | | | main.go:15:63:15:83 | index expression | main.go:15:11:15:84 | []type{args} [array] | provenance | | -| main.go:15:63:15:83 | index expression | main.go:15:11:15:84 | call to Sprintf | provenance | FunctionModel | -| main.go:16:11:16:85 | []type{args} [array] | main.go:16:11:16:85 | call to Sprintf | provenance | MaD:2 | -| main.go:16:63:16:70 | selection of Header | main.go:16:63:16:84 | call to Get | provenance | Src:MaD:8 MaD:4 | +| main.go:15:63:15:83 | index expression | main.go:15:11:15:84 | call to Sprintf | provenance | FunctionModel Sink:MaD:2 | +| main.go:16:11:16:85 | []type{args} [array] | main.go:16:11:16:85 | call to Sprintf | provenance | MaD:4 Sink:MaD:2 | +| main.go:16:63:16:70 | selection of Header | main.go:16:63:16:84 | call to Get | provenance | Src:MaD:24 MaD:20 | | main.go:16:63:16:84 | call to Get | main.go:16:11:16:85 | []type{args} [array] | provenance | | -| main.go:16:63:16:84 | call to Get | main.go:16:11:16:85 | call to Sprintf | provenance | FunctionModel | +| main.go:16:63:16:84 | call to Get | main.go:16:11:16:85 | call to Sprintf | provenance | FunctionModel Sink:MaD:2 | | main.go:28:17:31:2 | &... [pointer, Category] | main.go:34:3:34:13 | RequestData [pointer, Category] | provenance | | | main.go:28:18:31:2 | struct literal [Category] | main.go:28:17:31:2 | &... [pointer, Category] | provenance | | -| main.go:30:13:30:19 | selection of URL | main.go:30:13:30:27 | call to Query | provenance | Src:MaD:9 MaD:10 | +| main.go:30:13:30:19 | selection of URL | main.go:30:13:30:27 | call to Query | provenance | Src:MaD:25 MaD:26 | | main.go:30:13:30:27 | call to Query | main.go:30:13:30:39 | index expression | provenance | | | main.go:30:13:30:39 | index expression | main.go:28:18:31:2 | struct literal [Category] | provenance | | -| main.go:33:7:34:23 | []type{args} [array] | main.go:33:7:34:23 | call to Sprintf | provenance | MaD:2 | -| main.go:33:7:34:23 | call to Sprintf | main.go:35:11:35:11 | q | provenance | | +| main.go:33:7:34:23 | []type{args} [array] | main.go:33:7:34:23 | call to Sprintf | provenance | MaD:4 | +| main.go:33:7:34:23 | call to Sprintf | main.go:35:11:35:11 | q | provenance | Sink:MaD:1 | | main.go:34:3:34:13 | RequestData [pointer, Category] | main.go:34:3:34:13 | implicit dereference [Category] | provenance | | | main.go:34:3:34:13 | implicit dereference [Category] | main.go:34:3:34:22 | selection of Category | provenance | | | main.go:34:3:34:22 | selection of Category | main.go:33:7:34:23 | []type{args} [array] | provenance | | @@ -80,11 +80,11 @@ edges | main.go:39:2:39:12 | definition of RequestData [pointer, Category] | main.go:43:3:43:13 | RequestData [pointer, Category] | provenance | | | main.go:40:2:40:12 | RequestData [pointer, Category] | main.go:40:2:40:12 | implicit dereference [Category] | provenance | | | main.go:40:2:40:12 | implicit dereference [Category] | main.go:39:2:39:12 | definition of RequestData [pointer, Category] | provenance | | -| main.go:40:25:40:31 | selection of URL | main.go:40:25:40:39 | call to Query | provenance | Src:MaD:9 MaD:10 | +| main.go:40:25:40:31 | selection of URL | main.go:40:25:40:39 | call to Query | provenance | Src:MaD:25 MaD:26 | | main.go:40:25:40:39 | call to Query | main.go:40:25:40:51 | index expression | provenance | | | main.go:40:25:40:51 | index expression | main.go:40:2:40:12 | implicit dereference [Category] | provenance | | -| main.go:42:7:43:23 | []type{args} [array] | main.go:42:7:43:23 | call to Sprintf | provenance | MaD:2 | -| main.go:42:7:43:23 | call to Sprintf | main.go:44:11:44:11 | q | provenance | | +| main.go:42:7:43:23 | []type{args} [array] | main.go:42:7:43:23 | call to Sprintf | provenance | MaD:4 | +| main.go:42:7:43:23 | call to Sprintf | main.go:44:11:44:11 | q | provenance | Sink:MaD:1 | | main.go:43:3:43:13 | RequestData [pointer, Category] | main.go:43:3:43:13 | implicit dereference [Category] | provenance | | | main.go:43:3:43:13 | implicit dereference [Category] | main.go:43:3:43:22 | selection of Category | provenance | | | main.go:43:3:43:22 | selection of Category | main.go:42:7:43:23 | []type{args} [array] | provenance | | @@ -93,11 +93,11 @@ edges | main.go:48:2:48:12 | definition of RequestData [pointer, Category] | main.go:52:3:52:13 | RequestData [pointer, Category] | provenance | | | main.go:49:3:49:14 | star expression [Category] | main.go:48:2:48:12 | definition of RequestData [pointer, Category] | provenance | | | main.go:49:4:49:14 | RequestData [pointer, Category] | main.go:49:3:49:14 | star expression [Category] | provenance | | -| main.go:49:28:49:34 | selection of URL | main.go:49:28:49:42 | call to Query | provenance | Src:MaD:9 MaD:10 | +| main.go:49:28:49:34 | selection of URL | main.go:49:28:49:42 | call to Query | provenance | Src:MaD:25 MaD:26 | | main.go:49:28:49:42 | call to Query | main.go:49:28:49:54 | index expression | provenance | | | main.go:49:28:49:54 | index expression | main.go:49:3:49:14 | star expression [Category] | provenance | | -| main.go:51:7:52:23 | []type{args} [array] | main.go:51:7:52:23 | call to Sprintf | provenance | MaD:2 | -| main.go:51:7:52:23 | call to Sprintf | main.go:53:11:53:11 | q | provenance | | +| main.go:51:7:52:23 | []type{args} [array] | main.go:51:7:52:23 | call to Sprintf | provenance | MaD:4 | +| main.go:51:7:52:23 | call to Sprintf | main.go:53:11:53:11 | q | provenance | Sink:MaD:1 | | main.go:52:3:52:13 | RequestData [pointer, Category] | main.go:52:3:52:13 | implicit dereference [Category] | provenance | | | main.go:52:3:52:13 | implicit dereference [Category] | main.go:52:3:52:22 | selection of Category | provenance | | | main.go:52:3:52:22 | selection of Category | main.go:51:7:52:23 | []type{args} [array] | provenance | | @@ -106,44 +106,60 @@ edges | main.go:57:2:57:12 | definition of RequestData [pointer, Category] | main.go:61:5:61:15 | RequestData [pointer, Category] | provenance | | | main.go:58:3:58:14 | star expression [Category] | main.go:57:2:57:12 | definition of RequestData [pointer, Category] | provenance | | | main.go:58:4:58:14 | RequestData [pointer, Category] | main.go:58:3:58:14 | star expression [Category] | provenance | | -| main.go:58:28:58:34 | selection of URL | main.go:58:28:58:42 | call to Query | provenance | Src:MaD:9 MaD:10 | +| main.go:58:28:58:34 | selection of URL | main.go:58:28:58:42 | call to Query | provenance | Src:MaD:25 MaD:26 | | main.go:58:28:58:42 | call to Query | main.go:58:28:58:54 | index expression | provenance | | | main.go:58:28:58:54 | index expression | main.go:58:3:58:14 | star expression [Category] | provenance | | -| main.go:60:7:61:26 | []type{args} [array] | main.go:60:7:61:26 | call to Sprintf | provenance | MaD:2 | -| main.go:60:7:61:26 | call to Sprintf | main.go:62:11:62:11 | q | provenance | | +| main.go:60:7:61:26 | []type{args} [array] | main.go:60:7:61:26 | call to Sprintf | provenance | MaD:4 | +| main.go:60:7:61:26 | call to Sprintf | main.go:62:11:62:11 | q | provenance | Sink:MaD:1 | | main.go:61:3:61:25 | selection of Category | main.go:60:7:61:26 | []type{args} [array] | provenance | | | main.go:61:3:61:25 | selection of Category | main.go:60:7:61:26 | call to Sprintf | provenance | FunctionModel | | main.go:61:4:61:15 | star expression [Category] | main.go:61:3:61:25 | selection of Category | provenance | | | main.go:61:5:61:15 | RequestData [pointer, Category] | main.go:61:4:61:15 | star expression [Category] | provenance | | -| mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:42:28:42:41 | untrustedInput | provenance | Src:MaD:5 | +| mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:42:28:42:41 | untrustedInput | provenance | Src:MaD:21 | | mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:50:34:50:39 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:61:27:61:32 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:63:23:63:28 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:64:22:64:27 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:66:32:66:37 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:69:17:69:22 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:70:20:70:25 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:71:29:71:34 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:72:30:72:35 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:73:29:73:34 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:78:23:78:28 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:79:23:79:28 | filter | provenance | | -| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:80:22:80:27 | filter | provenance | | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:61:27:61:32 | filter | provenance | Sink:MaD:6 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:63:23:63:28 | filter | provenance | Sink:MaD:7 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:64:22:64:27 | filter | provenance | Sink:MaD:8 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:66:32:66:37 | filter | provenance | Sink:MaD:9 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:69:17:69:22 | filter | provenance | Sink:MaD:10 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:70:20:70:25 | filter | provenance | Sink:MaD:11 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:71:29:71:34 | filter | provenance | Sink:MaD:12 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:72:30:72:35 | filter | provenance | Sink:MaD:13 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:73:29:73:34 | filter | provenance | Sink:MaD:14 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:78:23:78:28 | filter | provenance | Sink:MaD:15 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:79:23:79:28 | filter | provenance | Sink:MaD:16 | +| mongoDB.go:42:19:42:42 | struct literal | mongoDB.go:80:22:80:27 | filter | provenance | Sink:MaD:17 | | mongoDB.go:42:28:42:41 | untrustedInput | mongoDB.go:42:19:42:42 | struct literal | provenance | Config | -| mongoDB.go:50:23:50:40 | struct literal | mongoDB.go:57:22:57:29 | pipeline | provenance | | -| mongoDB.go:50:23:50:40 | struct literal | mongoDB.go:81:18:81:25 | pipeline | provenance | | +| mongoDB.go:50:23:50:40 | struct literal | mongoDB.go:57:22:57:29 | pipeline | provenance | Sink:MaD:5 | +| mongoDB.go:50:23:50:40 | struct literal | mongoDB.go:81:18:81:25 | pipeline | provenance | Sink:MaD:18 | | mongoDB.go:50:34:50:39 | filter | mongoDB.go:50:23:50:40 | struct literal | provenance | Config | models -| 1 | Summary: encoding/json; ; false; Unmarshal; ; ; Argument[0]; Argument[1]; taint; manual | -| 2 | Summary: fmt; ; true; Sprintf; ; ; Argument[1].ArrayElement; ReturnValue; taint; manual | -| 3 | Summary: io/ioutil; ; false; ReadAll; ; ; Argument[0]; ReturnValue[0]; taint; manual | -| 4 | Summary: net/http; Header; true; Get; ; ; Argument[receiver]; ReturnValue; taint; manual | -| 5 | Source: net/http; Request; true; Referer; ; ; ReturnValue; remote; manual | -| 6 | Source: net/http; Request; true; Body; ; ; ; remote; manual | -| 7 | Source: net/http; Request; true; Form; ; ; ; remote; manual | -| 8 | Source: net/http; Request; true; Header; ; ; ; remote; manual | -| 9 | Source: net/http; Request; true; URL; ; ; ; remote; manual | -| 10 | Summary: net/url; URL; true; Query; ; ; Argument[receiver]; ReturnValue; taint; manual | +| 1 | Sink: database/sql; DB; false; Query; ; ; Argument[0]; sql-injection; manual | +| 2 | Sink: database/sql; Tx; false; Query; ; ; Argument[0]; sql-injection; manual | +| 3 | Summary: encoding/json; ; false; Unmarshal; ; ; Argument[0]; Argument[1]; taint; manual | +| 4 | Summary: fmt; ; true; Sprintf; ; ; Argument[1].ArrayElement; ReturnValue; taint; manual | +| 5 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; Aggregate; ; ; Argument[1]; nosql-injection; manual | +| 6 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; CountDocuments; ; ; Argument[1]; nosql-injection; manual | +| 7 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; DeleteMany; ; ; Argument[1]; nosql-injection; manual | +| 8 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; DeleteOne; ; ; Argument[1]; nosql-injection; manual | +| 9 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; Distinct; ; ; Argument[2]; nosql-injection; manual | +| 10 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; Find; ; ; Argument[1]; nosql-injection; manual | +| 11 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; FindOne; ; ; Argument[1]; nosql-injection; manual | +| 12 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; FindOneAndDelete; ; ; Argument[1]; nosql-injection; manual | +| 13 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; FindOneAndReplace; ; ; Argument[1]; nosql-injection; manual | +| 14 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; FindOneAndUpdate; ; ; Argument[1]; nosql-injection; manual | +| 15 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; ReplaceOne; ; ; Argument[1]; nosql-injection; manual | +| 16 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; UpdateMany; ; ; Argument[1]; nosql-injection; manual | +| 17 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; UpdateOne; ; ; Argument[1]; nosql-injection; manual | +| 18 | Sink: go.mongodb.org/mongo-driver/mongo; Collection; true; Watch; ; ; Argument[1]; nosql-injection; manual | +| 19 | Summary: io/ioutil; ; false; ReadAll; ; ; Argument[0]; ReturnValue[0]; taint; manual | +| 20 | Summary: net/http; Header; true; Get; ; ; Argument[receiver]; ReturnValue; taint; manual | +| 21 | Source: net/http; Request; true; Referer; ; ; ReturnValue; remote; manual | +| 22 | Source: net/http; Request; true; Body; ; ; ; remote; manual | +| 23 | Source: net/http; Request; true; Form; ; ; ; remote; manual | +| 24 | Source: net/http; Request; true; Header; ; ; ; remote; manual | +| 25 | Source: net/http; Request; true; URL; ; ; ; remote; manual | +| 26 | Summary: net/url; URL; true; Query; ; ; Argument[receiver]; ReturnValue; taint; manual | nodes | SqlInjection.go:10:7:11:30 | []type{args} [array] | semmle.label | []type{args} [array] | | SqlInjection.go:10:7:11:30 | call to Sprintf | semmle.label | call to Sprintf | From 2d2afb17ad6d8277a802dc93429b12899ab67047 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 16 Aug 2024 07:59:04 +0100 Subject: [PATCH 16/16] Convert gocb nosql-injection sinks to MaD --- .../ext/github.com.couchbase.gocb.model.yml | 57 +++++++++----- ...o.mongodb.org.mongo-driver.mongo.model.yml | 2 +- go/ql/lib/semmle/go/frameworks/Couchbase.qll | 46 ++--------- go/ql/lib/semmle/go/frameworks/NoSQL.qll | 76 ------------------- 4 files changed, 43 insertions(+), 138 deletions(-) diff --git a/go/ql/lib/ext/github.com.couchbase.gocb.model.yml b/go/ql/lib/ext/github.com.couchbase.gocb.model.yml index ff0a4c22c8de..d17b53dd6da9 100644 --- a/go/ql/lib/ext/github.com.couchbase.gocb.model.yml +++ b/go/ql/lib/ext/github.com.couchbase.gocb.model.yml @@ -3,28 +3,43 @@ extensions: pack: codeql/go-all extensible: packageGrouping data: - - ["gocb", "github.com/couchbase/gocb"] - - ["gocb", "gopkg.in/couchbase/gocb"] - - ["gocb", "github.com/couchbaselabs/gocb"] + - ["gocb1", "fixed-version:github.com/couchbase/gocb"] + - ["gocb1", "fixed-version:gopkg.in/couchbase/gocb.v1"] + - ["gocb1", "fixed-version:github.com/couchbaselabs/gocb"] + - ["gocb2", "github.com/couchbase/gocb/v2"] + - ["gocb2", "gopkg.in/couchbase/gocb.v2"] + - ["gocb2", "github.com/couchbaselabs/gocb/v2"] + - addsTo: + pack: codeql/go-all + extensible: sinkModel + data: + - ["group:gocb1", "Bucket", True, "ExecuteN1qlQuery", "", "", "Argument[0]", "nosql-injection", "manual"] + - ["group:gocb1", "Bucket", True, "ExecuteAnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"] + - ["group:gocb1", "Cluster", True, "ExecuteN1qlQuery", "", "", "Argument[0]", "nosql-injection", "manual"] + - ["group:gocb1", "Cluster", True, "ExecuteAnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"] + - ["group:gocb2", "Cluster", True, "AnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"] + - ["group:gocb2", "Cluster", True, "Query", "", "", "Argument[0]", "nosql-injection", "manual"] + - ["group:gocb2", "Scope", True, "AnalyticsQuery", "", "", "Argument[0]", "nosql-injection", "manual"] + - ["group:gocb2", "Scope", True, "Query", "", "", "Argument[0]", "nosql-injection", "manual"] - addsTo: pack: codeql/go-all extensible: summaryModel data: - - ["group:gocb", "", False, "NewAnalyticsQuery", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "", False, "NewN1qlQuery", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "AnalyticsQuery", True, "ContextId", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "AnalyticsQuery", True, "Deferred", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "AnalyticsQuery", True, "Pretty", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "AnalyticsQuery", True, "Priority", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "AnalyticsQuery", True, "RawParam", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "AnalyticsQuery", True, "ServerSideTimeout", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "AdHoc", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "Consistency", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "ConsistentWith", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "Custom", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "PipelineBatch", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "PipelineCap", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "Profile", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "ReadOnly", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "ScanCap", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] - - ["group:gocb", "N1qlQuery", True, "Timeout", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "", False, "NewAnalyticsQuery", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "", False, "NewN1qlQuery", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "AnalyticsQuery", True, "ContextId", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "AnalyticsQuery", True, "Deferred", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "AnalyticsQuery", True, "Pretty", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "AnalyticsQuery", True, "Priority", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "AnalyticsQuery", True, "RawParam", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "AnalyticsQuery", True, "ServerSideTimeout", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "AdHoc", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "Consistency", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "ConsistentWith", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "Custom", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "PipelineBatch", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "PipelineCap", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "Profile", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "ReadOnly", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "ScanCap", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["group:gocb1", "N1qlQuery", True, "Timeout", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] diff --git a/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml b/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml index ae07bd3a2139..6c2d4afdeae2 100644 --- a/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml +++ b/go/ql/lib/ext/go.mongodb.org.mongo-driver.mongo.model.yml @@ -3,6 +3,7 @@ extensions: pack: codeql/go-all extensible: sinkModel data: + - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Aggregate", "", "", "Argument[1]", "nosql-injection", "manual"] - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "CountDocuments", "", "", "Argument[1]", "nosql-injection", "manual"] - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "DeleteMany", "", "", "Argument[1]", "nosql-injection", "manual"] - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "DeleteOne", "", "", "Argument[1]", "nosql-injection", "manual"] @@ -16,4 +17,3 @@ extensions: - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "UpdateMany", "", "", "Argument[1]", "nosql-injection", "manual"] - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "UpdateOne", "", "", "Argument[1]", "nosql-injection", "manual"] - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Watch", "", "", "Argument[1]", "nosql-injection", "manual"] - - ["go.mongodb.org/mongo-driver/mongo", "Collection", True, "Aggregate", "", "", "Argument[1]", "nosql-injection", "manual"] diff --git a/go/ql/lib/semmle/go/frameworks/Couchbase.qll b/go/ql/lib/semmle/go/frameworks/Couchbase.qll index 5eaa4d20c3ad..b5bfbcb22a2d 100644 --- a/go/ql/lib/semmle/go/frameworks/Couchbase.qll +++ b/go/ql/lib/semmle/go/frameworks/Couchbase.qll @@ -5,57 +5,23 @@ import go /** + * DEPRECATED + * * Provides models of commonly used functions in the official Couchbase Go SDK library. */ -module Couchbase { +deprecated module Couchbase { /** + * DEPRECATED + * * Gets a package path for the official Couchbase Go SDK library. * * Note that v1 and v2 have different APIs, but the names are disjoint so there is no need to * distinguish between them. */ - string packagePath() { + deprecated string packagePath() { result = package([ "gopkg.in/couchbase/gocb", "github.com/couchbase/gocb", "github.com/couchbaselabs/gocb" ], "") } - - /** - * A query used in an API function acting on a `Bucket` or `Cluster` struct of v1 of - * the official Couchbase Go library, gocb. - */ - private class CouchbaseV1Query extends NoSql::Query::Range { - CouchbaseV1Query() { - // func (b *Bucket) ExecuteAnalyticsQuery(q *AnalyticsQuery, params interface{}) (AnalyticsResults, error) - // func (b *Bucket) ExecuteN1qlQuery(q *N1qlQuery, params interface{}) (QueryResults, error) - // func (c *Cluster) ExecuteAnalyticsQuery(q *AnalyticsQuery, params interface{}) (AnalyticsResults, error) - // func (c *Cluster) ExecuteN1qlQuery(q *N1qlQuery, params interface{}) (QueryResults, error) - exists(Method meth, string structName, string methodName | - structName in ["Bucket", "Cluster"] and - methodName in ["ExecuteN1qlQuery", "ExecuteAnalyticsQuery"] and - meth.hasQualifiedName(packagePath(), structName, methodName) and - this = meth.getACall().getArgument(0) - ) - } - } - - /** - * A query used in an API function acting on a `Bucket` or `Cluster` struct of v1 of - * the official Couchbase Go library, gocb. - */ - private class CouchbaseV2Query extends NoSql::Query::Range { - CouchbaseV2Query() { - // func (c *Cluster) AnalyticsQuery(statement string, opts *AnalyticsOptions) (*AnalyticsResult, error) - // func (c *Cluster) Query(statement string, opts *QueryOptions) (*QueryResult, error) - // func (s *Scope) AnalyticsQuery(statement string, opts *AnalyticsOptions) (*AnalyticsResult, error) - // func (s *Scope) Query(statement string, opts *QueryOptions) (*QueryResult, error) - exists(Method meth, string structName, string methodName | - structName in ["Cluster", "Scope"] and - methodName in ["AnalyticsQuery", "Query"] and - meth.hasQualifiedName(packagePath(), structName, methodName) and - this = meth.getACall().getArgument(0) - ) - } - } } diff --git a/go/ql/lib/semmle/go/frameworks/NoSQL.qll b/go/ql/lib/semmle/go/frameworks/NoSQL.qll index 873b8230f1bf..36932149628e 100644 --- a/go/ql/lib/semmle/go/frameworks/NoSQL.qll +++ b/go/ql/lib/semmle/go/frameworks/NoSQL.qll @@ -31,82 +31,6 @@ module NoSql { ) } } - // /** - // * 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(package("go.mongodb.org/mongo-driver", "mongo"), "Collection", - // methodName) and - // this = meth.getACall().getArgument(n) - // ) - // } - // } } /**