mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Python: django.http.response.HttpResponse
This commit is contained in:
@@ -499,7 +499,7 @@ private module Django {
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node http_attr(DataFlow::TypeTracker t, string attr_name) {
|
||||
attr_name in ["request", "HttpRequest"] and
|
||||
attr_name in ["request", "HttpRequest", "response", "HttpResponse"] and
|
||||
(
|
||||
t.start() and
|
||||
result = DataFlow::importNode("django.http" + "." + attr_name)
|
||||
@@ -627,6 +627,119 @@ private module Django {
|
||||
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// django.http.response
|
||||
// -------------------------------------------------------------------------
|
||||
/** Gets a reference to the `django.http.response` module. */
|
||||
DataFlow::Node response() { result = http_attr("response") }
|
||||
|
||||
/** Provides models for the `django.http.response` module */
|
||||
module response {
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of the `django.http.response` module.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node response_attr(DataFlow::TypeTracker t, string attr_name) {
|
||||
attr_name in ["HttpResponse"] and
|
||||
(
|
||||
t.start() and
|
||||
result = DataFlow::importNode("django.http.response" + "." + attr_name)
|
||||
or
|
||||
t.startInAttr(attr_name) and
|
||||
result = response()
|
||||
)
|
||||
or
|
||||
// Due to bad performance when using normal setup with `response_attr(t2, attr_name).track(t2, t)`
|
||||
// we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
response_attr_first_join(t2, attr_name, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate response_attr_first_join(
|
||||
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
|
||||
DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(response_attr(t2, attr_name), res, summary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of the `django.http.response` module.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node response_attr(string attr_name) {
|
||||
result = response_attr(DataFlow::TypeTracker::end(), attr_name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `django.http.response.HttpResponse` class
|
||||
*
|
||||
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponse.
|
||||
*/
|
||||
module HttpResponse {
|
||||
/** Gets a reference to the `django.http.response.HttpResponse` class. */
|
||||
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponse")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponse` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponse")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `django.http.response.HttpResponse` class. */
|
||||
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* A source of an instance of `django.http.response.HttpResponse`.
|
||||
*
|
||||
* This can include instantiation of the class, return value from function
|
||||
* calls, or a special parameter that will be set when functions are call by external
|
||||
* library.
|
||||
*
|
||||
* Use `HttpResponse::instance()` predicate to get references to instances of `django.http.response.HttpResponse`.
|
||||
*/
|
||||
abstract class InstanceSource extends HTTP::Server::HttpResponse::Range, DataFlow::Node {
|
||||
}
|
||||
|
||||
/** A direct instantiation of `django.http.response.HttpResponse`. */
|
||||
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
|
||||
|
||||
override DataFlow::Node getBody() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("content")]
|
||||
}
|
||||
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() {
|
||||
result.asCfgNode() in [node.getArg(1), node.getArgByName("content_type")]
|
||||
}
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponse`. */
|
||||
private DataFlow::Node instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponse`. */
|
||||
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,12 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
|
||||
class DedicatedResponseTest extends HttpServerHttpResponseTest {
|
||||
DedicatedResponseTest() { file.getShortName() = "response_test.py" }
|
||||
}
|
||||
|
||||
class OtherResponseTest extends HttpServerHttpResponseTest {
|
||||
OtherResponseTest() { not this instanceof DedicatedResponseTest }
|
||||
|
||||
override string getARelevantTag() { result = "HttpResponse" }
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ def fp_json_response(request):
|
||||
# Not an XSS sink, since the Content-Type is not "text/html"
|
||||
def fp_manual_json_response(request):
|
||||
json_data = '{"json": "{}"}'.format(request.GET.get("foo"))
|
||||
return HttpResponse(json_data, content_type="application/json")
|
||||
return HttpResponse(json_data, content_type="application/json") # $HttpResponse $mimetype=application/json $responseBody=json_data
|
||||
|
||||
# Not an XSS sink, since the Content-Type is not "text/html"
|
||||
def fp_manual_content_type(request):
|
||||
return HttpResponse('<img src="0" onerror="alert(1)">', content_type="text/plain")
|
||||
return HttpResponse('<img src="0" onerror="alert(1)">', content_type="text/plain") # $HttpResponse $mimetype=text/plain $responseBody='<img src="0" onerror="alert(1)">'
|
||||
|
||||
# XSS FP reported in https://github.com/github/codeql/issues/3466
|
||||
# Note: This should be a open-redirect sink, but not a XSS sink.
|
||||
@@ -26,4 +26,4 @@ def tp_not_found(request):
|
||||
|
||||
# Ensure we still have a XSS sink when manually setting the content_type to HTML
|
||||
def tp_manual_response_type(request):
|
||||
return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8")
|
||||
return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") # $HttpResponse $mimetype=text/html $responseBody=Attribute()
|
||||
|
||||
@@ -5,19 +5,19 @@ from django.views.generic import View
|
||||
|
||||
|
||||
def url_match_xss(request, foo, bar, no_taint=None): # $routeHandler $routedParameter=foo $routedParameter=bar
|
||||
return HttpResponse('url_match_xss: {} {}'.format(foo, bar))
|
||||
return HttpResponse('url_match_xss: {} {}'.format(foo, bar)) # $HttpResponse
|
||||
|
||||
|
||||
def get_params_xss(request): # $routeHandler
|
||||
return HttpResponse(request.GET.get("untrusted"))
|
||||
return HttpResponse(request.GET.get("untrusted")) # $HttpResponse
|
||||
|
||||
|
||||
def post_params_xss(request): # $routeHandler
|
||||
return HttpResponse(request.POST.get("untrusted"))
|
||||
return HttpResponse(request.POST.get("untrusted")) # $HttpResponse
|
||||
|
||||
|
||||
def http_resp_write(request): # $routeHandler
|
||||
rsp = HttpResponse()
|
||||
rsp = HttpResponse() # $HttpResponse
|
||||
rsp.write(request.GET.get("untrusted"))
|
||||
return rsp
|
||||
|
||||
@@ -27,22 +27,22 @@ class Foo(object):
|
||||
|
||||
|
||||
def post(self, request, untrusted): # $f-:routeHandler $f-:routedParameter=untrusted
|
||||
return HttpResponse('Foo post: {}'.format(untrusted))
|
||||
return HttpResponse('Foo post: {}'.format(untrusted)) # $HttpResponse
|
||||
|
||||
|
||||
class ClassView(View, Foo):
|
||||
|
||||
def get(self, request, untrusted): # $f-:routeHandler $f-:routedParameter=untrusted
|
||||
return HttpResponse('ClassView get: {}'.format(untrusted))
|
||||
return HttpResponse('ClassView get: {}'.format(untrusted)) # $HttpResponse
|
||||
|
||||
|
||||
def show_articles(request, page_number=1): # $routeHandler $routedParameter=page_number
|
||||
page_number = int(page_number)
|
||||
return HttpResponse('articles page: {}'.format(page_number))
|
||||
return HttpResponse('articles page: {}'.format(page_number)) # $HttpResponse
|
||||
|
||||
|
||||
def xxs_positional_arg(request, arg0, arg1, no_taint=None): # $routeHandler $routedParameter=arg0 $routedParameter=arg1
|
||||
return HttpResponse('xxs_positional_arg: {} {}'.format(arg0, arg1))
|
||||
return HttpResponse('xxs_positional_arg: {} {}'.format(arg0, arg1)) # $HttpResponse
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
@@ -63,7 +63,7 @@ urlpatterns = [
|
||||
# Using patterns() for routing
|
||||
|
||||
def show_user(request, username): # $routeHandler $routedParameter=username
|
||||
return HttpResponse('show_user {}'.format(username))
|
||||
return HttpResponse('show_user {}'.format(username)) # $HttpResponse
|
||||
|
||||
|
||||
urlpatterns = patterns(url(r"^users/(?P<username>[^/]+)", show_user)) # $routeSetup="^users/(?P<username>[^/]+)"
|
||||
@@ -72,7 +72,7 @@ urlpatterns = patterns(url(r"^users/(?P<username>[^/]+)", show_user)) # $routeS
|
||||
# Show we understand the keyword arguments to django.conf.urls.url
|
||||
|
||||
def kw_args(request): # $routeHandler
|
||||
return HttpResponse('kw_args')
|
||||
return HttpResponse('kw_args') # $HttpResponse
|
||||
|
||||
urlpatterns = [
|
||||
url(view=kw_args, regex=r"^kw_args") # $routeSetup="^kw_args"
|
||||
|
||||
@@ -1,2 +1,12 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
|
||||
class DedicatedResponseTest extends HttpServerHttpResponseTest {
|
||||
DedicatedResponseTest() { file.getShortName() = "response_test.py" }
|
||||
}
|
||||
|
||||
class OtherResponseTest extends HttpServerHttpResponseTest {
|
||||
OtherResponseTest() { not this instanceof DedicatedResponseTest }
|
||||
|
||||
override string getARelevantTag() { result = "HttpResponse" }
|
||||
}
|
||||
|
||||
@@ -5,19 +5,19 @@ from django.views import View
|
||||
|
||||
|
||||
def url_match_xss(request, foo, bar, no_taint=None): # $routeHandler $routedParameter=foo $routedParameter=bar
|
||||
return HttpResponse('url_match_xss: {} {}'.format(foo, bar))
|
||||
return HttpResponse('url_match_xss: {} {}'.format(foo, bar)) # $HttpResponse
|
||||
|
||||
|
||||
def get_params_xss(request): # $routeHandler
|
||||
return HttpResponse(request.GET.get("untrusted"))
|
||||
return HttpResponse(request.GET.get("untrusted")) # $HttpResponse
|
||||
|
||||
|
||||
def post_params_xss(request): # $routeHandler
|
||||
return HttpResponse(request.POST.get("untrusted"))
|
||||
return HttpResponse(request.POST.get("untrusted")) # $HttpResponse
|
||||
|
||||
|
||||
def http_resp_write(request): # $routeHandler
|
||||
rsp = HttpResponse()
|
||||
rsp = HttpResponse() # $HttpResponse
|
||||
rsp.write(request.GET.get("untrusted"))
|
||||
return rsp
|
||||
|
||||
@@ -27,22 +27,22 @@ class Foo(object):
|
||||
|
||||
|
||||
def post(self, request, untrusted): # $f-:routeHandler $f-:routedParameter=untrusted
|
||||
return HttpResponse('Foo post: {}'.format(untrusted))
|
||||
return HttpResponse('Foo post: {}'.format(untrusted)) # $HttpResponse
|
||||
|
||||
|
||||
class ClassView(View, Foo):
|
||||
|
||||
def get(self, request, untrusted): # $f-:routeHandler $f-:routedParameter=untrusted
|
||||
return HttpResponse('ClassView get: {}'.format(untrusted))
|
||||
return HttpResponse('ClassView get: {}'.format(untrusted)) # $HttpResponse
|
||||
|
||||
|
||||
def show_articles(request, page_number=1): # $routeHandler $routedParameter=page_number
|
||||
page_number = int(page_number)
|
||||
return HttpResponse('articles page: {}'.format(page_number))
|
||||
return HttpResponse('articles page: {}'.format(page_number)) # $HttpResponse
|
||||
|
||||
|
||||
def xxs_positional_arg(request, arg0, arg1, no_taint=None): # $routeHandler $routedParameter=arg0 $routedParameter=arg1
|
||||
return HttpResponse('xxs_positional_arg: {} {}'.format(arg0, arg1))
|
||||
return HttpResponse('xxs_positional_arg: {} {}'.format(arg0, arg1)) # $HttpResponse
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
@@ -63,7 +63,7 @@ urlpatterns = [
|
||||
# Show we understand the keyword arguments to django.urls.re_path
|
||||
|
||||
def re_path_kwargs(request): # $routeHandler
|
||||
return HttpResponse('re_path_kwargs')
|
||||
return HttpResponse('re_path_kwargs') # $HttpResponse
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
@@ -76,16 +76,16 @@ urlpatterns = [
|
||||
|
||||
# saying page_number is an externally controlled *string* is a bit strange, when we have an int converter :O
|
||||
def page_number(request, page_number=1): # $routeHandler $routedParameter=page_number
|
||||
return HttpResponse('page_number: {}'.format(page_number))
|
||||
return HttpResponse('page_number: {}'.format(page_number)) # $HttpResponse
|
||||
|
||||
def foo_bar_baz(request, foo, bar, baz): # $routeHandler $routedParameter=foo $routedParameter=bar $routedParameter=baz
|
||||
return HttpResponse('foo_bar_baz: {} {} {}'.format(foo, bar, baz))
|
||||
return HttpResponse('foo_bar_baz: {} {} {}'.format(foo, bar, baz)) # $HttpResponse
|
||||
|
||||
def path_kwargs(request, foo, bar): # $routeHandler $routedParameter=foo $routedParameter=bar
|
||||
return HttpResponse('path_kwargs: {} {} {}'.format(foo, bar))
|
||||
return HttpResponse('path_kwargs: {} {} {}'.format(foo, bar)) # $HttpResponse
|
||||
|
||||
def not_valid_identifier(request): # $routeHandler
|
||||
return HttpResponse('<foo!>')
|
||||
return HttpResponse('<foo!>') # $HttpResponse
|
||||
|
||||
urlpatterns = [
|
||||
path("articles/", page_number), # $routeSetup="articles/"
|
||||
@@ -102,7 +102,7 @@ urlpatterns = [
|
||||
from django.conf.urls import url
|
||||
|
||||
def deprecated(request): # $routeHandler
|
||||
return HttpResponse('deprecated')
|
||||
return HttpResponse('deprecated') # $HttpResponse
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^deprecated/", deprecated), # $routeSetup="^deprecated/"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
|
||||
def foo(request: HttpRequest): # $routeHandler
|
||||
return HttpResponse("foo")
|
||||
return HttpResponse("foo") # $HttpResponse
|
||||
|
||||
def bar_baz(request: HttpRequest): # $routeHandler
|
||||
return HttpResponse("bar_baz")
|
||||
return HttpResponse("bar_baz") # $HttpResponse
|
||||
|
||||
def deprecated(request: HttpRequest): # $routeHandler
|
||||
return HttpResponse("deprecated")
|
||||
return HttpResponse("deprecated") # $HttpResponse
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
|
||||
class DedicatedFlaskResponseTest extends HttpServerHttpResponseTest {
|
||||
DedicatedFlaskResponseTest() { file.getShortName() = "response_test.py" }
|
||||
class DedicatedResponseTest extends HttpServerHttpResponseTest {
|
||||
DedicatedResponseTest() { file.getShortName() = "response_test.py" }
|
||||
}
|
||||
|
||||
class OtherFlaskResponseTest extends HttpServerHttpResponseTest {
|
||||
OtherFlaskResponseTest() { not this instanceof DedicatedFlaskResponseTest }
|
||||
class OtherResponseTest extends HttpServerHttpResponseTest {
|
||||
OtherResponseTest() { not this instanceof DedicatedResponseTest }
|
||||
|
||||
override string getARelevantTag() { result = "HttpResponse" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user