Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9f8508f

Browse files
author
Max Schaefer
committed
JavaScript: Allow specifying additional remote flow sources through JSON.
1 parent 0ccfe4f commit 9f8508f

5 files changed

Lines changed: 153 additions & 0 deletions

File tree

javascript/ql/src/semmle/javascript/security/dataflow/RemoteFlowSources.qll

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,98 @@ abstract class RemoteFlowSource extends DataFlow::Node {
1616
*/
1717
predicate isUserControlledObject() { none() }
1818
}
19+
20+
/**
21+
* A specification of a remote flow source in a JSON file included in the database.
22+
*
23+
* The JSON file must be named `codeql-javascript-remote-flow-sources.json` and consist of a JSON
24+
* object. The object's keys are source types (in the sense of `RemoteFlowSource.getSourceType()`),
25+
* each mapping to a list of strings. Each string in that list must be of the form `window.f.props`
26+
* where `f` is the name of a global variable, and `props` is a possibly empty sequence of property
27+
* names separated by dots. It declares any value stored in the given property sequece of `f` to be
28+
* a remote flow source of the type specified by the key.
29+
*
30+
* For example, consider the following specification:
31+
*
32+
* ```json
33+
* {
34+
* "user input": [ "window.user.name", "window.user.address", "window.dob" ]
35+
* }
36+
* ```
37+
*
38+
* It declares that the contents of global variable `dob`, as well as the contents of properties
39+
* `name` and `address` of global variable `user` should be considered as remote flow sources with
40+
* source type "user input".
41+
*/
42+
private class RemoteFlowSourceAccessPath extends JSONString {
43+
string sourceType;
44+
45+
RemoteFlowSourceAccessPath() {
46+
exists(JSONObject specs |
47+
specs.isTopLevel() and
48+
this.getFile().getBaseName() = "codeql-javascript-remote-flow-sources.json" and
49+
this = specs.getPropValue(sourceType).(JSONArray).getElementValue(_) and
50+
this.getValue().regexpMatch("window(\\.\\w+)+")
51+
)
52+
}
53+
54+
/** Gets the source type of this remote flow source. */
55+
string getSourceType() { result = sourceType }
56+
57+
/** Gets the `i`th component of the access path specifying this remote flow source. */
58+
string getComponent(int i) {
59+
exists(string raw | raw = this.getValue().splitAt(".", i + 1) |
60+
i = 0 and
61+
result = "ExternalRemoteFlowSourceSpec " + raw
62+
or
63+
i > 0 and
64+
result = API::EdgeLabel::member(raw)
65+
)
66+
}
67+
68+
/** Gets the index of the last component of this access path. */
69+
int getMaxComponentIndex() { result = max(int i | exists(getComponent(i))) }
70+
71+
/**
72+
* Gets the API node to which the prefix of the access path up to and including `i` resolves.
73+
*
74+
* As a special base case, resolving up to -1 gives the root API node.
75+
*/
76+
private API::Node resolveUpTo(int i) {
77+
i = -1 and
78+
result = API::root()
79+
or
80+
result = resolveUpTo(i - 1).getASuccessor(getComponent(i))
81+
}
82+
83+
/** Gets the API node to which this access path resolves. */
84+
API::Use resolve() { result = resolveUpTo(getMaxComponentIndex()) }
85+
}
86+
87+
/**
88+
* The global variable referenced by a `RemoteFlowSourceAccessPath`, declared as an API
89+
* entry point.
90+
*/
91+
private class ExternalRemoteFlowSourceSpecEntryPoint extends API::EntryPoint {
92+
string name;
93+
94+
ExternalRemoteFlowSourceSpecEntryPoint() {
95+
this = any(RemoteFlowSourceAccessPath s).getComponent(0) and
96+
this = "ExternalRemoteFlowSourceSpec " + name
97+
}
98+
99+
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef(name) }
100+
101+
override DataFlow::Node getARhs() { none() }
102+
}
103+
104+
/**
105+
* A remote flow source induced by a `RemoteFlowSourceAccessPath`.
106+
*/
107+
private class ExternalRemoteFlowSource extends RemoteFlowSource {
108+
RemoteFlowSourceAccessPath ap;
109+
110+
ExternalRemoteFlowSource() { this = ap.resolve().getAnImmediateUse() }
111+
112+
override string getSourceType() { result = ap.getSourceType() }
113+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
missing
2+
spurious
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import javascript
2+
3+
predicate remoteFlowSourceSpec(Comment c, string path, int line, string sourceType) {
4+
c.getLocation().hasLocationInfo(path, line, _, _, _) and
5+
sourceType = c.getText().regexpCapture("\\s*RemoteFlowSource\\s*:\\s*(.+)", 1)
6+
}
7+
8+
predicate remoteFlowSource(RemoteFlowSource rfs, string path, int line, string sourceType) {
9+
rfs.hasLocationInfo(path, line, _, _, _) and
10+
sourceType = rfs.getSourceType()
11+
}
12+
13+
query predicate missing(Comment c, string sourceType) {
14+
exists(string path, int line |
15+
remoteFlowSourceSpec(c, path, line, sourceType) and
16+
not remoteFlowSource(_, path, line, sourceType)
17+
)
18+
}
19+
20+
query predicate spurious(RemoteFlowSource rfs, string sourceType) {
21+
exists(string path, int line |
22+
not remoteFlowSourceSpec(_, path, line, sourceType) and
23+
remoteFlowSource(rfs, path, line, sourceType)
24+
)
25+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"user input": [
3+
"window.user.name",
4+
"window.user.address",
5+
"window.dob"
6+
],
7+
"uncontrolled path": [
8+
"window.upload"
9+
]
10+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
window.user.name; // RemoteFlowSource: user input
2+
window.user.address; // RemoteFlowSource: user input
3+
window.dob; // RemoteFlowSource: user input
4+
window.upload; // RemoteFlowSource: uncontrolled path
5+
6+
this.user.name; // RemoteFlowSource: user input
7+
this.user.address; // RemoteFlowSource: user input
8+
this.dob; // RemoteFlowSource: user input
9+
this.upload; // RemoteFlowSource: uncontrolled path
10+
11+
user.name; // RemoteFlowSource: user input
12+
user.address; // RemoteFlowSource: user input
13+
dob; // RemoteFlowSource: user input
14+
upload; // RemoteFlowSource: uncontrolled path
15+
16+
(function (global) {
17+
global.user.name; // RemoteFlowSource: user input
18+
global.user.address; // RemoteFlowSource: user input
19+
global.dob; // RemoteFlowSource: user input
20+
global.upload; // RemoteFlowSource: uncontrolled path
21+
})(this);

0 commit comments

Comments
 (0)