mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
JavaScript: Add new query UnvalidatedDynamicMethodCall.
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
+ semmlecode-javascript-queries/Security/CWE-640/HostHeaderPoisoningInEmailGeneration.ql: /Security/CWE/CWE-640
|
||||
+ semmlecode-javascript-queries/Security/CWE-643/XpathInjection.ql: /Security/CWE/CWE-643
|
||||
+ semmlecode-javascript-queries/Security/CWE-730/RegExpInjection.ql: /Security/CWE/CWE-730
|
||||
+ semmlecode-javascript-queries/Security/CWE-754/UnvalidatedDynamicMethodCall.ql: /Security/CWE/CWE-754
|
||||
+ semmlecode-javascript-queries/Security/CWE-770/MissingRateLimiting.ql: /Security/CWE/CWE-770
|
||||
+ semmlecode-javascript-queries/Security/CWE-776/XmlBomb.ql: /Security/CWE/CWE-776
|
||||
+ semmlecode-javascript-queries/Security/CWE-798/HardcodedCredentials.ql: /Security/CWE/CWE-798
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
JavaScript makes it easy to look up object properties dynamically at runtime. In particular, methods
|
||||
can be looked up by name and then called. However, if he method name is user controlled, an attacker
|
||||
could choose a name that makes the application invoke an unexpected method, which may cause a runtime
|
||||
exception. If this exception is not handled, it could be used to mount a denial-of-service attack.
|
||||
</p>
|
||||
<p>
|
||||
For example, there might not be a method of the given name or the result of the lookup might not be
|
||||
a function, which would cause the method call to throw a <code>TypeError</code> at runtime.
|
||||
</p>
|
||||
<p>
|
||||
Another, more subtle example is where the result of the lookup is a standard library method from
|
||||
<code>Object.prototype</code>, which most objects have on their prototype chain. Examples of such
|
||||
methods include <code>valueOf</code>, <code>hasOwnProperty</code> and <code>__defineSetter__</code>.
|
||||
If the method call passes the wrong number or kind of arguments to these methods, they will
|
||||
throw an exception.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
It is best to avoid dynamic method lookup involving user-controlled names altogether, for instance
|
||||
by using a <code>Map</code> instead of a plain object.
|
||||
</p>
|
||||
<p>
|
||||
If the dynamic method lookup cannot be avoided, consider whitelisting permitted method names. At
|
||||
the very least, check that the method is an own property and not inherited from the prototype object.
|
||||
If the object on which the method is looked up contains properties that are not methods, you
|
||||
should additionally check that the result of the lookup is a function. Even if the object only
|
||||
contains methods it is still a good idea to perform this check in case other properties are
|
||||
added to the object later on.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In the following example, an HTTP request parameter <code>action</code> property is used to dynamically
|
||||
look up a function in the <code>actions</code> map, which is then invoked with the <code>payload</code>
|
||||
parameter as its argument.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnvalidatedDynamicMethodCall.js" />
|
||||
|
||||
<p>
|
||||
The intention is to allow clients to invoke the <code>play</code> or <code>pause</code> method, but there
|
||||
is no check that <code>action</code> is actually the name of a method stored in <code>actions</code>.
|
||||
If, for example, <code>action</code> is <code>rewind</code>, <code>action</code> will be <code>undefined</code>
|
||||
and the call will result in a runtime error.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The easiest way to prevent this is to turn <code>actions</code> into a <code>Map</code> and using
|
||||
<code>Map.prototype.has</code> to check whether the method name is valid before looking it up.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnvalidatedDynamicMethodCallGood.js" />
|
||||
|
||||
<p>
|
||||
If <code>actions</code> cannot be turned into a <code>Map</code>, a <code>hasOwnProperty</code>
|
||||
check should be added to validate the method name:
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnvalidatedDynamicMethodCallGood2.js" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Denial_of_Service">Denial of Service</a>.
|
||||
</li>
|
||||
<li>
|
||||
MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">Map</a>.
|
||||
</li>
|
||||
<li>
|
||||
MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype">Object.prototype</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Unvalidated dynamic method call
|
||||
* @description Calling a method with a user-controlled name may dispatch to
|
||||
* an unexpected target, which could cause an exception.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id js/unvalidated-dynamic-method-call
|
||||
* @tags security
|
||||
* external/cwe/cwe-754
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.UnvalidatedDynamicMethodCall::UnvalidatedDynamicMethodCall
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"Invocation of method with $@ name may dispatch to unexpected target and cause an exception.",
|
||||
source.getNode(), "user-controlled"
|
||||
@@ -0,0 +1,16 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
var actions = {
|
||||
play(data) {
|
||||
// ...
|
||||
},
|
||||
pause(data) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/perform/:action/:payload', function(req, res) {
|
||||
let action = actions[req.params.action];
|
||||
res.end(action(req.params.payload));
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
var actions = new Map();
|
||||
actions.put("play", function (data) {
|
||||
// ...
|
||||
});
|
||||
actions.put("pause", function(data) {
|
||||
// ...
|
||||
});
|
||||
|
||||
app.get('/perform/:action/:payload', function(req, res) {
|
||||
if (actions.has(req.params.action)) {
|
||||
let action = actions.get(req.params.action);
|
||||
res.end(action(req.params.payload));
|
||||
} else {
|
||||
res.end("Unsupported action.");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
var actions = {
|
||||
play(data) {
|
||||
// ...
|
||||
},
|
||||
pause(data) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/perform/:action/:payload', function(req, res) {
|
||||
if (actions.hasOwnProperty(req.params.action)) {
|
||||
let action = actions[req.params.action];
|
||||
if (typeof action === 'function') {
|
||||
res.end(action(req.params.payload));
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.end("Unsupported action.");
|
||||
});
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about unvalidated dynamic
|
||||
* method calls.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.frameworks.Express
|
||||
import PropertyInjectionShared
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
module UnvalidatedDynamicMethodCall {
|
||||
private import DataFlow::FlowLabel
|
||||
|
||||
/**
|
||||
* A data flow source for unvalidated dynamic method calls.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the flow label relevant for this source.
|
||||
*/
|
||||
DataFlow::FlowLabel getFlowLabel() {
|
||||
result = data()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for unvalidated dynamic method calls.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the flow label relevant for this sink
|
||||
*/
|
||||
abstract DataFlow::FlowLabel getFlowLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for unvalidated dynamic method calls.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node {
|
||||
abstract predicate sanitizes(DataFlow::Node source, DataFlow::Node sink, DataFlow::FlowLabel lbl);
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow label describing values read from a user-controlled property that
|
||||
* may not be functions.
|
||||
*/
|
||||
private class MaybeNonFunction extends DataFlow::FlowLabel {
|
||||
MaybeNonFunction() { this = "MaybeNonFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow label describing values read from a user-controlled property that
|
||||
* may originate from a prototype object.
|
||||
*/
|
||||
private class MaybeFromProto extends DataFlow::FlowLabel {
|
||||
MaybeFromProto() { this = "MaybeFromProto" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about unvalidated dynamic method calls.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "UnvalidatedDynamicMethodCall" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
|
||||
source.(Source).getFlowLabel() = label
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
|
||||
sink.(Sink).getFlowLabel() = label
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node nd) {
|
||||
super.isSanitizer(nd) or
|
||||
nd instanceof PropertyInjection::Sanitizer
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, DataFlow::FlowLabel dstlabel) {
|
||||
exists (DataFlow::PropRead read |
|
||||
src = read.getPropertyNameExpr().flow() and
|
||||
dst = read and
|
||||
(srclabel = data() or srclabel = taint()) and
|
||||
(dstlabel instanceof MaybeNonFunction
|
||||
or
|
||||
// a property of `Object.create(null)` cannot come from a prototype
|
||||
not PropertyInjection::isPrototypeLessObject(read.getBase().getALocalSource()) and
|
||||
dstlabel instanceof MaybeFromProto) and
|
||||
// avoid overlapping results with unsafe dynamic method access query
|
||||
not PropertyInjection::hasUnsafeMethods(read.getBase().getALocalSource())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a source for unvalidated dynamic method calls.
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* The page URL considered as a flow source for unvalidated dynamic method calls.
|
||||
*/
|
||||
class DocumentUrlAsSource extends Source {
|
||||
DocumentUrlAsSource() { isDocumentURL(asExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function invocation of an unsafe function, as a sink for remote unvalidated dynamic method calls.
|
||||
*/
|
||||
class CalleeAsSink extends Sink {
|
||||
InvokeExpr invk;
|
||||
|
||||
CalleeAsSink() {
|
||||
this = invk.getCallee().flow() and
|
||||
// don't flag invocations inside a try-catch
|
||||
not invk.getASuccessor() instanceof CatchClause
|
||||
}
|
||||
|
||||
override DataFlow::FlowLabel getFlowLabel() {
|
||||
result instanceof MaybeNonFunction and
|
||||
// don't flag if the type inference can prove that it is a function;
|
||||
// this complements the `FunctionCheck` sanitizer below: the type inference can
|
||||
// detect more checks locally, but doesn't provide inter-procedural reasoning
|
||||
this.analyze().getAType() != TTFunction()
|
||||
or
|
||||
result instanceof MaybeFromProto
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A check of the form `typeof x === 'function'`, which sanitizes away the `MaybeNonFunction`
|
||||
* taint kind.
|
||||
*/
|
||||
class FunctionCheck extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode {
|
||||
override EqualityTest astNode;
|
||||
TypeofExpr t;
|
||||
|
||||
FunctionCheck() {
|
||||
astNode.getAnOperand().getStringValue() = "function" and
|
||||
astNode.getAnOperand().getUnderlyingValue() = t
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
outcome = astNode.getPolarity() and
|
||||
e = t.getOperand().getUnderlyingValue()
|
||||
}
|
||||
|
||||
override DataFlow::FlowLabel getALabel() {
|
||||
result instanceof MaybeNonFunction
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// copied from tests for `UnsafeDynamicMethodAccess.ql` to check that they do not overlap
|
||||
|
||||
let obj = {};
|
||||
|
||||
window.addEventListener('message', (ev) => {
|
||||
let message = JSON.parse(ev.data);
|
||||
window[message.name](message.payload); // NOT OK, but reported by UnsafeDynamicMethodAccess.ql
|
||||
new window[message.name](message.payload); // NOT OK, but reported by UnsafeDynamicMethodAccess.ql
|
||||
window["HTMLElement" + message.name](message.payload); // OK - concatenation restricts choice of methods
|
||||
window[`HTMLElement${message.name}`](message.payload); // OK - concatenation restricts choice of methods
|
||||
|
||||
function f() {}
|
||||
f[message.name](message.payload)(); // NOT OK, but reported by UnsafeDynamicMethodAccess.ql
|
||||
|
||||
obj[message.name](message.payload); // NOT OK
|
||||
|
||||
window[ev](ev); // NOT OK, but reported by UnsafeDynamicMethodAccess.ql
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
nodes
|
||||
| UnsafeDynamicMethodAccess.js:5:37:5:38 | ev |
|
||||
| UnsafeDynamicMethodAccess.js:6:9:6:37 | message |
|
||||
| UnsafeDynamicMethodAccess.js:6:19:6:37 | JSON.parse(ev.data) |
|
||||
| UnsafeDynamicMethodAccess.js:6:30:6:31 | ev |
|
||||
| UnsafeDynamicMethodAccess.js:6:30:6:36 | ev.data |
|
||||
| UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] |
|
||||
| UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] |
|
||||
| UnsafeDynamicMethodAccess.js:15:9:15:15 | message |
|
||||
| UnsafeDynamicMethodAccess.js:15:9:15:20 | message.name |
|
||||
| UnvalidatedDynamicMethodCall.js:14:7:14:41 | action |
|
||||
| UnvalidatedDynamicMethodCall.js:14:7:14:41 | action |
|
||||
| UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] |
|
||||
| UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] |
|
||||
| UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action |
|
||||
| UnvalidatedDynamicMethodCall.js:15:11:15:16 | action |
|
||||
| UnvalidatedDynamicMethodCall.js:15:11:15:16 | action |
|
||||
| tst.js:6:39:6:40 | ev |
|
||||
| tst.js:7:9:7:39 | name |
|
||||
| tst.js:7:16:7:34 | JSON.parse(ev.data) |
|
||||
| tst.js:7:16:7:39 | JSON.pa ... a).name |
|
||||
| tst.js:7:27:7:28 | ev |
|
||||
| tst.js:7:27:7:33 | ev.data |
|
||||
| tst.js:9:5:9:16 | obj[ev.data] |
|
||||
| tst.js:9:5:9:16 | obj[ev.data] |
|
||||
| tst.js:9:9:9:10 | ev |
|
||||
| tst.js:9:9:9:15 | ev.data |
|
||||
| tst.js:11:5:11:13 | obj[name] |
|
||||
| tst.js:11:5:11:13 | obj[name] |
|
||||
| tst.js:11:9:11:12 | name |
|
||||
| tst.js:17:9:17:22 | fn |
|
||||
| tst.js:17:9:17:22 | fn |
|
||||
| tst.js:17:14:17:22 | obj[name] |
|
||||
| tst.js:17:14:17:22 | obj[name] |
|
||||
| tst.js:17:18:17:21 | name |
|
||||
| tst.js:18:5:18:6 | fn |
|
||||
| tst.js:18:5:18:6 | fn |
|
||||
| tst.js:19:9:19:31 | fn |
|
||||
| tst.js:20:7:20:8 | fn |
|
||||
| tst.js:21:7:21:15 | obj[name] |
|
||||
| tst.js:21:7:21:15 | obj[name] |
|
||||
| tst.js:21:11:21:14 | name |
|
||||
| tst.js:22:11:22:12 | fn |
|
||||
| tst.js:26:7:26:15 | obj[name] |
|
||||
| tst.js:26:7:26:15 | obj[name] |
|
||||
| tst.js:26:11:26:14 | name |
|
||||
| tst.js:28:7:28:15 | obj[name] |
|
||||
| tst.js:28:7:28:15 | obj[name] |
|
||||
| tst.js:28:11:28:14 | name |
|
||||
| tst.js:47:39:47:40 | ev |
|
||||
| tst.js:48:9:48:39 | name |
|
||||
| tst.js:48:16:48:34 | JSON.parse(ev.data) |
|
||||
| tst.js:48:16:48:39 | JSON.pa ... a).name |
|
||||
| tst.js:48:27:48:28 | ev |
|
||||
| tst.js:48:27:48:33 | ev.data |
|
||||
| tst.js:49:9:49:23 | fn |
|
||||
| tst.js:49:14:49:23 | obj2[name] |
|
||||
| tst.js:49:19:49:22 | name |
|
||||
| tst.js:50:5:50:6 | fn |
|
||||
edges
|
||||
| UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | UnsafeDynamicMethodAccess.js:6:30:6:31 | ev |
|
||||
| UnsafeDynamicMethodAccess.js:6:9:6:37 | message | UnsafeDynamicMethodAccess.js:15:9:15:15 | message |
|
||||
| UnsafeDynamicMethodAccess.js:6:19:6:37 | JSON.parse(ev.data) | UnsafeDynamicMethodAccess.js:6:9:6:37 | message |
|
||||
| UnsafeDynamicMethodAccess.js:6:30:6:31 | ev | UnsafeDynamicMethodAccess.js:6:30:6:36 | ev.data |
|
||||
| UnsafeDynamicMethodAccess.js:6:30:6:36 | ev.data | UnsafeDynamicMethodAccess.js:6:19:6:37 | JSON.parse(ev.data) |
|
||||
| UnsafeDynamicMethodAccess.js:15:9:15:15 | message | UnsafeDynamicMethodAccess.js:15:9:15:20 | message.name |
|
||||
| UnsafeDynamicMethodAccess.js:15:9:15:20 | message.name | UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] |
|
||||
| UnsafeDynamicMethodAccess.js:15:9:15:20 | message.name | UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] |
|
||||
| UnvalidatedDynamicMethodCall.js:14:7:14:41 | action | UnvalidatedDynamicMethodCall.js:15:11:15:16 | action |
|
||||
| UnvalidatedDynamicMethodCall.js:14:7:14:41 | action | UnvalidatedDynamicMethodCall.js:15:11:15:16 | action |
|
||||
| UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] | UnvalidatedDynamicMethodCall.js:14:7:14:41 | action |
|
||||
| UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] | UnvalidatedDynamicMethodCall.js:14:7:14:41 | action |
|
||||
| UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] |
|
||||
| UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] |
|
||||
| tst.js:6:39:6:40 | ev | tst.js:7:27:7:28 | ev |
|
||||
| tst.js:6:39:6:40 | ev | tst.js:9:9:9:10 | ev |
|
||||
| tst.js:7:9:7:39 | name | tst.js:11:9:11:12 | name |
|
||||
| tst.js:7:9:7:39 | name | tst.js:17:18:17:21 | name |
|
||||
| tst.js:7:9:7:39 | name | tst.js:21:11:21:14 | name |
|
||||
| tst.js:7:9:7:39 | name | tst.js:26:11:26:14 | name |
|
||||
| tst.js:7:9:7:39 | name | tst.js:28:11:28:14 | name |
|
||||
| tst.js:7:16:7:34 | JSON.parse(ev.data) | tst.js:7:16:7:39 | JSON.pa ... a).name |
|
||||
| tst.js:7:16:7:39 | JSON.pa ... a).name | tst.js:7:9:7:39 | name |
|
||||
| tst.js:7:27:7:28 | ev | tst.js:7:27:7:33 | ev.data |
|
||||
| tst.js:7:27:7:33 | ev.data | tst.js:7:16:7:34 | JSON.parse(ev.data) |
|
||||
| tst.js:9:9:9:10 | ev | tst.js:9:9:9:15 | ev.data |
|
||||
| tst.js:9:9:9:15 | ev.data | tst.js:9:5:9:16 | obj[ev.data] |
|
||||
| tst.js:9:9:9:15 | ev.data | tst.js:9:5:9:16 | obj[ev.data] |
|
||||
| tst.js:11:9:11:12 | name | tst.js:11:5:11:13 | obj[name] |
|
||||
| tst.js:11:9:11:12 | name | tst.js:11:5:11:13 | obj[name] |
|
||||
| tst.js:17:9:17:22 | fn | tst.js:18:5:18:6 | fn |
|
||||
| tst.js:17:9:17:22 | fn | tst.js:18:5:18:6 | fn |
|
||||
| tst.js:17:9:17:22 | fn | tst.js:19:9:19:31 | fn |
|
||||
| tst.js:17:14:17:22 | obj[name] | tst.js:17:9:17:22 | fn |
|
||||
| tst.js:17:14:17:22 | obj[name] | tst.js:17:9:17:22 | fn |
|
||||
| tst.js:17:18:17:21 | name | tst.js:17:14:17:22 | obj[name] |
|
||||
| tst.js:17:18:17:21 | name | tst.js:17:14:17:22 | obj[name] |
|
||||
| tst.js:19:9:19:31 | fn | tst.js:20:7:20:8 | fn |
|
||||
| tst.js:19:9:19:31 | fn | tst.js:22:11:22:12 | fn |
|
||||
| tst.js:21:11:21:14 | name | tst.js:21:7:21:15 | obj[name] |
|
||||
| tst.js:21:11:21:14 | name | tst.js:21:7:21:15 | obj[name] |
|
||||
| tst.js:26:11:26:14 | name | tst.js:26:7:26:15 | obj[name] |
|
||||
| tst.js:26:11:26:14 | name | tst.js:26:7:26:15 | obj[name] |
|
||||
| tst.js:28:11:28:14 | name | tst.js:28:7:28:15 | obj[name] |
|
||||
| tst.js:28:11:28:14 | name | tst.js:28:7:28:15 | obj[name] |
|
||||
| tst.js:47:39:47:40 | ev | tst.js:48:27:48:28 | ev |
|
||||
| tst.js:48:9:48:39 | name | tst.js:49:19:49:22 | name |
|
||||
| tst.js:48:16:48:34 | JSON.parse(ev.data) | tst.js:48:16:48:39 | JSON.pa ... a).name |
|
||||
| tst.js:48:16:48:39 | JSON.pa ... a).name | tst.js:48:9:48:39 | name |
|
||||
| tst.js:48:27:48:28 | ev | tst.js:48:27:48:33 | ev.data |
|
||||
| tst.js:48:27:48:33 | ev.data | tst.js:48:16:48:34 | JSON.parse(ev.data) |
|
||||
| tst.js:49:9:49:23 | fn | tst.js:50:5:50:6 | fn |
|
||||
| tst.js:49:14:49:23 | obj2[name] | tst.js:49:9:49:23 | fn |
|
||||
| tst.js:49:19:49:22 | name | tst.js:49:14:49:23 | obj2[name] |
|
||||
#select
|
||||
| UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | user-controlled |
|
||||
| UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | user-controlled |
|
||||
| UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | user-controlled |
|
||||
| UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | user-controlled |
|
||||
| tst.js:9:5:9:16 | obj[ev.data] | tst.js:6:39:6:40 | ev | tst.js:9:5:9:16 | obj[ev.data] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:9:5:9:16 | obj[ev.data] | tst.js:6:39:6:40 | ev | tst.js:9:5:9:16 | obj[ev.data] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:11:5:11:13 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:11:5:11:13 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:11:5:11:13 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:11:5:11:13 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:18:5:18:6 | fn | tst.js:6:39:6:40 | ev | tst.js:18:5:18:6 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:18:5:18:6 | fn | tst.js:6:39:6:40 | ev | tst.js:18:5:18:6 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:20:7:20:8 | fn | tst.js:6:39:6:40 | ev | tst.js:20:7:20:8 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:21:7:21:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:21:7:21:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:21:7:21:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:21:7:21:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:22:11:22:12 | fn | tst.js:6:39:6:40 | ev | tst.js:22:11:22:12 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:26:7:26:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:26:7:26:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:26:7:26:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:26:7:26:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:28:7:28:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:28:7:28:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:28:7:28:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:28:7:28:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled |
|
||||
| tst.js:50:5:50:6 | fn | tst.js:47:39:47:40 | ev | tst.js:50:5:50:6 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:47:39:47:40 | ev | user-controlled |
|
||||
@@ -0,0 +1,16 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
var actions = {
|
||||
play(data) {
|
||||
// ...
|
||||
},
|
||||
pause(data) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/perform/:action/:payload', function(req, res) {
|
||||
let action = actions[req.params.action];
|
||||
res.end(action(req.params.payload));
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-754/UnvalidatedDynamicMethodCall.ql
|
||||
@@ -0,0 +1,19 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
var actions = new Map();
|
||||
actions.put("play", function (data) {
|
||||
// ...
|
||||
});
|
||||
actions.put("pause", function(data) {
|
||||
// ...
|
||||
});
|
||||
|
||||
app.get('/perform/:action/:payload', function(req, res) {
|
||||
if (actions.has(req.params.action)) {
|
||||
let action = actions.get(req.params.action);
|
||||
res.end(action(req.params.payload));
|
||||
} else {
|
||||
res.end("Unsupported action.");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
var actions = {
|
||||
play(data) {
|
||||
// ...
|
||||
},
|
||||
pause(data) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/perform/:action/:payload', function(req, res) {
|
||||
if (actions.hasOwnProperty(req.params.action)) {
|
||||
let action = actions[req.params.action];
|
||||
if (typeof action === 'function') {
|
||||
res.end(action(req.params.payload));
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.end("Unsupported action.");
|
||||
});
|
||||
54
javascript/ql/test/query-tests/Security/CWE-754/tst.js
Normal file
54
javascript/ql/test/query-tests/Security/CWE-754/tst.js
Normal file
@@ -0,0 +1,54 @@
|
||||
(function() {
|
||||
let obj = {
|
||||
foo() {}
|
||||
};
|
||||
|
||||
window.addEventListener('message', (ev) => {
|
||||
let name = JSON.parse(ev.data).name;
|
||||
|
||||
obj[ev.data](); // NOT OK: might not be a function
|
||||
|
||||
obj[name](); // NOT OK: might not be a function
|
||||
|
||||
try {
|
||||
obj[name](); // OK: exception is caught
|
||||
} catch(e) {}
|
||||
|
||||
let fn = obj[name];
|
||||
fn(); // NOT OK: might not be a function
|
||||
if (typeof fn == 'function') {
|
||||
fn(); // NOT OK: might be `valueOf`
|
||||
obj[name](); // NOT OK: might be `__defineSetter__`
|
||||
new fn(); // NOT OK: might be `valueOf` or `toString`
|
||||
}
|
||||
|
||||
if (obj[name])
|
||||
obj[name](); // NOT OK
|
||||
if (typeof obj[name] === 'function')
|
||||
obj[name](); // NOT OK
|
||||
|
||||
if (obj.hasOwnProperty(name)) {
|
||||
obj[name](); // NOT OK, but not flagged
|
||||
}
|
||||
|
||||
let key = "$" + name;
|
||||
obj[key](); // NOT OK, but not flagged
|
||||
if (typeof obj[key] === 'function')
|
||||
obj[key](); // OK
|
||||
|
||||
if (typeof fn === 'function') {
|
||||
fn.apply(obj); // OK
|
||||
}
|
||||
});
|
||||
|
||||
let obj2 = Object.create(null);
|
||||
obj2.foo = function() {};
|
||||
|
||||
window.addEventListener('message', (ev) => {
|
||||
let name = JSON.parse(ev.data).name;
|
||||
let fn = obj2[name];
|
||||
fn(); // NOT OK: might not be a function
|
||||
if (typeof fn == 'function')
|
||||
fn(); // OK: cannot be from prototype
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user