Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit 2831ffd

Browse files
authored
Merge pull request #270 from smowton/smowton/cleanup/ricterz-libraries
Add support for Gorm, Gorestful, Sqlx and Json-iterator
2 parents f8b8af5 + 026dc5c commit 2831ffd

File tree

33 files changed

+2158
-0
lines changed

33 files changed

+2158
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
lgtm,codescanning
2+
* Basic support for the [Go-restful](https://github.com/emicklei/go-restful) HTTP library has been added, which
3+
may lead to more results from the security queries.
4+
* Basic support for the [Gorm](https://github.com/go-gorm/gorm) ORM library has been added (specifically, its SQL statement building facilities), which
5+
may lead to more results from the security queries.
6+
* Basic support for the [Sqlx](https://github.com/jmoiron/sqlx) database access library has been added, which
7+
may lead to more results from the security queries.
8+
* Basic support for the [Json-iterator](https://github.com/json-iterator/go) JSON library has been added, which
9+
may lead to more results from the security queries.
10+
11+

ql/src/go.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import semmle.go.dataflow.GlobalValueNumbering
2626
import semmle.go.dataflow.SSA
2727
import semmle.go.dataflow.TaintTracking
2828
import semmle.go.frameworks.Email
29+
import semmle.go.frameworks.Encoding
2930
import semmle.go.frameworks.Glog
3031
import semmle.go.frameworks.HTTP
3132
import semmle.go.frameworks.Macaron
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Provides classes modelling taint propagation through marshalling and encoding functions.
3+
*/
4+
5+
import go
6+
7+
/** A model of json-iterator's `Unmarshal` function, propagating taint from the JSON input to the decoded object. */
8+
private class JsonIteratorUnmarshalFunction extends TaintTracking::FunctionModel,
9+
UnmarshalingFunction::Range {
10+
JsonIteratorUnmarshalFunction() {
11+
this.hasQualifiedName("github.com/json-iterator/go", ["Unmarshal", "UnmarshalFromString"])
12+
or
13+
exists(Method m |
14+
m.hasQualifiedName("github.com/json-iterator/go", "API", ["Unmarshal", "UnmarshalFromString"]) and
15+
this.(Method).implements(m)
16+
)
17+
}
18+
19+
override DataFlow::FunctionInput getAnInput() { result.isParameter(0) }
20+
21+
override DataFlow::FunctionOutput getOutput() { result.isParameter(1) }
22+
23+
override string getFormat() { result = "JSON" }
24+
25+
override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) {
26+
inp = getAnInput() and outp = getOutput()
27+
}
28+
}

ql/src/semmle/go/frameworks/HTTP.qll

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,42 @@ private module StdlibHttp {
231231
}
232232
}
233233
}
234+
235+
/**
236+
* Provides models of the go-restful library (https://github.com/emicklei/go-restful).
237+
*/
238+
private module GoRestfulHttp {
239+
/**
240+
* A model for methods defined on go-restful's `Request` object that may return user-controlled data.
241+
*/
242+
private class GoRestfulSourceMethod extends Method {
243+
GoRestfulSourceMethod() {
244+
this
245+
.hasQualifiedName(package("github.com/emicklei/go-restful", ""), "Request",
246+
["QueryParameters", "QueryParameter", "BodyParameter", "HeaderParameter",
247+
"PathParameter", "PathParameters"])
248+
}
249+
}
250+
251+
/**
252+
* A model of go-restful's `Request` object as a source of user-controlled data.
253+
*/
254+
private class GoRestfulSource extends UntrustedFlowSource::Range {
255+
GoRestfulSource() { this = any(GoRestfulSourceMethod g).getACall() }
256+
}
257+
258+
/**
259+
* A model of go-restful's `Request.ReadEntity` method as a source of user-controlled data.
260+
*/
261+
private class GoRestfulReadEntitySource extends UntrustedFlowSource::Range {
262+
GoRestfulReadEntitySource() {
263+
exists(DataFlow::MethodCallNode call |
264+
call
265+
.getTarget()
266+
.hasQualifiedName(package("github.com/emicklei/go-restful", ""), "Request", "ReadEntity")
267+
|
268+
this = any(FunctionOutput output | output.isParameter(0)).getExitNode(call)
269+
)
270+
}
271+
}
272+
}

ql/src/semmle/go/frameworks/SQL.qll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,29 @@ module SQL {
160160
}
161161
}
162162
}
163+
164+
/** A model for sinks of github.com/jinzhu/gorm. */
165+
private class GormSink extends SQL::QueryString::Range {
166+
GormSink() {
167+
exists(Method meth, string name |
168+
meth.hasQualifiedName("github.com/jinzhu/gorm", "DB", name) and
169+
this = meth.getACall().getArgument(0) and
170+
name in ["Where", "Raw", "Order", "Not", "Or", "Select", "Table", "Group", "Having", "Joins"]
171+
)
172+
}
173+
}
174+
175+
/** A model for sinks of github.com/jmoiron/sqlx. */
176+
private class SqlxSink extends SQL::QueryString::Range {
177+
SqlxSink() {
178+
exists(Method meth, string name, int n |
179+
meth.hasQualifiedName("github.com/jmoiron/sqlx", ["DB", "Tx"], name) and
180+
this = meth.getACall().getArgument(n)
181+
|
182+
name = ["Select", "Get"] and n = 1
183+
or
184+
name = ["MustExec", "Queryx", "NamedExec", "NamedQuery"] and n = 0
185+
)
186+
}
187+
}
163188
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module jsonittest
2+
3+
go 1.14
4+
5+
require (
6+
github.com/json-iterator/go v1.1.10
7+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| jsoniter.go:28:15:28:24 | selection of field | jsoniter.go:23:20:23:38 | call to getUntrustedBytes : slice type | jsoniter.go:28:15:28:24 | selection of field | This command depends on $@. | jsoniter.go:23:20:23:38 | call to getUntrustedBytes | a user-provided value |
2+
| jsoniter.go:32:15:32:25 | selection of field | jsoniter.go:23:20:23:38 | call to getUntrustedBytes : slice type | jsoniter.go:32:15:32:25 | selection of field | This command depends on $@. | jsoniter.go:23:20:23:38 | call to getUntrustedBytes | a user-provided value |
3+
| jsoniter.go:36:15:36:25 | selection of field | jsoniter.go:24:21:24:40 | call to getUntrustedString : string | jsoniter.go:36:15:36:25 | selection of field | This command depends on $@. | jsoniter.go:24:21:24:40 | call to getUntrustedString | a user-provided value |
4+
| jsoniter.go:40:15:40:25 | selection of field | jsoniter.go:24:21:24:40 | call to getUntrustedString : string | jsoniter.go:40:15:40:25 | selection of field | This command depends on $@. | jsoniter.go:24:21:24:40 | call to getUntrustedString | a user-provided value |
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package jsonittest
2+
3+
import (
4+
jsoniter "github.com/json-iterator/go"
5+
"os/exec"
6+
)
7+
8+
func getUntrustedString() string {
9+
return "trouble"
10+
}
11+
12+
func getUntrustedBytes() []byte {
13+
return []byte{}
14+
}
15+
16+
type myData struct {
17+
field string
18+
}
19+
20+
func main() {
21+
22+
var json = jsoniter.ExampleConfig{}
23+
untrustedInput := getUntrustedBytes()
24+
untrustedString := getUntrustedString()
25+
26+
data := myData{}
27+
json.Unmarshal(untrustedInput, &data)
28+
exec.Command(data.field)
29+
30+
data2 := myData{}
31+
jsoniter.Unmarshal(untrustedInput, &data2)
32+
exec.Command(data2.field)
33+
34+
data3 := myData{}
35+
json.UnmarshalFromString(untrustedString, &data3)
36+
exec.Command(data3.field)
37+
38+
data4 := myData{}
39+
jsoniter.UnmarshalFromString(untrustedString, &data4)
40+
exec.Command(data4.field)
41+
42+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import go
2+
import semmle.go.security.CommandInjection
3+
4+
class UntrustedFunction extends Function {
5+
UntrustedFunction() { this.getName() = ["getUntrustedString", "getUntrustedBytes"] }
6+
}
7+
8+
class UntrustedSource extends DataFlow::Node, UntrustedFlowSource::Range {
9+
UntrustedSource() { this = any(UntrustedFunction f).getACall() }
10+
}
11+
12+
from CommandInjection::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
13+
where cfg.hasFlowPath(source, sink)
14+
select sink.getNode(), source, sink, "This command depends on $@.", source.getNode(),
15+
"a user-provided value"

ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/LICENSE

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)