mirror of
https://github.com/github/codeql.git
synced 2026-01-29 22:32:58 +01:00
Merge pull request #68 from adityasharad/go/request-forgery
Add experimental query for request forgery.
This commit is contained in:
20
ql/src/experimental/RequestForgery/RequestForgery.ql
Normal file
20
ql/src/experimental/RequestForgery/RequestForgery.ql
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Uncontrolled data used in network request
|
||||
* @description Sending network requests with user-controlled data allows for request forgery attacks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id go/request-forgery
|
||||
* @tags security
|
||||
* external/cwe/cwe-918
|
||||
*/
|
||||
|
||||
import go
|
||||
import RequestForgery::RequestForgery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
request = sink.getNode().(Sink).getARequest()
|
||||
select request, source, sink, "The $@ of this request depends on $@.", sink.getNode(),
|
||||
sink.getNode().(Sink).getKind(), source, "a user-provided value"
|
||||
35
ql/src/experimental/RequestForgery/RequestForgery.qll
Normal file
35
ql/src/experimental/RequestForgery/RequestForgery.qll
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about request forgery
|
||||
* (SSRF) vulnerabilities.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
module RequestForgery {
|
||||
import RequestForgeryCustomizations::RequestForgery
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about 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 isSanitizerOut(DataFlow::Node node) {
|
||||
super.isSanitizerOut(node) or
|
||||
node instanceof SanitizerEdge
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
super.isSanitizerGuard(guard) or guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Provides classes and predicates used by the request forgery query.
|
||||
*/
|
||||
|
||||
import go
|
||||
import semmle.go.security.UrlConcatenation
|
||||
|
||||
/** Provides classes and predicates for the request forgery query. */
|
||||
module RequestForgery {
|
||||
/** A data flow source for request forgery vulnerabilities. */
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/** A data flow sink for request forgery vulnerabilities. */
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/** Gets a request that uses this sink. */
|
||||
abstract DataFlow::Node getARequest();
|
||||
|
||||
/**
|
||||
* Gets the name of a part of the request that may be tainted by this sink,
|
||||
* such as the URL or the host.
|
||||
*/
|
||||
abstract string getKind();
|
||||
}
|
||||
|
||||
/** A sanitizer for request forgery vulnerabilities. */
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/** An outgoing sanitizer edge for request forgery vulnerabilities. */
|
||||
abstract class SanitizerEdge extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for request forgery vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A third-party controllable input, considered as a flow source for request forgery.
|
||||
*/
|
||||
class UntrustedFlowAsSource extends Source, UntrustedFlowSource { }
|
||||
|
||||
/**
|
||||
* The URL of a URL request, viewed as a sink for request forgery.
|
||||
*/
|
||||
private class ClientRequestUrlAsSink extends Sink {
|
||||
HTTP::ClientRequest request;
|
||||
|
||||
ClientRequestUrlAsSink() { this = request.getUrl() }
|
||||
|
||||
override DataFlow::Node getARequest() { result = request }
|
||||
|
||||
override string getKind() { result = "URL" }
|
||||
}
|
||||
|
||||
private class HostnameSanitizer extends SanitizerEdge {
|
||||
HostnameSanitizer() { hostnameSanitizingPrefixEdge(this, _) }
|
||||
}
|
||||
}
|
||||
@@ -527,6 +527,43 @@ module HTTP {
|
||||
ResponseWriter getResponseWriter() { result = self.getResponseWriter() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new HTTP client request APIs. */
|
||||
module ClientRequest {
|
||||
/**
|
||||
* A call that performs a request to a URL.
|
||||
*
|
||||
* Example: An HTTP POST request is a client request that sends some
|
||||
* `data` to a `url`, where both the headers and the body of the request
|
||||
* contribute to the `data`.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `HTTP::ClientRequest` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the URL of the request.
|
||||
*/
|
||||
abstract DataFlow::Node getUrl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that performs a request to a URL.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `HTTP::ClientRequest::Range` instead.
|
||||
*/
|
||||
class ClientRequest extends DataFlow::Node {
|
||||
ClientRequest::Range self;
|
||||
|
||||
ClientRequest() { this = self }
|
||||
|
||||
/**
|
||||
* Gets the URL of the request.
|
||||
*/
|
||||
DataFlow::Node getUrl() { result = self.getUrl() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new HTTP redirect APIs. */
|
||||
module Redirect {
|
||||
/**
|
||||
|
||||
@@ -147,4 +147,21 @@ private module StdlibHttp {
|
||||
|
||||
override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/** A call to a function in the `net/http` package that performs an HTTP request to a URL. */
|
||||
private class RequestCall extends HTTP::ClientRequest::Range, DataFlow::CallNode {
|
||||
RequestCall() {
|
||||
exists(string functionName |
|
||||
(
|
||||
this.getTarget().hasQualifiedName("net/http", functionName)
|
||||
or
|
||||
this.getTarget().(Method).hasQualifiedName("net/http", "Client", functionName)
|
||||
) and
|
||||
(functionName = "Get" or functionName = "Post" or functionName = "PostForm")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the URL of the request. */
|
||||
override DataFlow::Node getUrl() { result = this.getArgument(0) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user