-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Python: Add Server-side Request Forgery sinks #8275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
b23e28a
be40b54
40feb1f
56901ea
7d6d8be
c65839b
02a97b0
866e615
75bc532
d86284b
e47f726
f620e25
7e6666b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -639,3 +639,54 @@ module AiohttpWebModel { | |||||||||||||||||||||||||||||||||||||||||
override DataFlow::Node getValueArg() { result = value } | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Provides models for the web server part (`aiohttp.client`) of the `aiohttp` PyPI package. | ||||||||||||||||||||||||||||||||||||||||||
* See https://docs.aiohttp.org/en/stable/client.html | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
module AiohttpClientModel { | ||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Provides models for the `aiohttp.ClientSession` class | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* See https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientSession. | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
module ClientSession { | ||||||||||||||||||||||||||||||||||||||||||
/** Gets a reference to the `aiohttp.ClientSession` class. */ | ||||||||||||||||||||||||||||||||||||||||||
private API::Node classRef() { | ||||||||||||||||||||||||||||||||||||||||||
result = API::moduleImport("aiohttp").getMember("ClientSession") | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** Gets a reference to an instance of `aiohttp.ClientSession`. */ | ||||||||||||||||||||||||||||||||||||||||||
private API::Node instance() { result = classRef().getReturn() } | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** A method call on a ClientSession that sends off a request */ | ||||||||||||||||||||||||||||||||||||||||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||||||||||||||||||||||||||||||||||||||||||
string methodName; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
OutgoingRequestCall() { | ||||||||||||||||||||||||||||||||||||||||||
methodName in [HTTP::httpVerbLower(), "request"] and | ||||||||||||||||||||||||||||||||||||||||||
this = instance().getMember(methodName).getACall() | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
DataFlow::Node getUrlArg() { | ||||||||||||||||||||||||||||||||||||||||||
result = this.getArgByName("url") | ||||||||||||||||||||||||||||||||||||||||||
or | ||||||||||||||||||||||||||||||||||||||||||
not methodName = "request" and | ||||||||||||||||||||||||||||||||||||||||||
result = this.getArg(0) | ||||||||||||||||||||||||||||||||||||||||||
or | ||||||||||||||||||||||||||||||||||||||||||
methodName = "request" and | ||||||||||||||||||||||||||||||||||||||||||
result = this.getArg(1) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see any reason to have this separate predicate, can we just do
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Please review whether the test file is correct, thank you! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They looked great, but there was a failure for |
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
override string getFramework() { result = "aiohttp.ClientSession" } | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
override predicate disablesCertificateValidation( | ||||||||||||||||||||||||||||||||||||||||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||||||||||||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||||||||||||
none() | ||||||||||||||||||||||||||||||||||||||||||
RasmusWL marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* Provides classes modeling security-relevant aspects of the `httpx` PyPI package. | ||
* See https://www.python-httpx.org/ | ||
*/ | ||
|
||
private import python | ||
private import semmle.python.Concepts | ||
private import semmle.python.ApiGraphs | ||
|
||
/** | ||
* Provides models for the `httpx` PyPI package. | ||
* see https://www.python-httpx.org/ | ||
*/ | ||
module HttpxModel { | ||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||
string methodName; | ||
|
||
RequestCall() { | ||
methodName in [HTTP::httpVerbLower(), "request", "stream"] and | ||
this = API::moduleImport("httpx").getMember(methodName).getACall() | ||
} | ||
|
||
DataFlow::Node getUrlArg() { | ||
result = this.getArgByName("url") | ||
or | ||
not methodName = "request" and | ||
result = this.getArg(0) | ||
or | ||
methodName in ["request", "stream"] and | ||
result = this.getArg(1) | ||
} | ||
|
||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||
|
||
override string getFramework() { result = "httpx" } | ||
|
||
override predicate disablesCertificateValidation( | ||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||
) { | ||
none() | ||
} | ||
} | ||
|
||
/** | ||
* Provides models for the `httpx.[Async]Client` class | ||
* | ||
* See https://www.python-httpx.org/async/ | ||
*/ | ||
module Client { | ||
/** Get a reference to the `httpx.Client` or `httpx.AsyncClient` class. */ | ||
private API::Node classRef() { | ||
result = API::moduleImport("httpx").getMember(["Client", "AsyncClient"]) | ||
} | ||
|
||
/** Get a reference to an `httpx.Client` or `httpx.AsyncClient` instance. */ | ||
private API::Node instance() { result = classRef().getReturn() } | ||
|
||
/** A method call on a Client that sends off a request */ | ||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||
string methodName; | ||
|
||
OutgoingRequestCall() { | ||
methodName in [HTTP::httpVerbLower(), "request", "stream"] and | ||
this = instance().getMember(methodName).getACall() | ||
} | ||
|
||
DataFlow::Node getUrlArg() { | ||
result = this.getArgByName("url") | ||
or | ||
not methodName = "request" and | ||
result = this.getArg(0) | ||
or | ||
methodName in ["request", "stream"] and | ||
result = this.getArg(1) | ||
} | ||
|
||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||
|
||
override string getFramework() { result = "httpx.[Async]Client" } | ||
|
||
override predicate disablesCertificateValidation( | ||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||
) { | ||
none() | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* Provides classes modeling security-relevant aspects of the `libtaxii` PyPI package. | ||
* See https://github.com/TAXIIProject/libtaxii | ||
*/ | ||
|
||
private import python | ||
private import semmle.python.Concepts | ||
private import semmle.python.ApiGraphs | ||
|
||
/** | ||
* Provides models for the `libtaxii` PyPI package. | ||
* see https://github.com/TAXIIProject/libtaxii | ||
*/ | ||
module Libtaxii { | ||
/** | ||
* A call to `libtaxii.common.parse`. | ||
* When the `allow_url` parameter value is set to `True`, there is an SSRF vulnerability.. | ||
*/ | ||
private class ParseCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||
ParseCall() { | ||
this = API::moduleImport("libtaxii").getMember("common").getMember("parse").getACall() and | ||
this.getArgByName("allow_url").asExpr().toString() = "True" | ||
} | ||
|
||
DataFlow::Node getUrlArg() { result in [this.getArg(0), this.getArgByName("s")] } | ||
|
||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||
|
||
override string getFramework() { result = "libtaxii.common.parse" } | ||
|
||
override predicate disablesCertificateValidation( | ||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||
) { | ||
none() | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/** | ||
* Provides classes modeling security-relevant aspects of the `pycurl` PyPI package. | ||
* See https://pycurl.io/docs/latest/ | ||
*/ | ||
|
||
private import python | ||
private import semmle.python.Concepts | ||
private import semmle.python.ApiGraphs | ||
|
||
/** | ||
* Provides models for the `pycurl` PyPI package. | ||
* see https://pycurl.io/docs/latest/ | ||
*/ | ||
module Pycurl { | ||
/** | ||
* Provides models for the `pycurl.Curl` class | ||
* | ||
* See https://pycurl.io/docs/latest/curl.html. | ||
*/ | ||
module Curl { | ||
/** Gets a reference to the `pycurl.Curl` class. */ | ||
private API::Node classRef() { result = API::moduleImport("pycurl").getMember("Curl") } | ||
|
||
/** Gets a reference to an instance of `pycurl.Curl`. */ | ||
private API::Node instance() { result = classRef().getReturn() } | ||
|
||
/** | ||
* When the first parameter value of the `setopt` function is set to `pycurl.URL`, | ||
* the second parameter value is the request resource link. | ||
* | ||
* See https://pycurl.io/docs/latest/curl.html#set_option. | ||
*/ | ||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||
OutgoingRequestCall() { | ||
this = instance().getMember("setopt").getACall() and | ||
this.getArg(0).asCfgNode().(AttrNode).getName() = "URL" | ||
} | ||
|
||
DataFlow::Node getUrlArg() { result in [this.getArg(1), this.getArgByName("value")] } | ||
|
||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||
|
||
override string getFramework() { result = "pycurl.Curl" } | ||
|
||
override predicate disablesCertificateValidation( | ||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||
) { | ||
none() | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/** | ||
* Provides classes modeling security-relevant aspects of the `urllib` PyPI package. | ||
* See https://docs.python.org/3.9/library/urllib.html | ||
*/ | ||
|
||
private import python | ||
private import semmle.python.Concepts | ||
private import semmle.python.ApiGraphs | ||
|
||
/** | ||
* Provides models for the `Urllib` PyPI package. | ||
* see https://docs.python.org/3.9/library/urllib.html | ||
*/ | ||
module Urllib { | ||
/** | ||
* Provides models for the `urllib.request` extension library | ||
* | ||
* See https://docs.python.org/3.9/library/urllib.request.html | ||
*/ | ||
module Request { | ||
/** | ||
* See | ||
* - https://docs.python.org/3.9/library/urllib.request.html#urllib.request.Request | ||
*/ | ||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||
RequestCall() { | ||
this = API::moduleImport("urllib").getMember("request").getMember("Request").getACall() | ||
} | ||
|
||
DataFlow::Node getUrlArg() { result in [this.getArg(0), this.getArgByName("url")] } | ||
|
||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||
|
||
override string getFramework() { result = "urllib.request.Request" } | ||
|
||
override predicate disablesCertificateValidation( | ||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||
) { | ||
none() | ||
} | ||
} | ||
|
||
/** | ||
* See | ||
* - https://docs.python.org/3.9/library/urllib.request.html#urllib.request.urlopen | ||
*/ | ||
private class UrlOpenCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||
UrlOpenCall() { | ||
this = API::moduleImport("urllib").getMember("request").getMember("urlopen").getACall() | ||
} | ||
|
||
DataFlow::Node getUrlArg() { result in [this.getArg(0), this.getArgByName("url")] } | ||
|
||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||
|
||
override string getFramework() { result = "urllib.request.urlopen" } | ||
|
||
override predicate disablesCertificateValidation( | ||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||
) { | ||
none() | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/** | ||
* Provides classes modeling security-relevant aspects of the `urllib2` PyPI package. | ||
* See https://docs.python.org/2/library/urllib2.html | ||
*/ | ||
|
||
private import python | ||
private import semmle.python.Concepts | ||
private import semmle.python.ApiGraphs | ||
|
||
/** | ||
* Provides models for the `urllib2` PyPI package. | ||
* see https://docs.python.org/2/library/urllib2.html | ||
*/ | ||
module Urllib2 { | ||
/** | ||
* See | ||
* - https://docs.python.org/2/library/urllib2.html#urllib2.Request | ||
*/ | ||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||
RequestCall() { | ||
this = API::moduleImport("urllib2").getMember("Request").getACall() | ||
} | ||
|
||
DataFlow::Node getUrlArg() { result in [this.getArg(0), this.getArgByName("url")] } | ||
|
||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||
|
||
override string getFramework() { result = "urllib2.Request" } | ||
|
||
override predicate disablesCertificateValidation( | ||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||
) { | ||
none() | ||
} | ||
} | ||
|
||
/** | ||
* See | ||
* - https://docs.python.org/2/library/urllib2.html#urllib2.urlopen | ||
*/ | ||
private class UrlOpenCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { | ||
UrlOpenCall() { this = API::moduleImport("urllib2").getMember("urlopen").getACall() } | ||
|
||
DataFlow::Node getUrlArg() { result in [this.getArg(0), this.getArgByName("url")] } | ||
|
||
override DataFlow::Node getAUrlPart() { result = this.getUrlArg() } | ||
|
||
override string getFramework() { result = "urllib2.urlopen" } | ||
|
||
override predicate disablesCertificateValidation( | ||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin | ||
) { | ||
none() | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.