mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #118 from esben-semmle/js/request-forgery
Approved by asger-semmle
This commit is contained in:
@@ -29,3 +29,4 @@
|
||||
+ semmlecode-javascript-queries/Security/CWE-807/DifferentKindsComparisonBypass.ql: /Security/CWE/CWE-807
|
||||
+ semmlecode-javascript-queries/Security/CWE-843/TypeConfusionThroughParameterTampering.ql: /Security/CWE/CWE-834
|
||||
+ semmlecode-javascript-queries/Security/CWE-916/InsufficientPasswordHash.ql: /Security/CWE/CWE-916
|
||||
+ semmlecode-javascript-queries/Security/CWE-918/RequestForgery.ql: /Security/CWE/CWE-918
|
||||
|
||||
79
javascript/ql/src/Security/CWE-918/RequestForgery.qhelp
Normal file
79
javascript/ql/src/Security/CWE-918/RequestForgery.qhelp
Normal file
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Directly incorporating user input into an HTTP request
|
||||
without validating the input can facilitate different kinds of request
|
||||
forgery attacks, where the attacker essentially controls the request.
|
||||
|
||||
If the vulnerable request is in server-side code, then security
|
||||
mechanisms, such as external firewalls, can be bypassed.
|
||||
|
||||
If the vulnerable request is in client-side code, then unsuspecting
|
||||
users can send malicious requests to other servers, potentially
|
||||
resulting in a DDOS attack.
|
||||
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
|
||||
To guard against request forgery, it is advisable to avoid
|
||||
putting user input directly into a remote request. If a flexible
|
||||
remote request mechanism is required, it is recommended to maintain a
|
||||
list of authorized request targets and choose from that list based on
|
||||
the user input provided.
|
||||
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
The following example shows an HTTP request parameter
|
||||
being used directly in a URL request without validating the input,
|
||||
which facilitates an SSRF attack. The request
|
||||
<code>http.get(...)</code> is vulnerable since attackers can choose
|
||||
the value of <code>target</code> to be anything they want. For
|
||||
instance, the attacker can choose
|
||||
<code>"internal.example.com/#"</code> as the target, causing the URL
|
||||
used in the request to be
|
||||
<code>"https://internal.example.com/#.example.com/data"</code>.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
A request to <code>https://internal.example.com</code> may
|
||||
be problematic if that server is not meant to be
|
||||
directly accessible from the attacker's machine.
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/RequestForgeryBad.js"/>
|
||||
|
||||
<p>
|
||||
|
||||
One way to remedy the problem is to use the user input to
|
||||
select a known fixed string before performing the request:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/RequestForgeryGood.js"/>
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Server_Side_Request_Forgery">SSRF</a></li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
18
javascript/ql/src/Security/CWE-918/RequestForgery.ql
Normal file
18
javascript/ql/src/Security/CWE-918/RequestForgery.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Uncontrolled data used in remote request
|
||||
* @description Sending remote requests with user-controlled data allows for request forgery attacks.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
* @id js/request-forgery
|
||||
* @tags security
|
||||
* external/cwe/cwe-918
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.RequestForgery::RequestForgery
|
||||
|
||||
from Configuration cfg, DataFlow::Node source, Sink sink, DataFlow::Node request
|
||||
where cfg.hasFlow(source, sink) and
|
||||
request = sink.getARequest()
|
||||
select request, "The $@ of this request depends on $@.", sink, sink.getKind(), source, "a user-provided value"
|
||||
@@ -0,0 +1,12 @@
|
||||
import http from 'http';
|
||||
import url from 'url';
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var target = url.parse(request.url, true).query.target;
|
||||
|
||||
// BAD: `target` is controlled by the attacker
|
||||
http.get('https://' + target + ".example.com/data/", res => {
|
||||
// process request response ...
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import http from 'http';
|
||||
import url from 'url';
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var target = url.parse(request.url, true).query.target;
|
||||
|
||||
var subdomain;
|
||||
if (target === 'EU') {
|
||||
subdomain = "europe"
|
||||
} else {
|
||||
subdomain = "world"
|
||||
}
|
||||
|
||||
// GOOD: `subdomain` is controlled by the server
|
||||
http.get('https://' + subdomain + ".example.com/data/", res => {
|
||||
// process request response ...
|
||||
});
|
||||
|
||||
});
|
||||
@@ -54,6 +54,7 @@ import semmle.javascript.frameworks.AWS
|
||||
import semmle.javascript.frameworks.Azure
|
||||
import semmle.javascript.frameworks.Babel
|
||||
import semmle.javascript.frameworks.ComposedFunctions
|
||||
import semmle.javascript.frameworks.ClientRequests
|
||||
import semmle.javascript.frameworks.Credentials
|
||||
import semmle.javascript.frameworks.CryptoLibraries
|
||||
import semmle.javascript.frameworks.DigitalOcean
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Provides classes for modelling the client-side of a URL request.
|
||||
*
|
||||
* Subclass `ClientRequest` to refine the behavior of the analysis on existing client requests.
|
||||
* Subclass `CustomClientRequest` to introduce new kinds of client requests.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A call that performs a request to a URL.
|
||||
*/
|
||||
abstract class CustomClientRequest extends DataFlow::InvokeNode {
|
||||
|
||||
/**
|
||||
* Gets the URL of the request.
|
||||
*/
|
||||
abstract DataFlow::Node getUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that performs a request to a URL.
|
||||
*/
|
||||
class ClientRequest extends DataFlow::InvokeNode {
|
||||
|
||||
CustomClientRequest custom;
|
||||
|
||||
ClientRequest() {
|
||||
this = custom
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL of the request.
|
||||
*/
|
||||
DataFlow::Node getUrl() {
|
||||
result = custom.getUrl()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets name of an HTTP request method, in all-lowercase.
|
||||
*/
|
||||
private string httpMethodName() {
|
||||
result = any(HTTP::RequestMethodName m).toLowerCase()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of a property that likely contains a URL value.
|
||||
*/
|
||||
private string urlPropertyName() {
|
||||
result = "uri" or
|
||||
result = "url"
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using the `request` library.
|
||||
*/
|
||||
private class RequestUrlRequest extends CustomClientRequest {
|
||||
|
||||
DataFlow::Node url;
|
||||
|
||||
RequestUrlRequest() {
|
||||
exists (string moduleName, DataFlow::SourceNode callee |
|
||||
this = callee.getACall() |
|
||||
(
|
||||
moduleName = "request" or
|
||||
moduleName = "request-promise" or
|
||||
moduleName = "request-promise-any" or
|
||||
moduleName = "request-promise-native"
|
||||
) and
|
||||
(
|
||||
callee = DataFlow::moduleImport(moduleName) or
|
||||
callee = DataFlow::moduleMember(moduleName, httpMethodName())
|
||||
) and
|
||||
(
|
||||
url = getArgument(0) or
|
||||
url = getOptionArgument(0, urlPropertyName())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using the `axios` library.
|
||||
*/
|
||||
private class AxiosUrlRequest extends CustomClientRequest {
|
||||
|
||||
DataFlow::Node url;
|
||||
|
||||
AxiosUrlRequest() {
|
||||
exists (string moduleName, DataFlow::SourceNode callee |
|
||||
this = callee.getACall() |
|
||||
moduleName = "axios" and
|
||||
(
|
||||
callee = DataFlow::moduleImport(moduleName) or
|
||||
callee = DataFlow::moduleMember(moduleName, httpMethodName()) or
|
||||
callee = DataFlow::moduleMember(moduleName, "request")
|
||||
) and
|
||||
(
|
||||
url = getArgument(0) or
|
||||
// depends on the method name and the call arity, over-approximating slightly in the name of simplicity
|
||||
url = getOptionArgument([0..2], urlPropertyName())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using an implementation of the `fetch` API.
|
||||
*/
|
||||
private class FetchUrlRequest extends CustomClientRequest {
|
||||
|
||||
DataFlow::Node url;
|
||||
|
||||
FetchUrlRequest() {
|
||||
exists (string moduleName, DataFlow::SourceNode callee |
|
||||
this = callee.getACall() |
|
||||
(
|
||||
moduleName = "node-fetch" or
|
||||
moduleName = "cross-fetch" or
|
||||
moduleName = "isomorphic-fetch"
|
||||
) and
|
||||
callee = DataFlow::moduleImport(moduleName) and
|
||||
url = getArgument(0)
|
||||
)
|
||||
or
|
||||
(
|
||||
this = DataFlow::globalVarRef("fetch").getACall() and
|
||||
url = getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using the `got` library.
|
||||
*/
|
||||
private class GotUrlRequest extends CustomClientRequest {
|
||||
|
||||
DataFlow::Node url;
|
||||
|
||||
GotUrlRequest() {
|
||||
exists (string moduleName, DataFlow::SourceNode callee |
|
||||
this = callee.getACall() |
|
||||
moduleName = "got" and
|
||||
(
|
||||
callee = DataFlow::moduleImport(moduleName) or
|
||||
callee = DataFlow::moduleMember(moduleName, "stream")
|
||||
) and
|
||||
url = getArgument(0) and not exists (getOptionArgument(1, "baseUrl"))
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using the `superagent` library.
|
||||
*/
|
||||
private class SuperAgentUrlRequest extends CustomClientRequest {
|
||||
|
||||
DataFlow::Node url;
|
||||
|
||||
SuperAgentUrlRequest() {
|
||||
exists (string moduleName, DataFlow::SourceNode callee |
|
||||
this = callee.getACall() |
|
||||
moduleName = "superagent" and
|
||||
callee = DataFlow::moduleMember(moduleName, httpMethodName()) and
|
||||
url = getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = url
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,37 +33,51 @@ module Electron {
|
||||
this = DataFlow::moduleMember("electron", "BrowserView").getAnInstantiation()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A Node.js-style HTTP or HTTPS request made using an Electron module.
|
||||
*/
|
||||
abstract class ClientRequest extends NodeJSLib::ClientRequest {}
|
||||
abstract class CustomElectronClientRequest extends NodeJSLib::CustomNodeJSClientRequest {}
|
||||
|
||||
/**
|
||||
* A Node.js-style HTTP or HTTPS request made using an Electron module.
|
||||
*/
|
||||
class ElectronClientRequest extends NodeJSLib::NodeJSClientRequest {
|
||||
|
||||
ElectronClientRequest() {
|
||||
this instanceof CustomElectronClientRequest
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A Node.js-style HTTP or HTTPS request made using `electron.net`, for example `net.request(url)`.
|
||||
*/
|
||||
private class NetRequest extends ClientRequest {
|
||||
private class NetRequest extends CustomElectronClientRequest {
|
||||
NetRequest() {
|
||||
this = DataFlow::moduleMember("electron", "net").getAMemberCall("request")
|
||||
}
|
||||
|
||||
override DataFlow::Node getOptions() {
|
||||
result = this.(DataFlow::MethodCallNode).getArgument(0)
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = getArgument(0) or
|
||||
result = getOptionArgument(0, "url")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A Node.js-style HTTP or HTTPS request made using `electron.client`, for example `new client(url)`.
|
||||
*/
|
||||
private class NewClientRequest extends ClientRequest {
|
||||
private class NewClientRequest extends CustomElectronClientRequest {
|
||||
NewClientRequest() {
|
||||
this = DataFlow::moduleMember("electron", "ClientRequest").getAnInstantiation()
|
||||
}
|
||||
|
||||
override DataFlow::Node getOptions() {
|
||||
result = this.(DataFlow::NewNode).getArgument(0)
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = getArgument(0) or
|
||||
result = getOptionArgument(0, "url")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -75,12 +89,12 @@ module Electron {
|
||||
exists(NodeJSLib::ClientRequestHandler handler |
|
||||
this = handler.getParameter(0) and
|
||||
handler.getAHandledEvent() = "redirect" and
|
||||
handler.getClientRequest() instanceof ClientRequest
|
||||
handler.getClientRequest() instanceof ElectronClientRequest
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() {
|
||||
result = "Electron ClientRequest redirect event"
|
||||
result = "ElectronClientRequest redirect event"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,53 +502,47 @@ module NodeJSLib {
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is an HTTP or HTTPS client request made by a Node.js server, for example `http.request(url)`.
|
||||
* A data flow node that is an HTTP or HTTPS client request made by a Node.js application, for example `http.request(url)`.
|
||||
*/
|
||||
abstract class ClientRequest extends DataFlow::DefaultSourceNode {
|
||||
/**
|
||||
* Gets the options object or string URL used to make the request.
|
||||
*/
|
||||
abstract DataFlow::Node getOptions();
|
||||
abstract class CustomNodeJSClientRequest extends CustomClientRequest {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is an HTTP or HTTPS client request made by a Node.js application, for example `http.request(url)`.
|
||||
*/
|
||||
class NodeJSClientRequest extends ClientRequest {
|
||||
|
||||
NodeJSClientRequest() {
|
||||
this instanceof CustomNodeJSClientRequest
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is an HTTP or HTTPS client request made by a Node.js server, for example `http.request(url)`.
|
||||
* A model of a URL request in the Node.js `http` library.
|
||||
*/
|
||||
private class HttpRequest extends ClientRequest {
|
||||
HttpRequest() {
|
||||
exists(string protocol |
|
||||
private class NodeHttpUrlRequest extends CustomNodeJSClientRequest {
|
||||
|
||||
DataFlow::Node url;
|
||||
|
||||
NodeHttpUrlRequest() {
|
||||
exists (string moduleName, DataFlow::SourceNode callee |
|
||||
this = callee.getACall() |
|
||||
(moduleName = "http" or moduleName = "https") and
|
||||
(
|
||||
protocol = "http" or
|
||||
protocol = "https"
|
||||
)
|
||||
and
|
||||
this = DataFlow::moduleImport(protocol).getAMemberCall("request")
|
||||
callee = DataFlow::moduleMember(moduleName, any(HTTP::RequestMethodName m).toLowerCase())
|
||||
or
|
||||
callee = DataFlow::moduleMember(moduleName, "request")
|
||||
) and
|
||||
url = getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getOptions() {
|
||||
result = this.(DataFlow::MethodCallNode).getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is an HTTP or HTTPS client request made by a Node.js process, for example `https.get(url)`.
|
||||
*/
|
||||
private class HttpGet extends ClientRequest {
|
||||
HttpGet() {
|
||||
exists(string protocol |
|
||||
(
|
||||
protocol = "http" or
|
||||
protocol = "https"
|
||||
)
|
||||
and
|
||||
this = DataFlow::moduleImport(protocol).getAMemberCall("get")
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getOptions() {
|
||||
result = this.(DataFlow::MethodCallNode).getArgument(0)
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -556,13 +550,13 @@ module NodeJSLib {
|
||||
*/
|
||||
private class ClientRequestCallbackParam extends DataFlow::ParameterNode, RemoteFlowSource {
|
||||
ClientRequestCallbackParam() {
|
||||
exists(ClientRequest req |
|
||||
exists(NodeJSClientRequest req |
|
||||
this = req.(DataFlow::MethodCallNode).getCallback(1).getParameter(0)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() {
|
||||
result = "ClientRequest callback parameter"
|
||||
result = "NodeJSClientRequest callback parameter"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,7 +583,7 @@ module NodeJSLib {
|
||||
*/
|
||||
class ClientRequestHandler extends DataFlow::FunctionNode {
|
||||
string handledEvent;
|
||||
ClientRequest clientRequest;
|
||||
NodeJSClientRequest clientRequest;
|
||||
|
||||
ClientRequestHandler() {
|
||||
exists(DataFlow::MethodCallNode mcn |
|
||||
@@ -609,7 +603,7 @@ module NodeJSLib {
|
||||
/**
|
||||
* Gets a request this callback is registered for.
|
||||
*/
|
||||
ClientRequest getClientRequest() {
|
||||
NodeJSClientRequest getClientRequest() {
|
||||
result = clientRequest
|
||||
}
|
||||
}
|
||||
@@ -626,7 +620,7 @@ module NodeJSLib {
|
||||
}
|
||||
|
||||
override string getSourceType() {
|
||||
result = "ClientRequest response event"
|
||||
result = "NodeJSClientRequest response event"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,7 +637,7 @@ module NodeJSLib {
|
||||
}
|
||||
|
||||
override string getSourceType() {
|
||||
result = "ClientRequest data event"
|
||||
result = "NodeJSClientRequest data event"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +661,7 @@ module NodeJSLib {
|
||||
}
|
||||
|
||||
override string getSourceType() {
|
||||
result = "ClientRequest login event"
|
||||
result = "NodeJSClientRequest login event"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,7 +719,7 @@ module NodeJSLib {
|
||||
}
|
||||
|
||||
override string getSourceType() {
|
||||
result = "ClientRequest error event"
|
||||
result = "NodeJSClientRequest error event"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about request forgery.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.RemoteFlowSources
|
||||
import UrlConcatenation
|
||||
|
||||
module RequestForgery {
|
||||
|
||||
/**
|
||||
* A data flow source for request forgery.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for request forgery.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a request that uses this sink.
|
||||
*/
|
||||
abstract DataFlow::Node getARequest();
|
||||
|
||||
/**
|
||||
* Gets the kind of this sink.
|
||||
*/
|
||||
abstract string getKind();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for request forgery.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A taint tracking configuration for request forgery.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() {
|
||||
this = "RequestForgery"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof Source
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof Sink
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof Sanitizer
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node source, DataFlow::Node sink) {
|
||||
sanitizingPrefixEdge(source, sink)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A source of remote user input, considered as a flow source for request forgery. */
|
||||
private class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL of a URL request, viewed as a sink for request forgery.
|
||||
*/
|
||||
private class ClientRequestUrlAsSink extends Sink {
|
||||
|
||||
ClientRequest request;
|
||||
|
||||
ClientRequestUrlAsSink() {
|
||||
this = request.getUrl()
|
||||
}
|
||||
|
||||
override DataFlow::Node getARequest() {
|
||||
result = request
|
||||
}
|
||||
|
||||
override string getKind() {
|
||||
result = "URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
| tst.js:11:5:11:16 | request(url) |
|
||||
| tst.js:13:5:13:20 | request.get(url) |
|
||||
| tst.js:15:5:15:23 | request.delete(url) |
|
||||
| tst.js:17:5:17:25 | request ... url }) |
|
||||
| tst.js:19:5:19:23 | requestPromise(url) |
|
||||
| tst.js:21:5:21:23 | superagent.get(url) |
|
||||
| tst.js:23:5:23:17 | http.get(url) |
|
||||
| tst.js:25:5:25:14 | axios(url) |
|
||||
| tst.js:27:5:27:18 | axios.get(url) |
|
||||
| tst.js:29:5:29:23 | axios({ url: url }) |
|
||||
| tst.js:31:5:31:12 | got(url) |
|
||||
| tst.js:33:5:33:19 | got.stream(url) |
|
||||
| tst.js:35:5:35:21 | window.fetch(url) |
|
||||
| tst.js:37:5:37:18 | nodeFetch(url) |
|
||||
| tst.js:39:5:39:20 | net.request(url) |
|
||||
| tst.js:41:5:41:29 | net.req ... url }) |
|
||||
| tst.js:43:5:43:26 | new Cli ... st(url) |
|
||||
| tst.js:45:5:45:35 | new Cli ... url }) |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from ClientRequest r
|
||||
select r
|
||||
@@ -0,0 +1,22 @@
|
||||
| tst.js:11:5:11:16 | request(url) | tst.js:11:13:11:15 | url |
|
||||
| tst.js:13:5:13:20 | request.get(url) | tst.js:13:17:13:19 | url |
|
||||
| tst.js:15:5:15:23 | request.delete(url) | tst.js:15:20:15:22 | url |
|
||||
| tst.js:17:5:17:25 | request ... url }) | tst.js:17:13:17:24 | { url: url } |
|
||||
| tst.js:17:5:17:25 | request ... url }) | tst.js:17:20:17:22 | url |
|
||||
| tst.js:19:5:19:23 | requestPromise(url) | tst.js:19:20:19:22 | url |
|
||||
| tst.js:21:5:21:23 | superagent.get(url) | tst.js:21:20:21:22 | url |
|
||||
| tst.js:23:5:23:17 | http.get(url) | tst.js:23:14:23:16 | url |
|
||||
| tst.js:25:5:25:14 | axios(url) | tst.js:25:11:25:13 | url |
|
||||
| tst.js:27:5:27:18 | axios.get(url) | tst.js:27:15:27:17 | url |
|
||||
| tst.js:29:5:29:23 | axios({ url: url }) | tst.js:29:11:29:22 | { url: url } |
|
||||
| tst.js:29:5:29:23 | axios({ url: url }) | tst.js:29:18:29:20 | url |
|
||||
| tst.js:31:5:31:12 | got(url) | tst.js:31:9:31:11 | url |
|
||||
| tst.js:33:5:33:19 | got.stream(url) | tst.js:33:16:33:18 | url |
|
||||
| tst.js:35:5:35:21 | window.fetch(url) | tst.js:35:18:35:20 | url |
|
||||
| tst.js:37:5:37:18 | nodeFetch(url) | tst.js:37:15:37:17 | url |
|
||||
| tst.js:39:5:39:20 | net.request(url) | tst.js:39:17:39:19 | url |
|
||||
| tst.js:41:5:41:29 | net.req ... url }) | tst.js:41:17:41:28 | { url: url } |
|
||||
| tst.js:41:5:41:29 | net.req ... url }) | tst.js:41:24:41:26 | url |
|
||||
| tst.js:43:5:43:26 | new Cli ... st(url) | tst.js:43:23:43:25 | url |
|
||||
| tst.js:45:5:45:35 | new Cli ... url }) | tst.js:45:23:45:34 | { url: url } |
|
||||
| tst.js:45:5:45:35 | new Cli ... url }) | tst.js:45:30:45:32 | url |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from ClientRequest r
|
||||
select r, r.getUrl()
|
||||
@@ -0,0 +1,50 @@
|
||||
import request from 'request';
|
||||
import requestPromise from 'request-promise';
|
||||
import superagent from 'superagent';
|
||||
import http from 'http';
|
||||
import express from 'express';
|
||||
import axios from 'axios';
|
||||
import got from 'got';
|
||||
import nodeFetch from 'node-fetch';
|
||||
import {ClientRequest, net} from 'electron';
|
||||
(function() {
|
||||
request(url);
|
||||
|
||||
request.get(url);
|
||||
|
||||
request.delete(url);
|
||||
|
||||
request({ url: url });
|
||||
|
||||
requestPromise(url);
|
||||
|
||||
superagent.get(url);
|
||||
|
||||
http.get(url);
|
||||
|
||||
axios(url);
|
||||
|
||||
axios.get(url);
|
||||
|
||||
axios({ url: url });
|
||||
|
||||
got(url);
|
||||
|
||||
got.stream(url);
|
||||
|
||||
window.fetch(url);
|
||||
|
||||
nodeFetch(url);
|
||||
|
||||
net.request(url);
|
||||
|
||||
net.request({ url: url });
|
||||
|
||||
new ClientRequest(url);
|
||||
|
||||
new ClientRequest({ url: url });
|
||||
|
||||
unknown(url);
|
||||
|
||||
unknown({ url:url });
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from NodeJSLib::ClientRequest cr
|
||||
from Electron::ElectronClientRequest cr
|
||||
select cr
|
||||
@@ -1,4 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from NodeJSLib::ClientRequest cr
|
||||
from NodeJSLib::NodeJSClientRequest cr
|
||||
select cr
|
||||
@@ -0,0 +1,6 @@
|
||||
| tst.js:16:5:16:20 | request(tainted) | The $@ of this request depends on $@. | tst.js:16:13:16:19 | tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
|
||||
| tst.js:18:5:18:24 | request.get(tainted) | The $@ of this request depends on $@. | tst.js:18:17:18:23 | tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
|
||||
| tst.js:22:5:22:20 | request(options) | The $@ of this request depends on $@. | tst.js:21:19:21:25 | tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
|
||||
| tst.js:24:5:24:32 | request ... ainted) | The $@ of this request depends on $@. | tst.js:24:13:24:31 | "http://" + tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
|
||||
| tst.js:26:5:26:43 | request ... ainted) | The $@ of this request depends on $@. | tst.js:26:13:26:42 | "http:/ ... tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
|
||||
| tst.js:28:5:28:44 | request ... ainted) | The $@ of this request depends on $@. | tst.js:28:13:28:43 | "http:/ ... tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-918/RequestForgery.ql
|
||||
31
javascript/ql/test/query-tests/Security/CWE-918/tst.js
Normal file
31
javascript/ql/test/query-tests/Security/CWE-918/tst.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import request from 'request';
|
||||
import requestPromise from 'request-promise';
|
||||
import superagent from 'superagent';
|
||||
import http from 'http';
|
||||
import express from 'express';
|
||||
import axios from 'axios';
|
||||
import got from 'got';
|
||||
import nodeFetch from 'node-fetch';
|
||||
import url from 'url';
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var tainted = url.parse(req.url, true).query.url;
|
||||
|
||||
request("example.com"); // OK
|
||||
|
||||
request(tainted); // NOT OK
|
||||
|
||||
request.get(tainted); // NOT OK
|
||||
|
||||
var options = {};
|
||||
options.url = tainted;
|
||||
request(options); // NOT OK
|
||||
|
||||
request("http://" + tainted); // NOT OK
|
||||
|
||||
request("http://example.com" + tainted); // NOT OK
|
||||
|
||||
request("http://example.com/" + tainted); // NOT OK
|
||||
|
||||
request("http://example.com/?" + tainted); // OK
|
||||
})
|
||||
Reference in New Issue
Block a user