Skip to content

Commit 46b5f11

Browse files
authored
Merge pull request #438 from gagliardetto/clevergo
Pilot #0: Add web framework `clevergo`
2 parents 7bf5abf + 8e839f3 commit 46b5f11

21 files changed

+1380
-0
lines changed
Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
/**
2+
* Provides classes for working with concepts from the [`clevergo.tech/[email protected]`](https://pkg.go.dev/clevergo.tech/[email protected]) package.
3+
*/
4+
5+
import go
6+
7+
/**
8+
* Provides classes for working with concepts from the [`clevergo.tech/[email protected]`](https://pkg.go.dev/clevergo.tech/[email protected]) package.
9+
*/
10+
private module CleverGo {
11+
/** Gets the package path. */
12+
bindingset[result]
13+
string packagePath() {
14+
result = package(["clevergo.tech/clevergo", "github.com/clevergo/clevergo"], "")
15+
}
16+
17+
/**
18+
* Provides models of untrusted flow sources.
19+
*/
20+
private class UntrustedSources extends UntrustedFlowSource::Range {
21+
UntrustedSources() {
22+
// Methods on types of package: clevergo.tech/[email protected]
23+
exists(string receiverName, string methodName, Method mtd, FunctionOutput out |
24+
this = out.getExitNode(mtd.getACall()) and
25+
mtd.hasQualifiedName(packagePath(), receiverName, methodName)
26+
|
27+
receiverName = "Context" and
28+
(
29+
// signature: func (*Context).BasicAuth() (username string, password string, ok bool)
30+
methodName = "BasicAuth" and
31+
out.isResult([0, 1])
32+
or
33+
// signature: func (*Context).Decode(v interface{}) (err error)
34+
methodName = "Decode" and
35+
out.isParameter(0)
36+
or
37+
// signature: func (*Context).DefaultQuery(key string, defaultVlue string) string
38+
methodName = "DefaultQuery" and
39+
out.isResult()
40+
or
41+
// signature: func (*Context).FormValue(key string) string
42+
methodName = "FormValue" and
43+
out.isResult()
44+
or
45+
// signature: func (*Context).GetHeader(name string) string
46+
methodName = "GetHeader" and
47+
out.isResult()
48+
or
49+
// signature: func (*Context).PostFormValue(key string) string
50+
methodName = "PostFormValue" and
51+
out.isResult()
52+
or
53+
// signature: func (*Context).QueryParam(key string) string
54+
methodName = "QueryParam" and
55+
out.isResult()
56+
or
57+
// signature: func (*Context).QueryParams() net/url.Values
58+
methodName = "QueryParams" and
59+
out.isResult()
60+
or
61+
// signature: func (*Context).QueryString() string
62+
methodName = "QueryString" and
63+
out.isResult()
64+
)
65+
or
66+
receiverName = "Params" and
67+
(
68+
// signature: func (Params).String(name string) string
69+
methodName = "String" and
70+
out.isResult()
71+
)
72+
)
73+
or
74+
// Interfaces of package: clevergo.tech/[email protected]
75+
exists(string interfaceName, string methodName, Method mtd, FunctionOutput out |
76+
this = out.getExitNode(mtd.getACall()) and
77+
mtd.implements(packagePath(), interfaceName, methodName)
78+
|
79+
interfaceName = "Decoder" and
80+
(
81+
// signature: func (Decoder).Decode(req *net/http.Request, v interface{}) error
82+
methodName = "Decode" and
83+
out.isParameter(1)
84+
)
85+
)
86+
or
87+
// Structs of package: clevergo.tech/[email protected]
88+
exists(string structName, string fields, DataFlow::Field fld |
89+
this = fld.getARead() and
90+
fld.hasQualifiedName(packagePath(), structName, fields)
91+
|
92+
structName = "Context" and
93+
fields = "Params"
94+
or
95+
structName = "Param" and
96+
fields = ["Key", "Value"]
97+
)
98+
or
99+
// Types of package: clevergo.tech/[email protected]
100+
exists(ValueEntity v | v.getType().hasQualifiedName(packagePath(), "Params") |
101+
this = v.getARead()
102+
)
103+
}
104+
}
105+
106+
/**
107+
* Models taint-tracking through functions.
108+
*/
109+
private class TaintTrackingFunctionModels extends TaintTracking::FunctionModel {
110+
FunctionInput inp;
111+
FunctionOutput out;
112+
113+
TaintTrackingFunctionModels() {
114+
// Taint-tracking models for package: clevergo.tech/[email protected]
115+
(
116+
// signature: func CleanPath(p string) string
117+
this.hasQualifiedName(packagePath(), "CleanPath") and
118+
inp.isParameter(0) and
119+
out.isResult()
120+
)
121+
}
122+
123+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
124+
input = inp and output = out
125+
}
126+
}
127+
128+
/**
129+
* Models taint-tracking through method calls.
130+
*/
131+
private class TaintTrackingMethodModels extends TaintTracking::FunctionModel, Method {
132+
FunctionInput inp;
133+
FunctionOutput out;
134+
135+
TaintTrackingMethodModels() {
136+
// Taint-tracking models for package: clevergo.tech/[email protected]
137+
(
138+
// Receiver type: Application
139+
// signature: func (*Application).RouteURL(name string, args ...string) (*net/url.URL, error)
140+
this.hasQualifiedName(packagePath(), "Application", "RouteURL") and
141+
inp.isParameter(_) and
142+
out.isResult(0)
143+
or
144+
// Receiver type: Context
145+
// signature: func (*Context).Context() context.Context
146+
this.hasQualifiedName(packagePath(), "Context", "Context") and
147+
inp.isReceiver() and
148+
out.isResult()
149+
or
150+
// Receiver type: Params
151+
// signature: func (Params).String(name string) string
152+
this.hasQualifiedName(packagePath(), "Params", "String") and
153+
inp.isReceiver() and
154+
out.isResult()
155+
or
156+
// Receiver interface: Decoder
157+
// signature: func (Decoder).Decode(req *net/http.Request, v interface{}) error
158+
this.implements(packagePath(), "Decoder", "Decode") and
159+
inp.isParameter(0) and
160+
out.isParameter(1)
161+
or
162+
// Receiver interface: Renderer
163+
// signature: func (Renderer).Render(w io.Writer, name string, data interface{}, c *Context) error
164+
this.implements(packagePath(), "Renderer", "Render") and
165+
inp.isParameter(2) and
166+
out.isParameter(0)
167+
)
168+
}
169+
170+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
171+
input = inp and output = out
172+
}
173+
}
174+
175+
/**
176+
* Models HTTP redirects.
177+
*/
178+
private class HttpRedirect extends HTTP::Redirect::Range, DataFlow::CallNode {
179+
string package;
180+
DataFlow::Node urlNode;
181+
182+
HttpRedirect() {
183+
// HTTP redirect models for package: clevergo.tech/[email protected]
184+
package = packagePath() and
185+
// Receiver type: Context
186+
(
187+
// signature: func (*Context).Redirect(code int, url string) error
188+
this = any(Method m | m.hasQualifiedName(package, "Context", "Redirect")).getACall() and
189+
urlNode = this.getArgument(1)
190+
)
191+
}
192+
193+
override DataFlow::Node getUrl() { result = urlNode }
194+
195+
override HTTP::ResponseWriter getResponseWriter() { none() }
196+
}
197+
198+
/**
199+
* Models HTTP ResponseBody.
200+
*/
201+
private class HttpResponseBody extends HTTP::ResponseBody::Range {
202+
string package;
203+
DataFlow::CallNode bodySetterCall;
204+
string contentType;
205+
206+
HttpResponseBody() {
207+
// HTTP ResponseBody models for package: clevergo.tech/[email protected]
208+
package = packagePath() and
209+
(
210+
// One call sets both body and content-type (which is implicit in the func name).
211+
// Receiver type: Context
212+
exists(string methodName, Method m |
213+
m.hasQualifiedName(package, "Context", methodName) and
214+
bodySetterCall = m.getACall()
215+
|
216+
// signature: func (*Context).Error(code int, msg string) error
217+
methodName = "Error" and
218+
this = bodySetterCall.getArgument(1) and
219+
contentType = "text/plain"
220+
or
221+
// signature: func (*Context).HTML(code int, html string) error
222+
methodName = "HTML" and
223+
this = bodySetterCall.getArgument(1) and
224+
contentType = "text/html"
225+
or
226+
// signature: func (*Context).HTMLBlob(code int, bs []byte) error
227+
methodName = "HTMLBlob" and
228+
this = bodySetterCall.getArgument(1) and
229+
contentType = "text/html"
230+
or
231+
// signature: func (*Context).JSON(code int, data interface{}) error
232+
methodName = "JSON" and
233+
this = bodySetterCall.getArgument(1) and
234+
contentType = "application/json"
235+
or
236+
// signature: func (*Context).JSONBlob(code int, bs []byte) error
237+
methodName = "JSONBlob" and
238+
this = bodySetterCall.getArgument(1) and
239+
contentType = "application/json"
240+
or
241+
// signature: func (*Context).JSONP(code int, data interface{}) error
242+
methodName = "JSONP" and
243+
this = bodySetterCall.getArgument(1) and
244+
contentType = "application/javascript"
245+
or
246+
// signature: func (*Context).JSONPBlob(code int, bs []byte) error
247+
methodName = "JSONPBlob" and
248+
this = bodySetterCall.getArgument(1) and
249+
contentType = "application/javascript"
250+
or
251+
// signature: func (*Context).JSONPCallback(code int, callback string, data interface{}) error
252+
methodName = "JSONPCallback" and
253+
this = bodySetterCall.getArgument(2) and
254+
contentType = "application/javascript"
255+
or
256+
// signature: func (*Context).JSONPCallbackBlob(code int, callback string, bs []byte) (err error)
257+
methodName = "JSONPCallbackBlob" and
258+
this = bodySetterCall.getArgument(2) and
259+
contentType = "application/javascript"
260+
or
261+
// signature: func (*Context).String(code int, s string) error
262+
methodName = "String" and
263+
this = bodySetterCall.getArgument(1) and
264+
contentType = "text/plain"
265+
or
266+
// signature: func (*Context).StringBlob(code int, bs []byte) error
267+
methodName = "StringBlob" and
268+
this = bodySetterCall.getArgument(1) and
269+
contentType = "text/plain"
270+
or
271+
// signature: func (*Context).Stringf(code int, format string, a ...interface{}) error
272+
methodName = "Stringf" and
273+
this = bodySetterCall.getArgument([1, any(int i | i >= 2)]) and
274+
contentType = "text/plain"
275+
or
276+
// signature: func (*Context).XML(code int, data interface{}) error
277+
methodName = "XML" and
278+
this = bodySetterCall.getArgument(1) and
279+
contentType = "text/xml"
280+
or
281+
// signature: func (*Context).XMLBlob(code int, bs []byte) error
282+
methodName = "XMLBlob" and
283+
this = bodySetterCall.getArgument(1) and
284+
contentType = "text/xml"
285+
)
286+
or
287+
// One call sets both body and content-type (both are parameters in the func call).
288+
// Receiver type: Context
289+
exists(string methodName, Method m |
290+
m.hasQualifiedName(package, "Context", methodName) and
291+
bodySetterCall = m.getACall()
292+
|
293+
// signature: func (*Context).Blob(code int, contentType string, bs []byte) (err error)
294+
methodName = "Blob" and
295+
this = bodySetterCall.getArgument(2) and
296+
contentType = bodySetterCall.getArgument(1).getStringValue()
297+
or
298+
// signature: func (*Context).Emit(code int, contentType string, body string) (err error)
299+
methodName = "Emit" and
300+
this = bodySetterCall.getArgument(2) and
301+
contentType = bodySetterCall.getArgument(1).getStringValue()
302+
)
303+
or
304+
// Two calls, one to set the response body and one to set the content-type.
305+
// Receiver type: Context
306+
exists(string methodName, Method m |
307+
m.hasQualifiedName(package, "Context", methodName) and
308+
bodySetterCall = m.getACall()
309+
|
310+
// signature: func (*Context).Write(data []byte) (int, error)
311+
methodName = "Write" and
312+
this = bodySetterCall.getArgument(0)
313+
or
314+
// signature: func (*Context).WriteString(data string) (int, error)
315+
methodName = "WriteString" and
316+
this = bodySetterCall.getArgument(0)
317+
) and
318+
(
319+
// Receiver type: Context
320+
exists(string methodName, Method m, DataFlow::CallNode contentTypeSetterCall |
321+
m.hasQualifiedName(package, "Context", methodName) and
322+
contentTypeSetterCall = m.getACall() and
323+
contentTypeSetterCall.getReceiver().getAPredecessor*() =
324+
bodySetterCall.getReceiver().getAPredecessor*()
325+
|
326+
// signature: func (*Context).SetContentType(v string)
327+
methodName = "SetContentType" and
328+
contentType = contentTypeSetterCall.getArgument(0).getStringValue()
329+
)
330+
or
331+
// Receiver type: Context
332+
exists(string methodName, Method m, DataFlow::CallNode contentTypeSetterCall |
333+
m.hasQualifiedName(package, "Context", methodName) and
334+
contentTypeSetterCall = m.getACall() and
335+
contentTypeSetterCall.getReceiver().getAPredecessor*() =
336+
bodySetterCall.getReceiver().getAPredecessor*()
337+
|
338+
// signature: func (*Context).SetContentTypeHTML()
339+
methodName = "SetContentTypeHTML" and
340+
contentType = "text/html"
341+
or
342+
// signature: func (*Context).SetContentTypeJSON()
343+
methodName = "SetContentTypeJSON" and
344+
contentType = "application/json"
345+
or
346+
// signature: func (*Context).SetContentTypeText()
347+
methodName = "SetContentTypeText" and
348+
contentType = "text/plain"
349+
or
350+
// signature: func (*Context).SetContentTypeXML()
351+
methodName = "SetContentTypeXML" and
352+
contentType = "text/xml"
353+
)
354+
)
355+
)
356+
}
357+
358+
override string getAContentType() { result = contentType }
359+
360+
override HTTP::ResponseWriter getResponseWriter() { none() }
361+
}
362+
363+
/**
364+
* Models a HTTP header writer model for package: clevergo.tech/[email protected]
365+
*/
366+
private class HeaderWrite extends HTTP::HeaderWrite::Range, DataFlow::CallNode {
367+
HeaderWrite() {
368+
// Receiver type: Context
369+
// signature: func (*Context).SetHeader(key string, value string)
370+
this = any(Method m | m.hasQualifiedName(packagePath(), "Context", "SetHeader")).getACall()
371+
}
372+
373+
override DataFlow::Node getName() { result = this.getArgument(0) }
374+
375+
override DataFlow::Node getValue() { result = this.getArgument(1) }
376+
377+
override HTTP::ResponseWriter getResponseWriter() { none() }
378+
}
379+
}

ql/test/experimental/frameworks/CleverGo/HeaderWrite.expected

Whitespace-only changes.

0 commit comments

Comments
 (0)