@@ -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+ }
0 commit comments