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

Skip to content

Commit 9ae8880

Browse files
authored
Merge pull request #4802 from max-schaefer/js/external-remote-flow-sources
Approved by asgerf, jf205
2 parents be5dbf2 + cf58914 commit 9ae8880

9 files changed

Lines changed: 214 additions & 2 deletions

File tree

docs/codeql/codeql-language-guides/codeql-for-javascript.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
1313
codeql-library-for-typescript
1414
analyzing-data-flow-in-javascript-and-typescript
1515
using-flow-labels-for-precise-data-flow-analysis
16+
specifying-additional-remote-flow-sources-for-javascript
1617
using-type-tracking-for-api-modeling
1718
abstract-syntax-tree-classes-for-working-with-javascript-and-typescript-programs
1819
data-flow-cheat-sheet-for-javascript
@@ -27,6 +28,8 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
2728

2829
- :doc:`Using flow labels for precise data flow analysis <using-flow-labels-for-precise-data-flow-analysis>`: You can associate flow labels with each value tracked by the flow analysis to determine whether the flow contains potential vulnerabilities.
2930

31+
- :doc:`Specifying remote flow sources for JavaScript <specifying-additional-remote-flow-sources-for-javascript>`: You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
32+
3033
- :doc:`Using type tracking for API modeling <using-type-tracking-for-api-modeling>`: You can track data through an API by creating a model using the CodeQL type-tracking library for JavaScript.
3134

3235
- :doc:`Abstract syntax tree classes for working with JavaScript and TypeScript programs <abstract-syntax-tree-classes-for-working-with-javascript-and-typescript-programs>`: CodeQL has a large selection of classes for representing the abstract syntax tree of JavaScript and TypeScript programs.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
.. _specifying-additional-remote-flow-sources-for-javascript:
2+
3+
Specifying additional remote flow sources for JavaScript
4+
========================================================
5+
6+
You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
7+
8+
.. pull-quote::
9+
10+
Note
11+
12+
Specifying remote flow sources in external files is currently in beta and subject to change.
13+
14+
As mentioned in the :doc:`Data flow cheat sheet for JavaScript <data-flow-cheat-sheet-for-javascript>`, the CodeQL libraries for JavaScript
15+
provide a class `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$RemoteFlowSource.html>`__ to represent sources of untrusted user input, sometimes also referred to as remote flow
16+
sources.
17+
18+
To model a new source of untrusted input, such as a previously unmodelled library API, you can
19+
define a subclass of ``RemoteFlowSource`` that covers all uses of that API. All standard analyses
20+
will then automatically pick up this new source of remote flow.
21+
22+
However, this approach requires writing QL code and adding it to the standard library, which is not
23+
always easy to do. Instead, you can also add a JSON file describing custom sources of untrusted
24+
input to your code base and have it picked up without needing to modify the standard library. This
25+
JSON file can be hand-written or generated by another tool. The custom remote flow sources are only available to the code base containing the JSON file. This means that you need to copy the JSON file into each code base that requires the customizations.
26+
27+
Specification format
28+
--------------------
29+
30+
The JSON file must be called ``codeql-javascript-remote-flow-sources.json`` and
31+
can be located anywhere in your code base. It should consist of a single JSON object. The property
32+
names of this object are interpreted as `source types`. The values they map to should be arrays of
33+
strings. Each string should be of the form ``window.props``, where ``props`` is a sequence of one
34+
or more property names separated by dots. This notation specifies that any value reachable from the global window
35+
object by this sequence of property names should be considered as untrusted user input of the
36+
associated source type.
37+
38+
Example
39+
-------
40+
41+
Consider the following specification:
42+
43+
.. code-block:: json
44+
45+
{
46+
"user input": [ "window.user.name", "window.user.address", "window.dob" ]
47+
}
48+
49+
It declares that the contents of global variable ``dob``, as well as the contents of properties
50+
``name`` and ``address`` of global variable ``user``, should be considered as remote flow sources,
51+
with source type "user input".
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Custom remote flow sources can now be specified by including a file named `codeql-javascript-remote-flow-sources.json` in your code base. See documentation for more details.

javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@
146146
* FileType#HTML} (currently ".htm", ".html", ".xhtm", ".xhtml", ".vue").
147147
* <li>All YAML files, that is, files with one of the extensions supported by {@link
148148
* FileType#YAML} (currently ".raml", ".yaml", ".yml").
149-
* <li>Files with base name "package.json".
149+
* <li>Files with base name "package.json" or "tsconfig.json", and files whose base name
150+
* is of the form "codeql-javascript-*.json".
150151
* <li>JavaScript, JSON or YAML files whose base name starts with ".eslintrc".
151152
* <li>All extension-less files.
152153
* </ul>
@@ -402,10 +403,12 @@ private void setupFilters() {
402403
for (FileType filetype : defaultExtract)
403404
for (String extension : filetype.getExtensions()) patterns.add("**/*" + extension);
404405

405-
// include .eslintrc files, package.json files, and tsconfig.json files
406+
// include .eslintrc files, package.json files, tsconfig.json files, and
407+
// codeql-javascript-*.json files
406408
patterns.add("**/.eslintrc*");
407409
patterns.add("**/package.json");
408410
patterns.add("**/tsconfig.json");
411+
patterns.add("**/codeql-javascript-*.json");
409412

410413
// include any explicitly specified extensions
411414
for (String extension : fileTypes.keySet()) patterns.add("**/*" + extension);

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)