Skip to content
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

Python: Promote Template Injection query from experimental #17922

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
25 changes: 25 additions & 0 deletions python/ql/lib/semmle/python/Concepts.qll
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,31 @@ class LdapFilterEscaping extends Escaping {
LdapFilterEscaping() { super.getKind() = Escaping::getLdapFilterKind() }
}

/**
* A data-flow node that constructs a template in a templating engine.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `TemplateConstruction::Range` instead.
*/
class TemplateConstruction extends DataFlow::Node instanceof TemplateConstruction::Range {
/** Gets the argument that specifies the template source. */
DataFlow::Node getSourceArg() { result = super.getSourceArg() }
}

/** Provides classes for modeling template construction APIs. */
module TemplateConstruction {
/**
* A data-flow node that constructs a template in a templating engine.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `TemplateConstruction` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the argument that specifies the template source. */
abstract DataFlow::Node getSourceArg();
}
}

/** Provides classes for modeling HTTP-related APIs. */
module Http {
/** Gets an HTTP verb, in upper case */
Expand Down
8 changes: 8 additions & 0 deletions python/ql/lib/semmle/python/Frameworks.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@
private import semmle.python.frameworks.Aiomysql
private import semmle.python.frameworks.Aiopg
private import semmle.python.frameworks.Aiosqlite
private import semmle.python.frameworks.Airspeed
private import semmle.python.frameworks.Anyio
private import semmle.python.frameworks.Asyncpg
private import semmle.python.frameworks.Baize
private import semmle.python.frameworks.Bottle
private import semmle.python.frameworks.BSon
private import semmle.python.frameworks.Bottle

Check warning

Code scanning / CodeQL

Redundant import Warning

Duplicate import, the module is already imported by
semmle.python.frameworks.Bottle
.
private import semmle.python.frameworks.CassandraDriver
private import semmle.python.frameworks.Chameleon
private import semmle.python.frameworks.Cherrypy
private import semmle.python.frameworks.Chevron
private import semmle.python.frameworks.ClickhouseDriver
private import semmle.python.frameworks.Cryptodome
private import semmle.python.frameworks.Cryptography
Expand All @@ -30,10 +34,12 @@
private import semmle.python.frameworks.Flask
private import semmle.python.frameworks.FlaskAdmin
private import semmle.python.frameworks.FlaskSqlAlchemy
private import semmle.python.frameworks.Genshi
private import semmle.python.frameworks.Gradio
private import semmle.python.frameworks.Httpx
private import semmle.python.frameworks.Idna
private import semmle.python.frameworks.Invoke
private import semmle.python.frameworks.Jinja2
private import semmle.python.frameworks.Jmespath
private import semmle.python.frameworks.Joblib
private import semmle.python.frameworks.JsonPickle
Expand All @@ -42,6 +48,7 @@
private import semmle.python.frameworks.Libtaxii
private import semmle.python.frameworks.Libxml2
private import semmle.python.frameworks.Lxml
private import semmle.python.frameworks.Mako
private import semmle.python.frameworks.MarkupSafe
private import semmle.python.frameworks.Multidict
private import semmle.python.frameworks.Mysql
Expand Down Expand Up @@ -78,6 +85,7 @@
private import semmle.python.frameworks.Toml
private import semmle.python.frameworks.Torch
private import semmle.python.frameworks.Tornado
private import semmle.python.frameworks.TRender
private import semmle.python.frameworks.Twisted
private import semmle.python.frameworks.Ujson
private import semmle.python.frameworks.Urllib3
Expand Down
25 changes: 25 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Airspeed.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Provides classes modeling security-relevant aspects of the `airspeed` library.
* See https://github.com/purcell/airspeed.
*/

private import python
private import semmle.python.ApiGraphs
private import semmle.python.Concepts

/**
* INTERNAL: Do not use.
*
* Provides classes modeling security-relevant aspects of the `airspeed` library.
* See https://github.com/purcell/airspeed.
*/
module Airspeed {
/** A call to `airspeed.Template`. */
private class AirspeedTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
AirspeedTemplateConstruction() {
this = API::moduleImport("airspeed").getMember("Template").getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
14 changes: 13 additions & 1 deletion python/ql/lib/semmle/python/frameworks/Bottle.qll
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ module Bottle {
ViewCallable() { this = any(BottleRouteSetup rs).getARequestHandler() }
}

/** Get methods that reprsent a route in Bottle */
/** Get methods that represent a route in Bottle */
string routeMethods() { result = ["route", "get", "post", "put", "delete", "patch"] }

private class BottleRouteSetup extends Http::Server::RouteSetup::Range, DataFlow::CallCfgNode {
Expand Down Expand Up @@ -171,5 +171,17 @@ module Bottle {
override predicate valueAllowsNewline() { none() }
}
}

/** Provides models for functions that construct templates. */
module Templates {
/** A call to `bottle.template`or `bottle.SimpleTemplate`. */
private class BottleTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
BottleTemplateConstruction() {
this = API::moduleImport("bottle").getMember(["template", "SimpleTemplate"]).getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
}
}
25 changes: 25 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Chameleon.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Provides classes modeling security-relevant aspects of the `chameleon` PyPI package.
* See https://chameleon.readthedocs.io/en/latest/.
*/

private import python
private import semmle.python.ApiGraphs
private import semmle.python.Concepts

/**
* INTERNAL: Do not use.
*
* Provides classes modeling security-relevant aspects of the `chameleon` PyPI package.
* See https://chameleon.readthedocs.io/en/latest/.
*/
module Chameleon {
/** A call to `chameleon.PageTemplate`. */
private class ChameleonTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
ChameleonTemplateConstruction() {
this = API::moduleImport("chameleon").getMember("PageTemplate").getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
25 changes: 25 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Chevron.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Provides classes modeling security-relevant aspects of the `chevron` PyPI package.
* See https://pypi.org/project/chevron.
*/

private import python
private import semmle.python.ApiGraphs
private import semmle.python.Concepts

/**
* INTERNAL: Do not use.
*
* Provides classes modeling security-relevant aspects of the `chevron` PyPI package.
* See https://pypi.org/project/chevron.
*/
module Chevron {
/** A call to `chevron.render`. */
private class ChevronRenderConstruction extends TemplateConstruction::Range, API::CallNode {
ChevronRenderConstruction() {
this = API::moduleImport("chevron").getMember("render").getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
13 changes: 13 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Django.qll
Original file line number Diff line number Diff line change
Expand Up @@ -2996,4 +2996,17 @@ module PrivateDjango {
any()
}
}

// ---------------------------------------------------------------------------
// Templates
// ---------------------------------------------------------------------------
/** A call to `django.template.Template` */
private class DjangoTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
DjangoTemplateConstruction() {
this = API::moduleImport("django").getMember("template").getMember("Template").getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
// TODO: Support `from_string` on instances of `django.template.Engine`.
}
9 changes: 9 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Flask.qll
Original file line number Diff line number Diff line change
Expand Up @@ -721,4 +721,13 @@ module Flask {
preservesValue = false
}
}

/** A call to `flask.render_template_string` as a template construction sink. */
private class FlaskTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
FlaskTemplateConstruction() {
this = API::moduleImport("flask").getMember("render_template_string").getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
44 changes: 44 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Genshi.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Provides classes modeling security-relevant aspects of the `Genshi` PyPI package.
* See https://genshi.edgewall.org/.
*/

private import python
private import semmle.python.ApiGraphs
private import semmle.python.Concepts

/**
* INTERNAL: Do not use.
*
* Provides classes modeling security-relevant aspects of the `Genshi` PyPI package.
* See https://genshi.edgewall.org/.
*/
module Genshi {
/** A call to `genshi.template.text.NewTextTemplate` or `genshi.template.text.OldTextTemplate`. */
private class GenshiTextTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
GenshiTextTemplateConstruction() {
this =
API::moduleImport("genshi")
.getMember("template")
.getMember("text")
.getMember(["NewTextTemplate", "OldTextTemplate"])
.getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}

/** A call to `genshi.template.MarkupTemplate` */
private class GenshiMarkupTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
GenshiMarkupTemplateConstruction() {
this =
API::moduleImport("genshi")
.getMember("template")
.getMember("markup")
.getMember("MarkupTemplate")
.getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
55 changes: 55 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Jinja2.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Provides classes modeling security-relevant aspects of the `jinja2` PyPI package.
* See https://jinja.palletsprojects.com.
*/

private import python
private import semmle.python.ApiGraphs
private import semmle.python.Concepts
private import semmle.python.frameworks.data.ModelsAsData

/**
* INTERNAL: Do not use
*
* Provides classes modeling security-relevant aspects of the `jinja2` PyPI package.
* See https://jinja.palletsprojects.com.
*/
module Jinja2 {
/** A call to `jinja2.Template`. */
private class Jinja2TemplateConstruction extends TemplateConstruction::Range, API::CallNode {
Jinja2TemplateConstruction() {
this = API::moduleImport("jinja2").getMember("Template").getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}

module EnvironmentClass {

Check warning on line 27 in python/ql/lib/semmle/python/frameworks/Jinja2.qll

View workflow job for this annotation

GitHub Actions / qldoc

Missing QLdoc for module Jinja2::Jinja2::EnvironmentClass
/** Gets a reference to the `jinja2.Environment` class. */
API::Node classRef() {
result = API::moduleImport("jinja2").getMember("Environment")
or
result = ModelOutput::getATypeNode("jinja.Environment~Subclass").getASubclass*()
}

/** Gets a reference to an instance of `jinja2.Environment`. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result = EnvironmentClass::classRef().getACall()
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}

/** Gets a reference to an instance of `jinja2.Environment`. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }

/** A call to `jinja2.Environment.from_string`. */
private class Jinja2FromStringConstruction extends TemplateConstruction::Range,
DataFlow::MethodCallNode
{
Jinja2FromStringConstruction() { this.calls(EnvironmentClass::instance(), "from_string") }

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
}
25 changes: 25 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Mako.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Provides classes modeling security-relevant aspects of the `Mako` PyPI package.
* See https://www.makotemplates.org/.
*/

private import python
private import semmle.python.ApiGraphs
private import semmle.python.Concepts

/**
* INTERNAL: Do not use.
*
* Provides classes modeling security-relevant aspects of the `Mako` PyPI package.
* See https://www.makotemplates.org/.
*/
module Mako {
/** A call to `mako.template.Template`. */
private class MakoTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
MakoTemplateConstruction() {
this = API::moduleImport("mako").getMember("template").getMember("Template").getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
25 changes: 25 additions & 0 deletions python/ql/lib/semmle/python/frameworks/TRender.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Provides classes modeling security-relevant aspects of the `trender` PyPI package.
* See https://github.com/cesbit/trender.
*/

private import python
private import semmle.python.ApiGraphs
private import semmle.python.Concepts

/**
* INTERNAL: Do not use.
*
* Provides classes modeling security-relevant aspects of the `trender` PyPI package.
* See https://github.com/cesbit/trender.
*/
module TRender {
/** A call to `trender.TRender`. */
private class TRenderTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
TRenderTemplateConstruction() {
this = API::moduleImport("trender").getMember("TRender").getACall()
}

override DataFlow::Node getSourceArg() { result = this.getArg(0) }
}
}
Loading
Loading