mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #3704 from asger-semmle/js/cve-serve
Approved by esbena
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
- [jGrowl](https://github.com/stanlemon/jGrowl)
|
- [jGrowl](https://github.com/stanlemon/jGrowl)
|
||||||
- [jQuery](https://jquery.com/)
|
- [jQuery](https://jquery.com/)
|
||||||
- [marsdb](https://www.npmjs.com/package/marsdb)
|
- [marsdb](https://www.npmjs.com/package/marsdb)
|
||||||
|
- [micro](https://www.npmjs.com/package/micro/)
|
||||||
- [minimongo](https://www.npmjs.com/package/minimongo/)
|
- [minimongo](https://www.npmjs.com/package/minimongo/)
|
||||||
- [mssql](https://www.npmjs.com/package/mssql)
|
- [mssql](https://www.npmjs.com/package/mssql)
|
||||||
- [mysql](https://www.npmjs.com/package/mysql)
|
- [mysql](https://www.npmjs.com/package/mysql)
|
||||||
|
|||||||
@@ -576,6 +576,22 @@ module Bluebird {
|
|||||||
|
|
||||||
override DataFlow::Node getArrayNode() { result = getArgument(0) }
|
override DataFlow::Node getArrayNode() { result = getArgument(0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An async function created using a call to `bluebird.coroutine`.
|
||||||
|
*/
|
||||||
|
class BluebirdCoroutineDefinition extends DataFlow::CallNode {
|
||||||
|
BluebirdCoroutineDefinition() { this = bluebird().getAMemberCall("coroutine") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BluebirdCoroutineDefinitionAsPartialInvoke extends DataFlow::PartialInvokeNode::Range,
|
||||||
|
BluebirdCoroutineDefinition {
|
||||||
|
override DataFlow::SourceNode getBoundFunction(DataFlow::Node callback, int boundArgs) {
|
||||||
|
boundArgs = 0 and
|
||||||
|
callback = getArgument(0) and
|
||||||
|
result = this
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import semmle.javascript.frameworks.Express
|
|||||||
import semmle.javascript.frameworks.Hapi
|
import semmle.javascript.frameworks.Hapi
|
||||||
import semmle.javascript.frameworks.Koa
|
import semmle.javascript.frameworks.Koa
|
||||||
import semmle.javascript.frameworks.NodeJSLib
|
import semmle.javascript.frameworks.NodeJSLib
|
||||||
|
import semmle.javascript.frameworks.Micro
|
||||||
import semmle.javascript.frameworks.Restify
|
import semmle.javascript.frameworks.Restify
|
||||||
import semmle.javascript.frameworks.Connect
|
import semmle.javascript.frameworks.Connect
|
||||||
import semmle.javascript.frameworks.Fastify
|
import semmle.javascript.frameworks.Fastify
|
||||||
|
|||||||
114
javascript/ql/src/semmle/javascript/frameworks/Micro.qll
Normal file
114
javascript/ql/src/semmle/javascript/frameworks/Micro.qll
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* Provides a model of the `micro` NPM package.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private import javascript
|
||||||
|
|
||||||
|
private module Micro {
|
||||||
|
private import DataFlow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node that should be interpreted as a route handler, to use as starting
|
||||||
|
* point for back-tracking.
|
||||||
|
*/
|
||||||
|
Node microRouteHandlerSink() {
|
||||||
|
result = moduleMember("micro", "run").getACall().getLastArgument()
|
||||||
|
or
|
||||||
|
result = moduleImport("micro").getACall().getArgument(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a data flow node interpreted as a route handler. */
|
||||||
|
private DataFlow::SourceNode microRouteHandler(DataFlow::TypeBackTracker t) {
|
||||||
|
t.start() and
|
||||||
|
result = microRouteHandlerSink().getALocalSource()
|
||||||
|
or
|
||||||
|
exists(DataFlow::TypeBackTracker t2 | result = microRouteHandler(t2).backtrack(t2, t))
|
||||||
|
or
|
||||||
|
exists(DataFlow::CallNode transformer |
|
||||||
|
transformer = moduleImport("micro-compress").getACall()
|
||||||
|
or
|
||||||
|
transformer instanceof Bluebird::BluebirdCoroutineDefinition
|
||||||
|
|
|
||||||
|
microRouteHandler(t.continue()) = transformer and
|
||||||
|
result = transformer.getArgument(0).getALocalSource()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a data flow node interpreted as a route handler. */
|
||||||
|
DataFlow::SourceNode microRouteHandler() {
|
||||||
|
result = microRouteHandler(DataFlow::TypeBackTracker::end())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function passed to `micro` or `micro.run`.
|
||||||
|
*/
|
||||||
|
class MicroRouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||||
|
MicroRouteHandler() { this = microRouteHandler().getAFunctionValue() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MicroRequestSource extends HTTP::Servers::RequestSource {
|
||||||
|
MicroRouteHandler h;
|
||||||
|
|
||||||
|
MicroRequestSource() { this = h.getParameter(0) }
|
||||||
|
|
||||||
|
override HTTP::RouteHandler getRouteHandler() { result = h }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MicroResponseSource extends HTTP::Servers::ResponseSource {
|
||||||
|
MicroRouteHandler h;
|
||||||
|
|
||||||
|
MicroResponseSource() { this = h.getParameter(1) }
|
||||||
|
|
||||||
|
override HTTP::RouteHandler getRouteHandler() { result = h }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MicroRequestExpr extends NodeJSLib::RequestExpr {
|
||||||
|
override MicroRequestSource src;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MicroReseponseExpr extends NodeJSLib::ResponseExpr {
|
||||||
|
override MicroResponseSource src;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HTTP::RouteHandler getRouteHandlerFromReqRes(DataFlow::Node node) {
|
||||||
|
exists(HTTP::Servers::RequestSource src |
|
||||||
|
src.ref().flowsTo(node) and
|
||||||
|
result = src.getRouteHandler()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(HTTP::Servers::ResponseSource src |
|
||||||
|
src.ref().flowsTo(node) and
|
||||||
|
result = src.getRouteHandler()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class MicroBodyParserCall extends HTTP::RequestInputAccess, DataFlow::CallNode {
|
||||||
|
string name;
|
||||||
|
|
||||||
|
MicroBodyParserCall() {
|
||||||
|
name = ["buffer", "text", "json"] and
|
||||||
|
this = moduleMember("micro", name).getACall()
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getKind() { result = "body" }
|
||||||
|
|
||||||
|
override HTTP::RouteHandler getRouteHandler() {
|
||||||
|
result = getRouteHandlerFromReqRes(getArgument(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isUserControlledObject() { name = "json" }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MicroSendArgument extends HTTP::ResponseSendArgument {
|
||||||
|
CallNode send;
|
||||||
|
|
||||||
|
MicroSendArgument() {
|
||||||
|
send = moduleMember("micro", ["send", "sendError"]).getACall() and
|
||||||
|
this = send.getLastArgument().asExpr()
|
||||||
|
}
|
||||||
|
|
||||||
|
override HTTP::RouteHandler getRouteHandler() {
|
||||||
|
result = getRouteHandlerFromReqRes(send.getArgument([0, 1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -212,11 +212,10 @@ module TaintedPath {
|
|||||||
DataFlow::Node output;
|
DataFlow::Node output;
|
||||||
|
|
||||||
PreservingPathCall() {
|
PreservingPathCall() {
|
||||||
exists(string name | name = "dirname" or name = "toNamespacedPath" |
|
this =
|
||||||
this = NodeJSLib::Path::moduleMember(name).getACall() and
|
NodeJSLib::Path::moduleMember(["dirname", "toNamespacedPath", "parse", "format"]).getACall() and
|
||||||
input = getAnArgument() and
|
input = getAnArgument() and
|
||||||
output = this
|
output = this
|
||||||
)
|
|
||||||
or
|
or
|
||||||
// non-global replace or replace of something other than /\.\./g, /[/]/g, or /[\.]/g.
|
// non-global replace or replace of something other than /\.\./g, /[/]/g, or /[\.]/g.
|
||||||
this.getCalleeName() = "replace" and
|
this.getCalleeName() = "replace" and
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
routeHandler
|
||||||
|
| tst.js:5:7:10:1 | async ( ... llo";\\n} |
|
||||||
|
| tst.js:12:1:15:1 | functio ... nse";\\n} |
|
||||||
|
requestSource
|
||||||
|
| tst.js:5:14:5:16 | req |
|
||||||
|
| tst.js:12:26:12:28 | req |
|
||||||
|
responseSource
|
||||||
|
| tst.js:5:19:5:21 | res |
|
||||||
|
| tst.js:12:31:12:33 | res |
|
||||||
|
requestInputAccess
|
||||||
|
| body | tst.js:7:5:7:19 | micro.json(req) |
|
||||||
|
| header | tst.js:6:5:6:31 | req.hea ... -type'] |
|
||||||
|
| header | tst.js:13:5:13:31 | req.hea ... -type'] |
|
||||||
|
userControlledObject
|
||||||
|
| tst.js:7:5:7:19 | micro.json(req) |
|
||||||
|
responseSendArgument
|
||||||
|
| tst.js:8:31:8:36 | "data" |
|
||||||
|
responseSendArgumentHandler
|
||||||
|
| tst.js:5:7:10:1 | async ( ... llo";\\n} | tst.js:8:31:8:36 | "data" |
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import javascript
|
||||||
|
|
||||||
|
query HTTP::RouteHandler routeHandler() { any() }
|
||||||
|
|
||||||
|
query HTTP::Servers::RequestSource requestSource() { any() }
|
||||||
|
|
||||||
|
query HTTP::Servers::ResponseSource responseSource() { any() }
|
||||||
|
|
||||||
|
query HTTP::RequestInputAccess requestInputAccess(string kind) { kind = result.getKind() }
|
||||||
|
|
||||||
|
query HTTP::RequestInputAccess userControlledObject() { result.isUserControlledObject() }
|
||||||
|
|
||||||
|
query HTTP::ResponseSendArgument responseSendArgument() { any() }
|
||||||
|
|
||||||
|
query HTTP::ResponseSendArgument responseSendArgumentHandler(HTTP::RouteHandler h) {
|
||||||
|
h = result.getRouteHandler()
|
||||||
|
}
|
||||||
19
javascript/ql/test/library-tests/frameworks/Micro/tst.js
Normal file
19
javascript/ql/test/library-tests/frameworks/Micro/tst.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const micro = require('micro')
|
||||||
|
const bluebird = require('bluebird');
|
||||||
|
const compress = require('micro-compress');
|
||||||
|
|
||||||
|
micro(async (req, res) => {
|
||||||
|
req.headers['content-type'];
|
||||||
|
micro.json(req);
|
||||||
|
micro.sendError(req, res, "data");
|
||||||
|
return "Hello";
|
||||||
|
})
|
||||||
|
|
||||||
|
function* wrappedHandler(req, res) {
|
||||||
|
req.headers['content-type'];
|
||||||
|
yield "Response";
|
||||||
|
}
|
||||||
|
|
||||||
|
let handler = bluebird.coroutine(wrappedHandler);
|
||||||
|
|
||||||
|
micro(compress(handler));
|
||||||
Reference in New Issue
Block a user