mirror of
https://github.com/github/codeql.git
synced 2026-01-04 18:20:18 +01:00
Merge branch 'js-team-sprint' into js/insecure-http-options
This commit is contained in:
@@ -3,22 +3,6 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
<li>OWASP Top 10: <a href="https://www.owasp.org/index.php/Top_10-2017_A1-Injection">A1 Injection</a>.</li>
|
||||
|
||||
</references>
|
||||
<include src="IncompleteSanitization.qhelp" />
|
||||
|
||||
</qhelp>
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* @name Server crash
|
||||
* @description A server that can be forced to crash may be vulnerable to denial-of-service
|
||||
* attacks.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id js/server-crash
|
||||
* @tags security
|
||||
* external/cwe/cwe-730
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Gets a function that `caller` invokes.
|
||||
*/
|
||||
Function getACallee(Function caller) {
|
||||
exists(DataFlow::InvokeNode invk |
|
||||
invk.getEnclosingFunction() = caller and result = invk.getACallee()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that `caller` invokes, excluding calls guarded in `try`-blocks.
|
||||
*/
|
||||
Function getAnUnguardedCallee(Function caller) {
|
||||
exists(DataFlow::InvokeNode invk |
|
||||
invk.getEnclosingFunction() = caller and
|
||||
result = invk.getACallee() and
|
||||
not exists(invk.asExpr().getEnclosingStmt().getEnclosingTryCatchStmt())
|
||||
)
|
||||
}
|
||||
|
||||
predicate isHeaderValue(HTTP::ExplicitHeaderDefinition def, DataFlow::Node node) {
|
||||
def.definesExplicitly(_, node.asExpr())
|
||||
}
|
||||
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "Configuration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
// using control characters in a header value will cause an exception
|
||||
isHeaderValue(_, node)
|
||||
}
|
||||
}
|
||||
|
||||
predicate isLikelyToThrow(DataFlow::Node crash) {
|
||||
exists(Configuration cfg, DataFlow::Node sink | cfg.hasFlow(_, sink) | isHeaderValue(crash, sink))
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that looks like it is asynchronous.
|
||||
*/
|
||||
class AsyncCall extends DataFlow::CallNode {
|
||||
DataFlow::FunctionNode callback;
|
||||
|
||||
AsyncCall() {
|
||||
callback.flowsTo(getLastArgument()) and
|
||||
callback.getParameter(0).getName() = ["e", "err", "error"] and
|
||||
callback.getNumParameter() = 2 and
|
||||
not exists(callback.getAReturn())
|
||||
}
|
||||
|
||||
DataFlow::FunctionNode getCallback() { result = callback }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that is invoked by `asyncCallback` without any try-block wrapping, `asyncCallback` is in turn is called indirectly by `routeHandler`.
|
||||
*
|
||||
* If the result throws an excection, the server of `routeHandler` will crash.
|
||||
*/
|
||||
Function getAPotentialServerCrasher(
|
||||
HTTP::RouteHandler routeHandler, DataFlow::FunctionNode asyncCallback
|
||||
) {
|
||||
exists(AsyncCall asyncCall |
|
||||
// the route handler transitively calls an async function
|
||||
asyncCall.getEnclosingFunction() =
|
||||
getACallee*(routeHandler.(DataFlow::FunctionNode).getFunction()) and
|
||||
asyncCallback = asyncCall.getCallback() and
|
||||
// the async function transitively calls a function that may throw an exception out of the the async function
|
||||
result = getAnUnguardedCallee*(asyncCallback.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an AST node that is likely to throw an uncaught exception in `fun`.
|
||||
*/
|
||||
ExprOrStmt getALikelyExceptionThrower(Function fun) {
|
||||
result.getContainer() = fun and
|
||||
not exists([result.(Expr).getEnclosingStmt(), result.(Stmt)].getEnclosingTryCatchStmt()) and
|
||||
(isLikelyToThrow(result.(Expr).flow()) or result instanceof ThrowStmt)
|
||||
}
|
||||
|
||||
from HTTP::RouteHandler routeHandler, DataFlow::FunctionNode asyncCallback, ExprOrStmt crasher
|
||||
where crasher = getALikelyExceptionThrower(getAPotentialServerCrasher(routeHandler, asyncCallback))
|
||||
select crasher, "When an exception is thrown here and later exits $@, the server of $@ will crash.",
|
||||
asyncCallback, "this asynchronous callback", routeHandler, "this route handler"
|
||||
@@ -1,113 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
|
||||
<p>
|
||||
|
||||
Applications are constrained by how many resources they can make use
|
||||
of. Failing to respect these constraints may cause the application to
|
||||
be unresponsive or crash. It is therefore problematic if attackers
|
||||
can control the sizes or lifetimes of allocated objects.
|
||||
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
|
||||
Ensure that attackers can not control object sizes and their
|
||||
lifetimes. If object sizes and lifetimes must be controlled by
|
||||
external parties, ensure you restrict the object sizes and lifetimes so that
|
||||
they are within acceptable ranges.
|
||||
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
The following example allocates a buffer with a user-controlled
|
||||
size.
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/ResourceExhaustion_buffer.js" />
|
||||
|
||||
<p>
|
||||
|
||||
This is problematic since an attacker can choose a size
|
||||
that makes the application run out of memory. Even worse, in older
|
||||
versions of Node.js, this could leak confidential memory.
|
||||
|
||||
To prevent such attacks, limit the buffer size:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/ResourceExhaustion_buffer_fixed.js" />
|
||||
|
||||
</example>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
As another example, consider an application that allocates an
|
||||
array with a user-controlled size, and then fills it with values:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/ResourceExhaustion_array.js" />
|
||||
|
||||
<p>
|
||||
The allocation of the array itself is not problematic since arrays are
|
||||
allocated sparsely, but the subsequent filling of the array will take
|
||||
a long time, causing the application to be unresponsive, or even run
|
||||
out of memory.
|
||||
|
||||
Again, a limit on the size will prevent the attack:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/ResourceExhaustion_array_fixed.js" />
|
||||
|
||||
</example>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
Finally, the following example lets a user choose a delay after
|
||||
which a function is executed:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/ResourceExhaustion_timeout.js" />
|
||||
|
||||
<p>
|
||||
|
||||
This is problematic because a large delay essentially makes the
|
||||
application wait indefinitely before executing the function. Repeated
|
||||
registrations of such delays will therefore use up all of the memory
|
||||
in the application.
|
||||
|
||||
Again, a limit on the delay will prevent the attack:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/ResourceExhaustion_timeout_fixed.js" />
|
||||
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @name Resource exhaustion
|
||||
* @description Allocating objects or timers with user-controlled
|
||||
* sizes or durations can cause resource exhaustion.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id js/resource-exhaustion
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-770
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
import semmle.javascript.security.dataflow.ResourceExhaustion::ResourceExhaustion
|
||||
|
||||
from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where dataflow.hasFlowPath(source, sink)
|
||||
select sink, source, sink, sink.getNode().(Sink).getProblemDescription() + " from $@.", source,
|
||||
"here"
|
||||
@@ -1,10 +0,0 @@
|
||||
var http = require("http"),
|
||||
url = require("url");
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var size = parseInt(url.parse(req.url, true).query.size);
|
||||
|
||||
let dogs = new Array(size).fill(x => "dog"); // BAD
|
||||
|
||||
// ... use the dogs
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
var http = require("http"),
|
||||
url = require("url");
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var size = parseInt(url.parse(req.url, true).query.size);
|
||||
|
||||
if (size > 1024) {
|
||||
res.statusCode = 400;
|
||||
res.end("Bad request.");
|
||||
return;
|
||||
}
|
||||
|
||||
let dogs = new Array(size).fill(x => "dog"); // GOOD
|
||||
|
||||
// ... use the dogs
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
var http = require("http"),
|
||||
url = require("url");
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var size = parseInt(url.parse(req.url, true).query.size);
|
||||
|
||||
let buffer = Buffer.alloc(size); // BAD
|
||||
|
||||
// ... use the buffer
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
var http = require("http"),
|
||||
url = require("url");
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var size = parseInt(url.parse(req.url, true).query.size);
|
||||
|
||||
if (size > 1024) {
|
||||
res.statusCode = 400;
|
||||
res.end("Bad request.");
|
||||
return;
|
||||
}
|
||||
|
||||
let buffer = Buffer.alloc(size); // GOOD
|
||||
|
||||
// ... use the buffer
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
var http = require("http"),
|
||||
url = require("url");
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var delay = parseInt(url.parse(req.url, true).query.delay);
|
||||
|
||||
setTimeout(f, delay); // BAD
|
||||
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
var http = require("http"),
|
||||
url = require("url");
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var delay = parseInt(url.parse(req.url, true).query.delay);
|
||||
|
||||
if (delay > 1000) {
|
||||
res.statusCode = 400;
|
||||
res.end("Bad request.");
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(f, delay); // GOOD
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user