mirror of
https://github.com/github/codeql.git
synced 2026-01-29 22:32:58 +01:00
Merge pull request #449 from owen-mc/model-couchbase-gocb
Model Couchbase Go library
This commit is contained in:
2
change-notes/2021-01-12-model-couchbase.md
Normal file
2
change-notes/2021-01-12-model-couchbase.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added support for [the offical Couchbase Go SDK library](https://github.com/couchbase/gocb), v1 and v2. The `go/sql-injection` query (which also handles non-SQL databases such as Couchbase) will now identify Couchbase queries built from untrusted external input.
|
||||
@@ -32,6 +32,7 @@ import semmle.go.dataflow.TaintTracking2
|
||||
import semmle.go.frameworks.Beego
|
||||
import semmle.go.frameworks.BeegoOrm
|
||||
import semmle.go.frameworks.Chi
|
||||
import semmle.go.frameworks.Couchbase
|
||||
import semmle.go.frameworks.Echo
|
||||
import semmle.go.frameworks.Email
|
||||
import semmle.go.frameworks.Encoding
|
||||
|
||||
99
ql/src/semmle/go/frameworks/Couchbase.qll
Normal file
99
ql/src/semmle/go/frameworks/Couchbase.qll
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Provides models of commonly used functions in the official Couchbase Go SDK library.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
/**
|
||||
* Provides models of commonly used functions in the official Couchbase Go SDK library.
|
||||
*/
|
||||
module Couchbase {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bindingset[result]
|
||||
string packagePath() {
|
||||
result =
|
||||
package([
|
||||
"gopkg.in/couchbase/gocb", "github.com/couchbase/gocb", "github.com/couchbaselabs/gocb"
|
||||
], "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Models of methods on `gocb/AnalyticsQuery` and `gocb/N1qlQuery` which which support a fluent
|
||||
* interface by returning the receiver. They are not inherently relevant to taint.
|
||||
*/
|
||||
private class QueryMethodV1 extends TaintTracking::FunctionModel, Method {
|
||||
QueryMethodV1() {
|
||||
exists(string queryTypeName, string methodName |
|
||||
queryTypeName = "AnalyticsQuery" and
|
||||
methodName in [
|
||||
"ContextId", "Deferred", "Pretty", "Priority", "RawParam", "ServerSideTimeout"
|
||||
]
|
||||
or
|
||||
queryTypeName = "N1qlQuery" and
|
||||
methodName in [
|
||||
"AdHoc", "Consistency", "ConsistentWith", "Custom", "PipelineBatch", "PipelineCap",
|
||||
"Profile", "ReadOnly", "ScanCap", "Timeout"
|
||||
]
|
||||
|
|
||||
this.hasQualifiedName(packagePath(), queryTypeName, methodName)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
|
||||
inp.isReceiver() and outp.isResult()
|
||||
}
|
||||
}
|
||||
|
||||
private class QueryFromN1qlStatementV1 extends TaintTracking::FunctionModel {
|
||||
QueryFromN1qlStatementV1() {
|
||||
this.hasQualifiedName(packagePath(), ["NewAnalyticsQuery", "NewN1qlQuery"])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
|
||||
inp.isParameter(0) and outp.isResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
go 1.14
|
||||
|
||||
module test
|
||||
|
||||
require (
|
||||
github.com/golang/snappy v0.0.2 // indirect
|
||||
github.com/google/uuid v1.1.4 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
gopkg.in/couchbase/gocb.v1 v1.6.7
|
||||
gopkg.in/couchbase/gocbcore.v7 v7.1.18 // indirect
|
||||
gopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4 // indirect
|
||||
gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4 // indirect
|
||||
gopkg.in/couchbaselabs/jsonx.v1 v1.0.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
package test
|
||||
|
||||
//go:generate depstubber -vendor gopkg.in/couchbase/gocb.v1 Bucket,Cluster NewAnalyticsQuery,NewN1qlQuery,QueryProfileNone,StatementPlus
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"gopkg.in/couchbase/gocb.v1"
|
||||
)
|
||||
|
||||
func analyticsQuery(bucket gocb.Bucket, untrustedSource *http.Request) {
|
||||
untrusted := untrustedSource.UserAgent()
|
||||
q0 := gocb.NewAnalyticsQuery(untrusted)
|
||||
q1 := q0.ContextId("")
|
||||
q2 := q1.Deferred(true)
|
||||
q3 := q2.Pretty(true)
|
||||
q4 := q3.Priority(true)
|
||||
q5 := q4.RawParam("name", nil)
|
||||
duration, _ := time.ParseDuration("300s")
|
||||
q6 := q5.ServerSideTimeout(duration)
|
||||
bucket.ExecuteAnalyticsQuery(q6, nil) // $sqlinjection=q6
|
||||
}
|
||||
|
||||
func n1qlQuery(cluster gocb.Cluster, untrustedSource *http.Request) {
|
||||
untrusted := untrustedSource.UserAgent()
|
||||
q0 := gocb.NewN1qlQuery(untrusted)
|
||||
q1 := q0.AdHoc(true)
|
||||
q2 := q1.Consistency(gocb.StatementPlus)
|
||||
q3 := q2.ConsistentWith(&gocb.MutationState{})
|
||||
q4 := q3.Custom("name", nil)
|
||||
q5 := q4.PipelineBatch(2)
|
||||
q6 := q5.PipelineCap(5)
|
||||
q7 := q6.Profile(gocb.QueryProfileNone)
|
||||
q8 := q7.ReadOnly(false)
|
||||
q9 := q8.ScanCap(10)
|
||||
duration, _ := time.ParseDuration("300s")
|
||||
q10 := q9.Timeout(duration)
|
||||
cluster.ExecuteN1qlQuery(q10, nil) // $sqlinjection=q10
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import go
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.go.security.SqlInjection
|
||||
|
||||
class SqlInjectionTest extends InlineExpectationsTest {
|
||||
SqlInjectionTest() { this = "SqlInjectionTest" }
|
||||
|
||||
override string getARelevantTag() { result = "sqlinjection" }
|
||||
|
||||
override predicate hasActualResult(string file, int line, string element, string tag, string value) {
|
||||
tag = "sqlinjection" and
|
||||
exists(DataFlow::Node sink | any(SqlInjection::Configuration c).hasFlow(_, sink) |
|
||||
element = sink.toString() and
|
||||
value = sink.toString() and
|
||||
sink.hasLocationInfo(file, line, _, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
1285
ql/test/library-tests/semmle/go/frameworks/CouchbaseV1/vendor/gopkg.in/couchbase/gocb.v1/stub.go
generated
vendored
Normal file
1285
ql/test/library-tests/semmle/go/frameworks/CouchbaseV1/vendor/gopkg.in/couchbase/gocb.v1/stub.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
33
ql/test/library-tests/semmle/go/frameworks/CouchbaseV1/vendor/modules.txt
vendored
Normal file
33
ql/test/library-tests/semmle/go/frameworks/CouchbaseV1/vendor/modules.txt
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# github.com/golang/snappy v0.0.2
|
||||
## explicit
|
||||
github.com/golang/snappy
|
||||
# github.com/google/uuid v1.1.4
|
||||
## explicit
|
||||
github.com/google/uuid
|
||||
# github.com/opentracing/opentracing-go v1.2.0
|
||||
## explicit
|
||||
github.com/opentracing/opentracing-go
|
||||
# github.com/pkg/errors v0.9.1
|
||||
## explicit
|
||||
github.com/pkg/errors
|
||||
# golang.org/x/net v0.0.0-20201224014010-6772e930b67b
|
||||
## explicit
|
||||
golang.org/x/net
|
||||
# golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
|
||||
## explicit
|
||||
golang.org/x/sync
|
||||
# gopkg.in/couchbase/gocb.v1 v1.6.7
|
||||
## explicit
|
||||
gopkg.in/couchbase/gocb.v1
|
||||
# gopkg.in/couchbase/gocbcore.v7 v7.1.18
|
||||
## explicit
|
||||
gopkg.in/couchbase/gocbcore.v7
|
||||
# gopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4
|
||||
## explicit
|
||||
gopkg.in/couchbaselabs/gocbconnstr.v1
|
||||
# gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4
|
||||
## explicit
|
||||
gopkg.in/couchbaselabs/gojcbmock.v1
|
||||
# gopkg.in/couchbaselabs/jsonx.v1 v1.0.0
|
||||
## explicit
|
||||
gopkg.in/couchbaselabs/jsonx.v1
|
||||
@@ -1,14 +0,0 @@
|
||||
| main.go:24:22:24:29 | pipeline |
|
||||
| main.go:27:27:27:32 | filter |
|
||||
| main.go:29:23:29:28 | filter |
|
||||
| main.go:30:22:30:27 | filter |
|
||||
| main.go:32:32:32:37 | filter |
|
||||
| main.go:35:17:35:22 | filter |
|
||||
| main.go:36:20:36:25 | filter |
|
||||
| main.go:37:29:37:34 | filter |
|
||||
| main.go:38:30:38:35 | filter |
|
||||
| main.go:39:29:39:34 | filter |
|
||||
| main.go:45:23:45:28 | filter |
|
||||
| main.go:47:23:47:28 | filter |
|
||||
| main.go:48:22:48:27 | filter |
|
||||
| main.go:49:18:49:25 | pipeline |
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
import go
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
select any(NoSQL::Query q)
|
||||
class NoSQLQueryTest extends InlineExpectationsTest {
|
||||
NoSQLQueryTest() { this = "NoSQLQueryTest" }
|
||||
|
||||
override string getARelevantTag() { result = "nosqlquery" }
|
||||
|
||||
override predicate hasActualResult(string file, int line, string element, string tag, string value) {
|
||||
exists(NoSQL::Query q |
|
||||
q.hasLocationInfo(file, line, _, _, _) and
|
||||
element = q.toString() and
|
||||
value = q.toString() and
|
||||
tag = "nosqlquery"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,14 @@ module main
|
||||
|
||||
go 1.14
|
||||
|
||||
require go.mongodb.org/mongo-driver v1.3.2
|
||||
require (
|
||||
github.com/couchbase/gocb/v2 v2.2.0
|
||||
github.com/google/uuid v1.1.4 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.3.2
|
||||
gopkg.in/couchbase/gocb.v1 v1.6.7
|
||||
gopkg.in/couchbase/gocbcore.v7 v7.1.18 // indirect
|
||||
gopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4 // indirect
|
||||
gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4 // indirect
|
||||
gopkg.in/couchbaselabs/jsonx.v1 v1.0.0 // indirect
|
||||
)
|
||||
|
||||
@@ -2,10 +2,15 @@ package main
|
||||
|
||||
//go:generate depstubber -vendor go.mongodb.org/mongo-driver/bson/primitive D
|
||||
//go:generate depstubber -vendor go.mongodb.org/mongo-driver/mongo Collection,Pipeline
|
||||
//go:generate depstubber -vendor gopkg.in/couchbase/gocb.v1 Bucket,Cluster
|
||||
//go:generate depstubber -vendor github.com/couchbase/gocb/v2 Cluster,Scope
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
gocbv2 "github.com/couchbase/gocb/v2"
|
||||
gocbv1 "gopkg.in/couchbase/gocb.v1"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
@@ -21,32 +26,46 @@ func test(coll *mongo.Collection, filter interface{}, models []mongo.WriteModel,
|
||||
matchStage := bson.D{{"$match", filter}}
|
||||
pipeline := mongo.Pipeline{matchStage}
|
||||
|
||||
coll.Aggregate(ctx, pipeline, nil)
|
||||
coll.Aggregate(ctx, pipeline, nil) // $nosqlquery=pipeline
|
||||
coll.BulkWrite(ctx, models, nil)
|
||||
coll.Clone(nil)
|
||||
coll.CountDocuments(ctx, filter, nil)
|
||||
coll.CountDocuments(ctx, filter, nil) // $nosqlquery=filter
|
||||
coll.Database()
|
||||
coll.DeleteMany(ctx, filter, nil)
|
||||
coll.DeleteOne(ctx, filter, nil)
|
||||
coll.DeleteMany(ctx, filter, nil) // $nosqlquery=filter
|
||||
coll.DeleteOne(ctx, filter, nil) // $nosqlquery=filter
|
||||
|
||||
coll.Distinct(ctx, fieldName, filter)
|
||||
coll.Distinct(ctx, fieldName, filter) // $nosqlquery=filter
|
||||
coll.Drop(ctx)
|
||||
coll.EstimatedDocumentCount(ctx, nil)
|
||||
coll.Find(ctx, filter, nil)
|
||||
coll.FindOne(ctx, filter, nil)
|
||||
coll.FindOneAndDelete(ctx, filter, nil)
|
||||
coll.FindOneAndReplace(ctx, filter, nil)
|
||||
coll.FindOneAndUpdate(ctx, filter, nil)
|
||||
coll.Find(ctx, filter, nil) // $nosqlquery=filter
|
||||
coll.FindOne(ctx, filter, nil) // $nosqlquery=filter
|
||||
coll.FindOneAndDelete(ctx, filter, nil) // $nosqlquery=filter
|
||||
coll.FindOneAndReplace(ctx, filter, nil) // $nosqlquery=filter
|
||||
coll.FindOneAndUpdate(ctx, filter, nil) // $nosqlquery=filter
|
||||
coll.Indexes()
|
||||
coll.InsertMany(ctx, documents)
|
||||
coll.InsertOne(ctx, document, nil)
|
||||
coll.Name()
|
||||
replacement := bson.D{{"location", "NYC"}}
|
||||
coll.ReplaceOne(ctx, filter, replacement)
|
||||
coll.ReplaceOne(ctx, filter, replacement) // $nosqlquery=filter
|
||||
update := bson.D{{"$inc", bson.D{{"age", 1}}}}
|
||||
coll.UpdateMany(ctx, filter, update)
|
||||
coll.UpdateOne(ctx, filter, update)
|
||||
coll.Watch(ctx, pipeline)
|
||||
coll.UpdateMany(ctx, filter, update) // $nosqlquery=filter
|
||||
coll.UpdateOne(ctx, filter, update) // $nosqlquery=filter
|
||||
coll.Watch(ctx, pipeline) // $nosqlquery=pipeline
|
||||
}
|
||||
|
||||
func testGocbV1(bucket gocbv1.Bucket, cluster gocbv1.Cluster, aq *gocbv1.AnalyticsQuery, nq *gocbv1.N1qlQuery) {
|
||||
bucket.ExecuteAnalyticsQuery(aq, nil) // $nosqlquery=aq
|
||||
cluster.ExecuteAnalyticsQuery(aq, nil) // $nosqlquery=aq
|
||||
bucket.ExecuteN1qlQuery(nq, nil) // $nosqlquery=nq
|
||||
cluster.ExecuteN1qlQuery(nq, nil) // $nosqlquery=nq
|
||||
}
|
||||
|
||||
func testGocbV2(cluster gocbv2.Cluster, scope gocbv2.Scope) {
|
||||
cluster.AnalyticsQuery("a", nil) // $nosqlquery="a"
|
||||
scope.AnalyticsQuery("b", nil) // $nosqlquery="b"
|
||||
cluster.Query("c", nil) // $nosqlquery="c"
|
||||
scope.Query("d", nil) // $nosqlquery="d"
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
1930
ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/github.com/couchbase/gocb/v2/stub.go
generated
vendored
Normal file
1930
ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/github.com/couchbase/gocb/v2/stub.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,6 @@
|
||||
// Package primitive is a stub of go.mongodb.org/mongo-driver/bson/primitive, generated by depstubber.
|
||||
package primitive
|
||||
|
||||
import ()
|
||||
|
||||
type D []E
|
||||
|
||||
func (_ D) Map() M {
|
||||
|
||||
1273
ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/gopkg.in/couchbase/gocb.v1/stub.go
generated
vendored
Normal file
1273
ql/test/library-tests/semmle/go/frameworks/NoSQL/vendor/gopkg.in/couchbase/gocb.v1/stub.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,27 @@
|
||||
# go.mongodb.org/mongo-driver v1.3.2
|
||||
## explicit
|
||||
go.mongodb.org/mongo-driver
|
||||
# github.com/couchbase/gocb/v2 v2.2.0
|
||||
## explicit
|
||||
github.com/couchbase/gocb/v2
|
||||
# github.com/google/uuid v1.1.4
|
||||
## explicit
|
||||
github.com/google/uuid
|
||||
# github.com/opentracing/opentracing-go v1.2.0
|
||||
## explicit
|
||||
github.com/opentracing/opentracing-go
|
||||
# gopkg.in/couchbase/gocb.v1 v1.6.7
|
||||
## explicit
|
||||
gopkg.in/couchbase/gocb.v1
|
||||
# gopkg.in/couchbase/gocbcore.v7 v7.1.18
|
||||
## explicit
|
||||
gopkg.in/couchbase/gocbcore.v7
|
||||
# gopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4
|
||||
## explicit
|
||||
gopkg.in/couchbaselabs/gocbconnstr.v1
|
||||
# gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4
|
||||
## explicit
|
||||
gopkg.in/couchbaselabs/gojcbmock.v1
|
||||
# gopkg.in/couchbaselabs/jsonx.v1 v1.0.0
|
||||
## explicit
|
||||
gopkg.in/couchbaselabs/jsonx.v1
|
||||
|
||||
Reference in New Issue
Block a user