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

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
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
9 changes: 8 additions & 1 deletion python/ql/lib/semmle/python/Frameworks.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +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.BSon
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 @@ -30,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 @@ -42,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 @@ -78,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
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`.
}
12 changes: 12 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,16 @@ module Flask {
preservesValue = false
}
}

/** A call to `flask.render_template_string` or `flask.stream_template_string` as a template construction sink. */
private class FlaskTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
FlaskTemplateConstruction() {
this =
API::moduleImport("flask")
.getMember(["render_template_string", "stream_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", "TextTemplate"])
.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) }
}
}
48 changes: 48 additions & 0 deletions python/ql/lib/semmle/python/frameworks/Jinja2.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 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) }
}

/** Definitions for modeling jinja `Environment`s. */
module 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`. */
API::Node instance() { result = classRef().getAnInstance() }

/** A call to `jinja2.Environment.from_string`. */
private class Jinja2FromStringConstruction extends TemplateConstruction::Range, API::CallNode {
Jinja2FromStringConstruction() {
this = EnvironmentClass::instance().getMember("from_string").getACall()
}

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) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
private import TemplateConstructionConcept

/**
* Provides default sources, sinks and sanitizers for detecting
Expand All @@ -32,11 +31,6 @@ module TemplateInjection {
*/
abstract class Sanitizer extends DataFlow::Node { }

/**
* DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead!
*/
deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource;

/**
* An active threat-model source, considered as a flow source.
*/
Expand All @@ -53,7 +47,4 @@ module TemplateInjection {
* A comparison with a constant, considered as a sanitizer-guard.
*/
class ConstCompareAsSanitizerGuard extends Sanitizer, ConstCompareBarrier { }

/** DEPRECATED: Use ConstCompareAsSanitizerGuard instead. */
deprecated class StringConstCompareAsSanitizerGuard = ConstCompareAsSanitizerGuard;
}
Loading
Loading