diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 809176a9d52..56543246784 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -252,3 +252,61 @@ class HeaderDeclaration extends DataFlow::Node { */ DataFlow::Node getValueArg() { result = range.getValueArg() } } + +module ExperimentalHTTP { + /** + * A data-flow node that sets a cookie in an HTTP response. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `HTTP::CookieWrite::Range` instead. + */ + class CookieWrite extends DataFlow::Node { + CookieWrite::Range range; + + CookieWrite() { this = range } + + /** + * Gets the argument, if any, specifying the raw cookie header. + */ + DataFlow::Node getHeaderArg() { result = range.getHeaderArg() } + + /** + * Gets the argument, if any, specifying the cookie name. + */ + DataFlow::Node getNameArg() { result = range.getNameArg() } + + /** + * Gets the argument, if any, specifying the cookie value. + */ + DataFlow::Node getValueArg() { result = range.getValueArg() } + } + + /** Provides a class for modeling new cookie writes on HTTP responses. */ + module CookieWrite { + /** + * A data-flow node that sets a cookie in an HTTP response. + * + * Note: we don't require that this redirect must be sent to a client (a kind of + * "if a tree falls in a forest and nobody hears it" situation). + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `HttpResponse` instead. + */ + abstract class Range extends DataFlow::Node { + /** + * Gets the argument, if any, specifying the raw cookie header. + */ + abstract DataFlow::Node getHeaderArg(); + + /** + * Gets the argument, if any, specifying the cookie name. + */ + abstract DataFlow::Node getNameArg(); + + /** + * Gets the argument, if any, specifying the cookie value. + */ + abstract DataFlow::Node getValueArg(); + } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Django.qll b/python/ql/src/experimental/semmle/python/frameworks/Django.qll index c525b73b40e..da7db6fd18b 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Django.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Django.qll @@ -75,6 +75,17 @@ private module PrivateDjango { override DataFlow::Node getValueArg() { result = headerInput } } + + class DjangoSetCookieCall extends DataFlow::CallCfgNode, + ExperimentalHTTP::CookieWrite::Range { + DjangoSetCookieCall() { this = baseClassRef().getMember("set_cookie").getACall() } + + override DataFlow::Node getHeaderArg() { none() } + + override DataFlow::Node getNameArg() { result = this.getArg(0) } + + override DataFlow::Node getValueArg() { result = this.getArg(1) } + } } } }