forked from cli/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSafeURLPathConstruction.ql
More file actions
73 lines (69 loc) · 2.72 KB
/
Copy pathSafeURLPathConstruction.ql
File metadata and controls
73 lines (69 loc) · 2.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* @name HTTP request URL not built with safeurl.SafeURL
* @description Flags any HTTP request, a REST API call being the common case, whose URL argument is
* not literally a call to (safeurl.SafeURL).String. The argument expression itself must
* be a SafeURL.String call; any other form, such as a string literal, string
* concatenation, or fmt.Sprintf, is reported. This keeps every hand built URL routed
* through safeurl so its variable path components are percent-encoded.
* @kind problem
* @problem.severity warning
* @precision high
* @id cli-cli/safeurl-path-construction
* @tags security
* correctness
* maintainability
*/
import go
/**
* Holds when `node` is the URL argument of an HTTP request, a REST API call being the common case.
*
* Covered entry points:
* - (github.com/cli/cli/v2/api.Client).REST and .RESTWithNext, where the path is argument 2.
* - net/http.NewRequest, where the URL is argument 1.
* - net/http.NewRequestWithContext, where the URL is argument 2.
* - (net/http.Client).Get, .Head, .Post and .PostForm, where the URL is argument 0.
*/
predicate isHttpUrlArgument(DataFlow::Node node) {
exists(Method m, DataFlow::CallNode call |
m.hasQualifiedName("github.com/cli/cli/v2/api", "Client", ["REST", "RESTWithNext"]) and
call = m.getACall() and
node = call.getArgument(2)
)
or
exists(Function f, DataFlow::CallNode call |
f.hasQualifiedName("net/http", "NewRequest") and
call = f.getACall() and
node = call.getArgument(1)
)
or
exists(Function f, DataFlow::CallNode call |
f.hasQualifiedName("net/http", "NewRequestWithContext") and
call = f.getACall() and
node = call.getArgument(2)
)
or
exists(Method m, DataFlow::CallNode call |
m.hasQualifiedName("net/http", "Client", ["Get", "Head", "Post", "PostForm"]) and
call = m.getACall() and
node = call.getArgument(0)
)
}
/**
* Holds when `node` is a call to the String method of one of the safeurl URL types:
* the SafeURL interface or either of its implementations, MutableSafeURL and
* ImmutableSafeURL. Matching all three keeps call sites free of explicit conversions:
* a value of the concrete type can be passed to the sink directly without first being
* assigned to a SafeURL typed variable.
*/
predicate isSafeurlStringCall(DataFlow::Node node) {
exists(Method m |
m.hasQualifiedName("github.com/cli/cli/v2/internal/safeurl",
["SafeURL", "MutableSafeURL", "ImmutableSafeURL"], "String") and
node = m.getACall()
)
}
from DataFlow::Node sink
where
isHttpUrlArgument(sink) and
not isSafeurlStringCall(sink)
select sink, "This HTTP request URL is not passed directly as the result of safeurl.SafeURL.String."