Skip to content

Commit 396ad33

Browse files
committed
JS: add RemoteFlowSource.isDeepObject() and populate it
1 parent 46b2015 commit 396ad33

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

javascript/ql/src/semmle/javascript/frameworks/Express.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,20 @@ module Express {
488488
override string getKind() {
489489
result = kind
490490
}
491+
492+
override predicate isDeepObject() {
493+
kind = "body" and
494+
exists (ExpressLibraries::BodyParser bodyParser, RouteHandlerExpr expr |
495+
expr.getBody() = rh and
496+
bodyParser.isDeepObject() and
497+
bodyParser.flowsToExpr(expr.getAMatchingAncestor())
498+
)
499+
or
500+
// If we can't find the middlewares for the route handler,
501+
// but all known body parsers are deep, assume req.body is a deep object.
502+
kind = "body" and
503+
forall(ExpressLibraries::BodyParser bodyParser | bodyParser.isDeepObject())
504+
}
491505
}
492506

493507
/**

javascript/ql/src/semmle/javascript/frameworks/ExpressModules.qll

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,52 @@ module ExpressLibraries {
230230

231231
}
232232

233+
/**
234+
* An instance of the Express `body-parser` middleware.
235+
*/
236+
class BodyParser extends DataFlow::InvokeNode {
237+
string kind;
238+
239+
BodyParser() {
240+
this = DataFlow::moduleImport("body-parser").getACall() and kind = "json"
241+
or
242+
exists (string moduleName |
243+
(moduleName = "body-parser" or moduleName = "express") and
244+
(kind = "json" or kind = "urlencoded") and
245+
this = DataFlow::moduleMember(moduleName, kind).getACall()
246+
)
247+
}
248+
249+
/**
250+
* Holds if this is a JSON body parser.
251+
*/
252+
predicate isJson() {
253+
kind = "json"
254+
}
255+
256+
/**
257+
* Holds if this is a URL-encoded body parser.
258+
*/
259+
predicate isUrlEncoded() {
260+
kind = "urlencoded"
261+
}
262+
263+
/**
264+
* Holds if this is an extended URL-encoded body parser.
265+
*
266+
* The extended URL-encoding allows for nested objects, like JSON.
267+
*/
268+
predicate isExtendedUrlEncoded() {
269+
kind = "urlencoded" and
270+
not getOptionArgument(0, "extended").mayHaveBooleanValue(false)
271+
}
272+
273+
/**
274+
* Holds if this parses the input as JSON or extended URL-encoding.
275+
*/
276+
predicate isDeepObject() {
277+
isJson() or isExtendedUrlEncoded()
278+
}
279+
}
280+
233281
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import semmle.javascript.security.dataflow.DOM
1010
abstract class RemoteFlowSource extends DataFlow::Node {
1111
/** Gets a string that describes the type of this remote flow source. */
1212
abstract string getSourceType();
13+
14+
/**
15+
* Holds if this can be a user-controlled deep object, such as a JSON object parsed from user-controlled data.
16+
*/
17+
predicate isDeepObject() { none() }
1318
}
1419

1520
/**

0 commit comments

Comments
 (0)