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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 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,32 @@
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 modelling template construction APIs. */
Fixed Show fixed Hide fixed
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,12 +11,16 @@ private import semmle.python.frameworks.Aiohttp
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.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 @@ -29,10 +33,12 @@ private import semmle.python.frameworks.FastApi
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 @@ -41,6 +47,7 @@ private import semmle.python.frameworks.Ldap3
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 @@ -77,6 +84,7 @@ private import semmle.python.frameworks.Streamlit
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
26 changes: 26 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Airspeed.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Provides classes modeling security-relevant aspects of the `airspeed` library.
* See https://github.com/purcell/airspeed.
*/

private import python
private import semmle.python.dataflow.new.DataFlow
Fixed Show fixed Hide fixed
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) }
}
}
37 changes: 37 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Bottle.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Provides classes modeling security-relevant aspects of the `bottle` PyPI package.
* See https://bottlepy.org/docs/dev/.
*/

private import python
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
private import semmle.python.frameworks.Stdlib

/**
* INTERNAL: Do not use.
*
* Provides models for the `bottle` PyPI package.
* See https://bottlepy.org/docs/dev/.
*/
module Bottle {
/** Gets a reference to the `bottle` module. */
API::Node bottle() { result = API::moduleImport("bottle") }

/** Provides models for the `bottle` module. */
module BottleModule {
/** 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) }
}
}
}
}
26 changes: 26 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Chameleon.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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.dataflow.new.DataFlow
Fixed Show fixed Hide fixed
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) }
}
}
26 changes: 26 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Chevron.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Provides classes modeling security-relevant aspects of the `chevron` PyPI package.
* See https://pypi.org/project/chevron.
*/

private import python
private import semmle.python.dataflow.new.DataFlow
Fixed Show fixed Hide fixed
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) }
}
}
15 changes: 15 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,19 @@ 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) }
}
}
45 changes: 45 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Genshi.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Provides classes modeling security-relevant aspects of the `Genshi` PyPI package.
* See https://genshi.edgewall.org/.
*/

private import python
private import semmle.python.dataflow.new.DataFlow
Fixed Show fixed Hide fixed
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) }
}
}
56 changes: 56 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Jinja2.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Provides classes modeling security-relevant aspects of the `jinja2` PyPI package.
* See https://jinja.palletsprojects.com.
*/

private import python
private import semmle.python.dataflow.new.DataFlow
Fixed Show fixed Hide fixed
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 28 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) }
}
}
}
26 changes: 26 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Mako.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Provides classes modeling security-relevant aspects of the `Mako` PyPI package.
* See https://www.makotemplates.org/.
*/

private import python
private import semmle.python.dataflow.new.DataFlow
Fixed Show fixed Hide fixed
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) }
}
}
26 changes: 26 additions & 0 deletions python/ql/lib/semmle/python/frameworks/TRender.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Provides classes modeling security-relevant aspects of the `trender` PyPI package.
* See https://github.com/cesbit/trender.
*/

private import python
private import semmle.python.dataflow.new.DataFlow
Fixed Show fixed Hide fixed
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