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

Commit 4dfa9d5

Browse files
owen-mcsmowton
authored andcommitted
Model Revel
1 parent f4f29be commit 4dfa9d5

File tree

9 files changed

+1053
-0
lines changed

9 files changed

+1053
-0
lines changed

ql/src/go.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import semmle.go.frameworks.Macaron
3939
import semmle.go.frameworks.Mux
4040
import semmle.go.frameworks.NoSQL
4141
import semmle.go.frameworks.Protobuf
42+
import semmle.go.frameworks.Revel
4243
import semmle.go.frameworks.Spew
4344
import semmle.go.frameworks.SQL
4445
import semmle.go.frameworks.Stdlib
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* Provides classes for working with untrusted flow sources from the `github.com/revel/revel` package.
3+
*/
4+
5+
import go
6+
7+
module Revel {
8+
/** Gets the package name. */
9+
bindingset[result]
10+
string packagePath() { result = package(["github.com/revel", "github.com/robfig"], "revel") }
11+
12+
private class ControllerParams extends UntrustedFlowSource::Range, DataFlow::FieldReadNode {
13+
ControllerParams() {
14+
exists(Field f |
15+
this.readsField(_, f) and
16+
f.hasQualifiedName(packagePath(), "Controller", "Params")
17+
)
18+
}
19+
}
20+
21+
private class ParamsFixedSanitizer extends TaintTracking::DefaultTaintSanitizer,
22+
DataFlow::FieldReadNode {
23+
ParamsFixedSanitizer() {
24+
exists(Field f |
25+
this.readsField(_, f) and
26+
f.hasQualifiedName(packagePath(), "Params", "Fixed")
27+
)
28+
}
29+
}
30+
31+
private class ParamsBind extends TaintTracking::FunctionModel, Method {
32+
ParamsBind() { this.hasQualifiedName(packagePath(), "Params", ["Bind", "BindJSON"]) }
33+
34+
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
35+
inp.isReceiver() and outp.isParameter(0)
36+
}
37+
}
38+
39+
private class RouteMatchParams extends UntrustedFlowSource::Range, DataFlow::FieldReadNode {
40+
RouteMatchParams() {
41+
exists(Field f |
42+
this.readsField(_, f) and
43+
f.hasQualifiedName(packagePath(), "RouteMatch", "Params")
44+
)
45+
}
46+
}
47+
48+
/** An access to an HTTP request field whose value may be controlled by an untrusted user. */
49+
private class UserControlledRequestField extends UntrustedFlowSource::Range,
50+
DataFlow::FieldReadNode {
51+
UserControlledRequestField() {
52+
exists(string fieldName |
53+
this.getField().hasQualifiedName(packagePath(), "Request", fieldName)
54+
|
55+
fieldName in ["In", "Header", "URL", "Form", "MultipartForm"]
56+
)
57+
}
58+
}
59+
60+
private class UserControlledRequestMethod extends UntrustedFlowSource::Range,
61+
DataFlow::MethodCallNode {
62+
UserControlledRequestMethod() {
63+
this
64+
.getTarget()
65+
.hasQualifiedName(packagePath(), "Request",
66+
["FormValue", "PostFormValue", "GetQuery", "GetForm", "GetMultipartForm", "GetBody"])
67+
}
68+
}
69+
70+
private class ServerMultipartFormGetFiles extends TaintTracking::FunctionModel, Method {
71+
ServerMultipartFormGetFiles() {
72+
this.hasQualifiedName(packagePath(), "ServerMultipartForm", "GetFiles")
73+
}
74+
75+
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
76+
inp.isReceiver() and outp.isResult()
77+
}
78+
}
79+
80+
private class ServerMultipartFormGetValues extends TaintTracking::FunctionModel, Method {
81+
ServerMultipartFormGetValues() {
82+
this.hasQualifiedName(packagePath(), "ServerMultipartForm", "GetValues")
83+
}
84+
85+
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
86+
inp.isReceiver() and outp.isResult()
87+
}
88+
}
89+
90+
private class ServerRequestGet extends TaintTracking::FunctionModel, Method {
91+
ServerRequestGet() { this.hasQualifiedName(packagePath(), "ServerRequest", "Get") }
92+
93+
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
94+
inp.isReceiver() and outp.isResult(0)
95+
}
96+
}
97+
}

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,4 +271,28 @@ module WebSocketReader {
271271

272272
override FunctionOutput getAnOutput() { result.isResult(1) }
273273
}
274+
275+
/**
276+
* The `ServerWebSocket.MessageReceive` method of the `github.com/revel/revel` package.
277+
*/
278+
private class RevelServerWebSocketMessageReceive extends Range, Method {
279+
RevelServerWebSocketMessageReceive() {
280+
// func MessageReceive(v interface{}) error
281+
this.hasQualifiedName(Revel::packagePath(), "ServerWebSocket", "MessageReceive")
282+
}
283+
284+
override FunctionOutput getAnOutput() { result.isParameter(0) }
285+
}
286+
287+
/**
288+
* The `ServerWebSocket.MessageReceiveJSON` method of the `github.com/revel/revel` package.
289+
*/
290+
private class RevelServerWebSocketMessageReceiveJSON extends Range, Method {
291+
RevelServerWebSocketMessageReceiveJSON() {
292+
// func MessageReceiveJSON(v interface{}) error
293+
this.hasQualifiedName(Revel::packagePath(), "ServerWebSocket", "MessageReceiveJSON")
294+
}
295+
296+
override FunctionOutput getAnOutput() { result.isParameter(0) }
297+
}
274298
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package main
2+
3+
//go:generate depstubber -vendor github.com/revel/revel Controller,Params,Request,Router HTTP_QUERY
4+
5+
import (
6+
"io/ioutil"
7+
"mime/multipart"
8+
"net/url"
9+
10+
"github.com/revel/revel"
11+
)
12+
13+
func main() {}
14+
15+
type myAppController struct {
16+
*revel.Controller
17+
OtherStuff string
18+
}
19+
20+
type person struct {
21+
Name string `form:"name"`
22+
Address string `form:"address"`
23+
}
24+
25+
func useString(val string) {}
26+
27+
func useFiles(val *multipart.FileHeader) {}
28+
29+
func useJSON(val []byte) {}
30+
31+
func useURLValues(v url.Values) {
32+
useString(v["key"][0])
33+
useString(v.Get("key"))
34+
}
35+
36+
func usePerson(p person) {}
37+
38+
func (c myAppController) accessingParamsDirectlyIsUnsafe() {
39+
useString(c.Params.Get("key")) // NOT OK
40+
useURLValues(c.Params.Values) // NOT OK
41+
42+
val4 := ""
43+
c.Params.Bind(&val4, "key") // NOT OK
44+
useString(val4)
45+
46+
useString(c.Request.FormValue("key")) // NOT OK
47+
}
48+
49+
func (c myAppController) accessingFixedIsSafe(mainRouter *revel.Router) {
50+
useURLValues(c.Params.Fixed) // OK
51+
useString(mainRouter.Route(c.Request).FixedParams[0]) // OK
52+
}
53+
54+
func (c myAppController) accessingRouteIsUnsafe(mainRouter *revel.Router) {
55+
useURLValues(c.Params.Route) // NOT OK
56+
useURLValues(mainRouter.Route(c.Request).Params) // NOT OK
57+
}
58+
59+
func (c myAppController) accessingParamsQueryIsUnsafe() {
60+
useURLValues(c.Params.Query) // NOT OK
61+
}
62+
63+
func (c myAppController) accessingParamsFormIsUnsafe() {
64+
useURLValues(c.Params.Form) // NOT OK
65+
useString(c.Request.PostFormValue("key")) // NOT OK
66+
}
67+
68+
func (c myAppController) accessingParamsFilesIsUnsafe() {
69+
useFiles(c.Params.Files["key"][0]) // NOT OK
70+
}
71+
72+
func (c myAppController) accessingParamsJSONIsUnsafe() {
73+
useJSON(c.Params.JSON) // NOT OK
74+
75+
var val2 map[string]interface{}
76+
c.Params.BindJSON(&val2) // NOT OK
77+
useString(val2["name"].(string))
78+
}
79+
80+
func accessingRequestDirectlyIsUnsafe(c *revel.Controller) {
81+
useURLValues(c.Request.GetQuery()) // NOT OK
82+
useURLValues(c.Request.Form) // NOT OK
83+
useURLValues(c.Request.MultipartForm.Value) // NOT OK
84+
85+
form, _ := c.Request.GetForm() // NOT OK
86+
useURLValues(form)
87+
88+
smp1, _ := c.Request.GetMultipartForm() // NOT OK
89+
useURLValues(smp1.GetValues())
90+
91+
smp2, _ := c.Request.GetMultipartForm() // NOT OK
92+
useFiles(smp2.GetFiles()["key"][0])
93+
94+
useFiles(c.Request.MultipartForm.File["key"][0]) // NOT OK
95+
96+
json, _ := ioutil.ReadAll(c.Request.GetBody()) // NOT OK
97+
useJSON(json)
98+
}
99+
100+
func accessingServerRequest(c *revel.Controller) {
101+
query, _ := c.Request.In.Get(revel.HTTP_QUERY) // NOT OK
102+
useURLValues(query.(url.Values))
103+
104+
var message string
105+
c.Request.WebSocket.MessageReceive(&message) // NOT OK
106+
useString(message)
107+
108+
var p person
109+
c.Request.WebSocket.MessageReceiveJSON(&p) // NOT OK
110+
usePerson(p)
111+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
| Revel.go:39:12:39:19 | selection of Params : pointer type | Revel.go:39:12:39:30 | call to Get | 39 |
2+
| Revel.go:40:15:40:22 | selection of Params : pointer type | Revel.go:32:12:32:22 | index expression | 40 |
3+
| Revel.go:40:15:40:22 | selection of Params : pointer type | Revel.go:33:12:33:23 | call to Get | 40 |
4+
| Revel.go:43:2:43:9 | selection of Params : pointer type | Revel.go:44:12:44:15 | val4 | 43 |
5+
| Revel.go:46:12:46:37 | call to FormValue | Revel.go:46:12:46:37 | call to FormValue | 46 |
6+
| Revel.go:55:15:55:22 | selection of Params : pointer type | Revel.go:32:12:32:22 | index expression | 55 |
7+
| Revel.go:55:15:55:22 | selection of Params : pointer type | Revel.go:33:12:33:23 | call to Get | 55 |
8+
| Revel.go:56:15:56:48 | selection of Params : map type | Revel.go:32:12:32:22 | index expression | 56 |
9+
| Revel.go:56:15:56:48 | selection of Params : map type | Revel.go:33:12:33:23 | call to Get | 56 |
10+
| Revel.go:60:15:60:22 | selection of Params : pointer type | Revel.go:32:12:32:22 | index expression | 60 |
11+
| Revel.go:60:15:60:22 | selection of Params : pointer type | Revel.go:33:12:33:23 | call to Get | 60 |
12+
| Revel.go:64:15:64:22 | selection of Params : pointer type | Revel.go:32:12:32:22 | index expression | 64 |
13+
| Revel.go:64:15:64:22 | selection of Params : pointer type | Revel.go:33:12:33:23 | call to Get | 64 |
14+
| Revel.go:65:12:65:41 | call to PostFormValue | Revel.go:65:12:65:41 | call to PostFormValue | 65 |
15+
| Revel.go:69:11:69:18 | selection of Params : pointer type | Revel.go:69:11:69:34 | index expression | 69 |
16+
| Revel.go:73:10:73:17 | selection of Params : pointer type | Revel.go:73:10:73:22 | selection of JSON | 73 |
17+
| Revel.go:76:2:76:9 | selection of Params : pointer type | Revel.go:77:12:77:32 | type assertion | 76 |
18+
| Revel.go:81:15:81:34 | call to GetQuery : Values | Revel.go:32:12:32:22 | index expression | 81 |
19+
| Revel.go:81:15:81:34 | call to GetQuery : Values | Revel.go:33:12:33:23 | call to Get | 81 |
20+
| Revel.go:82:15:82:28 | selection of Form : Values | Revel.go:32:12:32:22 | index expression | 82 |
21+
| Revel.go:82:15:82:28 | selection of Form : Values | Revel.go:33:12:33:23 | call to Get | 82 |
22+
| Revel.go:83:15:83:37 | selection of MultipartForm : pointer type | Revel.go:32:12:32:22 | index expression | 83 |
23+
| Revel.go:83:15:83:37 | selection of MultipartForm : pointer type | Revel.go:33:12:33:23 | call to Get | 83 |
24+
| Revel.go:85:13:85:31 | call to GetForm : tuple type | Revel.go:32:12:32:22 | index expression | 85 |
25+
| Revel.go:85:13:85:31 | call to GetForm : tuple type | Revel.go:33:12:33:23 | call to Get | 85 |
26+
| Revel.go:88:13:88:40 | call to GetMultipartForm : tuple type | Revel.go:32:12:32:22 | index expression | 88 |
27+
| Revel.go:88:13:88:40 | call to GetMultipartForm : tuple type | Revel.go:33:12:33:23 | call to Get | 88 |
28+
| Revel.go:91:13:91:40 | call to GetMultipartForm : tuple type | Revel.go:92:11:92:35 | index expression | 91 |
29+
| Revel.go:94:11:94:33 | selection of MultipartForm : pointer type | Revel.go:94:11:94:48 | index expression | 94 |
30+
| Revel.go:96:28:96:46 | call to GetBody : Reader | Revel.go:97:10:97:13 | json | 96 |
31+
| Revel.go:101:14:101:25 | selection of In : ServerRequest | Revel.go:32:12:32:22 | index expression | 101 |
32+
| Revel.go:101:14:101:25 | selection of In : ServerRequest | Revel.go:33:12:33:23 | call to Get | 101 |
33+
| Revel.go:105:37:105:44 | &... : pointer type | Revel.go:106:12:106:18 | message | 105 |
34+
| Revel.go:109:41:109:42 | &... : pointer type | Revel.go:110:12:110:12 | p | 109 |
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import go
2+
3+
class SinkFunction extends Function {
4+
SinkFunction() { this.getName() = ["useFiles", "useJSON", "usePerson", "useString"] }
5+
}
6+
7+
class TestConfig extends TaintTracking::Configuration {
8+
TestConfig() { this = "testconfig" }
9+
10+
override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
11+
12+
override predicate isSink(DataFlow::Node sink) {
13+
sink = any(SinkFunction f).getACall().getAnArgument()
14+
}
15+
}
16+
17+
from TaintTracking::Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink, int i
18+
where config.hasFlowPath(source, sink) and source.hasLocationInfo(_, i, _, _, _)
19+
select source, sink, i order by i
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module codeql-go-tests/frameworks/Revel
2+
3+
go 1.14
4+
5+
require (
6+
github.com/fsnotify/fsnotify v1.4.9 // indirect
7+
github.com/github/depstubber v0.0.0-20200910105848-4f6b1e61222c // indirect
8+
github.com/go-stack/stack v1.8.0 // indirect
9+
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 // indirect
10+
github.com/mattn/go-colorable v0.1.7 // indirect
11+
github.com/revel/config v1.0.0 // indirect
12+
github.com/revel/log15 v2.11.20+incompatible // indirect
13+
github.com/revel/pathtree v0.0.0-20140121041023-41257a1839e9 // indirect
14+
github.com/revel/revel v1.0.0
15+
github.com/twinj/uuid v1.0.0 // indirect
16+
github.com/xeonx/timeago v1.0.0-rc4 // indirect
17+
golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
18+
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
19+
gopkg.in/stack.v0 v0.0.0-20141108040640-9b43fcefddd0 // indirect
20+
)

0 commit comments

Comments
 (0)