From 796a81a0bacebeca3e3d059cc043f0fab4ca0230 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Fri, 3 Jan 2025 22:25:10 -0500 Subject: [PATCH 1/8] gorm models --- go/ql/lib/ext/gorm.io.gorm.model.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/go/ql/lib/ext/gorm.io.gorm.model.yml b/go/ql/lib/ext/gorm.io.gorm.model.yml index bfcf1fa66a76..a72ab8453764 100644 --- a/go/ql/lib/ext/gorm.io.gorm.model.yml +++ b/go/ql/lib/ext/gorm.io.gorm.model.yml @@ -6,6 +6,21 @@ extensions: - ["gorm", "gorm.io/gorm"] - ["gorm", "github.com/jinzhu/gorm"] - ["gorm", "github.com/go-gorm/gorm"] + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["group:gorm", "ConnPool", True, "QueryContext", "", "", "ReturnValue", "manual"] + - ["group:gorm", "ConnPool", True, "QueryRowContext", "", "", "ReturnValue", "manual"] + - ["group:gorm", "DB", True, "Association", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "Find", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "FindInBatches", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "First", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "FirstOrCreate", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "FirstOrInit", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "Last", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "Scan", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "Take", "", "", "Argument[0]", "database", "manual"] - addsTo: pack: codeql/go-all extensible: sinkModel From 20d1ae1396a39c816f65a6524d206beaf43a4ca7 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Fri, 3 Jan 2025 22:35:24 -0500 Subject: [PATCH 2/8] Fix Association model --- go/ql/lib/ext/gorm.io.gorm.model.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/go/ql/lib/ext/gorm.io.gorm.model.yml b/go/ql/lib/ext/gorm.io.gorm.model.yml index a72ab8453764..a157d9d08995 100644 --- a/go/ql/lib/ext/gorm.io.gorm.model.yml +++ b/go/ql/lib/ext/gorm.io.gorm.model.yml @@ -10,9 +10,9 @@ extensions: pack: codeql/go-all extensible: sourceModel data: - - ["group:gorm", "ConnPool", True, "QueryContext", "", "", "ReturnValue", "manual"] - - ["group:gorm", "ConnPool", True, "QueryRowContext", "", "", "ReturnValue", "manual"] - - ["group:gorm", "DB", True, "Association", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "Association", True, "Find", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "ConnPool", True, "QueryContext", "", "", "ReturnValue[0]", "database", "manual"] + - ["group:gorm", "ConnPool", True, "QueryRowContext", "", "", "ReturnValue", "database", "manual"] - ["group:gorm", "DB", True, "Find", "", "", "Argument[0]", "database", "manual"] - ["group:gorm", "DB", True, "FindInBatches", "", "", "Argument[0]", "database", "manual"] - ["group:gorm", "DB", True, "First", "", "", "Argument[0]", "database", "manual"] From f38008e73de4352210bd81ad95ebaa8605f8cfd7 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Sun, 5 Jan 2025 22:07:20 -0500 Subject: [PATCH 3/8] Add test vendoring --- .../flowsources/local/database/go.mod | 7 +++ .../database/vendor/gorm.io/gorm/stub.go | 57 +++++++++++++++++++ .../local/database/vendor/modules.txt | 3 + 3 files changed, 67 insertions(+) create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/go.mod create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/gorm.io/gorm/stub.go create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/modules.txt diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/go.mod b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/go.mod new file mode 100644 index 000000000000..d9fb9e8f9870 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/go.mod @@ -0,0 +1,7 @@ +module test + +go 1.22.5 + +require ( + gorm.io/gorm v1.23.0 +) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/gorm.io/gorm/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/gorm.io/gorm/stub.go new file mode 100644 index 000000000000..e6170a2ed90b --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/gorm.io/gorm/stub.go @@ -0,0 +1,57 @@ +package gorm + +import ( + "context" + "database/sql" +) + +type DB struct{} + +func (db *DB) Find(dest interface{}, conds ...interface{}) *DB { + return db +} + +func (db *DB) FindInBatches(dest interface{}, batchSize int, fc func(tx *DB, batch int) error) *DB { + return db +} + +func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) *DB { + return db +} + +func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) *DB { + return db +} + +func (db *DB) First(dest interface{}, conds ...interface{}) *DB { + return db +} + +func (db *DB) Last(dest interface{}, conds ...interface{}) *DB { + return db +} + +func (db *DB) Take(dest interface{}, conds ...interface{}) *DB { + return db +} + +func (db *DB) Scan(dest interface{}) *DB { + return db +} + +type Association struct { + DB *DB +} + +func (a *Association) Find(dest interface{}) *Association { + return a +} + +type ConnPool interface { + PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) + ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) + QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) + QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row +} + +type Model interface{} diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/modules.txt b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/modules.txt new file mode 100644 index 000000000000..ff139a75895e --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/modules.txt @@ -0,0 +1,3 @@ +# gorm.io/gorm v1.23.0 +## explicit +gorm.io/gorm \ No newline at end of file From c7be77c1af9b868720f5f828e2a7031c629b72f8 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Sun, 5 Jan 2025 22:07:34 -0500 Subject: [PATCH 4/8] Add gorm tests --- .../flowsources/local/database/test_gorm.go | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/test_gorm.go diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/test_gorm.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/test_gorm.go new file mode 100644 index 000000000000..02dc1feee85b --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/test_gorm.go @@ -0,0 +1,45 @@ +package test + +import "gorm.io/gorm" + +type User struct{} + +// test querying an Association +func test_gorm_AssociationQuery(association *gorm.Association) { + association.Find(&User{}) // $ source +} + +// test querying a ConnPool +func test_gorm_ConnPoolQuery(connPool gorm.ConnPool) { + rows, err := connPool.QueryContext(nil, "SELECT * FROM users") // $ source + + if err != nil { + return + } + + defer rows.Close() + + userRow := connPool.QueryRowContext(nil, "SELECT * FROM users WHERE id = 1") // $ source + + ignore(userRow) +} + +// test querying a DB +func test_gorm_db(db *gorm.DB) { + db.Find(&User{}) // $ source + + db.FindInBatches(&User{}, 10, nil) // $ source + + db.FirstOrCreate(&User{}) // $ source + + db.FirstOrInit(&User{}) // $ source + + db.First(&User{}) // $ source + + db.Last(&User{}) // $ source + + db.Take(&User{}) // $ source + + db.Scan(&User{}) // $ source + +} From d28e03cda57e58a40f252517a5f345022e3fa1f8 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Sun, 5 Jan 2025 22:09:12 -0500 Subject: [PATCH 5/8] Change note --- go/ql/lib/change-notes/2025-01-05-gorm-database-sources.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 go/ql/lib/change-notes/2025-01-05-gorm-database-sources.md diff --git a/go/ql/lib/change-notes/2025-01-05-gorm-database-sources.md b/go/ql/lib/change-notes/2025-01-05-gorm-database-sources.md new file mode 100644 index 000000000000..a8ae4792feb5 --- /dev/null +++ b/go/ql/lib/change-notes/2025-01-05-gorm-database-sources.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Added `database` source models for database methods from the `gorm.io/gorm` package. +` From 4e5d2e27b1d2a89ba3388ae2faf0a2d33a4b2bee Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Tue, 7 Jan 2025 22:07:58 -0500 Subject: [PATCH 6/8] user.go for example type --- .../semmle/go/dataflow/flowsources/local/database/user.go | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/user.go diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/user.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/user.go new file mode 100644 index 000000000000..f5a03dbe8b12 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/user.go @@ -0,0 +1,3 @@ +package test + +type User struct{} From 55c6bea08b142fda7a7f1679e1ca2cf1903793d6 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Tue, 7 Jan 2025 22:08:21 -0500 Subject: [PATCH 7/8] Add missing models --- go/ql/lib/ext/gorm.io.gorm.model.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/go/ql/lib/ext/gorm.io.gorm.model.yml b/go/ql/lib/ext/gorm.io.gorm.model.yml index a157d9d08995..7cea07e18ff8 100644 --- a/go/ql/lib/ext/gorm.io.gorm.model.yml +++ b/go/ql/lib/ext/gorm.io.gorm.model.yml @@ -19,6 +19,10 @@ extensions: - ["group:gorm", "DB", True, "FirstOrCreate", "", "", "Argument[0]", "database", "manual"] - ["group:gorm", "DB", True, "FirstOrInit", "", "", "Argument[0]", "database", "manual"] - ["group:gorm", "DB", True, "Last", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "Model", "", "", "Argument[0]", "database", "manual"] + - ["group:gorm", "DB", True, "Pluck", "", "", "Argument[1]", "database", "manual"] + - ["group:gorm", "DB", True, "Row", "", "", "ReturnValue", "database", "manual"] + - ["group:gorm", "DB", True, "Rows", "", "", "ReturnValue[0]", "database", "manual"] - ["group:gorm", "DB", True, "Scan", "", "", "Argument[0]", "database", "manual"] - ["group:gorm", "DB", True, "Take", "", "", "Argument[0]", "database", "manual"] - addsTo: @@ -38,3 +42,8 @@ extensions: - ["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"] + - addsTo: + pack: codeql/go-all + extensible: summaryModel + data: + - ["group:gorm", "DB", True, "ScanRows", "", "", "Argument[0]", "Argument[1]", "taint", "manual"] From e7b0329d5d1755441d49c7ca02d0ac9218c1596c Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Tue, 7 Jan 2025 22:08:37 -0500 Subject: [PATCH 8/8] Test missing models --- .../flowsources/local/database/test_gorm.go | 18 +++++++++++++++-- .../database/vendor/gorm.io/gorm/stub.go | 20 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/test_gorm.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/test_gorm.go index 02dc1feee85b..9fc1de0de4ef 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/test_gorm.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/test_gorm.go @@ -2,8 +2,6 @@ package test import "gorm.io/gorm" -type User struct{} - // test querying an Association func test_gorm_AssociationQuery(association *gorm.Association) { association.Find(&User{}) // $ source @@ -42,4 +40,20 @@ func test_gorm_db(db *gorm.DB) { db.Scan(&User{}) // $ source + var user User + db.Model(&user) // $ source + + row := db.Row() // $ source + ignore(row) + + rows, err := db.Rows() // $ source + ignore(err) + + var user2 User + db.ScanRows(rows, &user2) + + sink(user2) // $ hasTaintFlow="user2" + + var names []string + db.Pluck("name", &names) // $ source } diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/gorm.io/gorm/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/gorm.io/gorm/stub.go index e6170a2ed90b..d17686106452 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/gorm.io/gorm/stub.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/database/vendor/gorm.io/gorm/stub.go @@ -27,10 +27,18 @@ func (db *DB) First(dest interface{}, conds ...interface{}) *DB { return db } +func (db *DB) Model(value interface{}) *DB { + return db +} + func (db *DB) Last(dest interface{}, conds ...interface{}) *DB { return db } +func (db *DB) Pluck(column string, dest interface{}) *DB { + return db +} + func (db *DB) Take(dest interface{}, conds ...interface{}) *DB { return db } @@ -39,6 +47,18 @@ func (db *DB) Scan(dest interface{}) *DB { return db } +func (db *DB) ScanRows(rows *sql.Rows, result interface{}) error { + return nil +} + +func (db *DB) Row() *sql.Row { + return nil +} + +func (db *DB) Rows() (*sql.Rows, error) { + return nil, nil +} + type Association struct { DB *DB }