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: Port old experimental points-to based queries #13990

Merged
merged 13 commits into from
Aug 28, 2023
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.ApiGraphs

/**
* A data-flow node that constructs a template.
*
* 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 a class for modeling new system-command execution APIs. */
module TemplateConstruction {
/**
* A data-flow node that constructs a template.
*
* 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();
}
}

// -----------------------------------------------------------------------------
/** A call to `airspeed.Template`. */
class AirspeedTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
AirspeedTemplateConstruction() {
this = API::moduleImport("airspeed").getMember("Template").getACall()
}

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

/** A call to `bottle.SimpleTemplate`. */
class BottleSimpleTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
BottleSimpleTemplateConstruction() {
this = API::moduleImport("bottle").getMember("SimpleTemplate").getACall()
}

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

/** A call to `bottle.template`. */
class BottleTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
BottleTemplateConstruction() {
this = API::moduleImport("bottle").getMember("template").getACall()
}

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

/** A call to `chameleon.PageTemplate`. */
class ChameleonTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
ChameleonTemplateConstruction() {
this = API::moduleImport("chameleon").getMember("PageTemplate").getACall()
}

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

/** A call to `Cheetah.Template.Template`. */
class CheetahTemplateConstruction extends TemplateConstruction::Range, API::CallNode {
CheetahTemplateConstruction() {
this =
API::moduleImport("Cheetah")
.getMember("Template")
.getMember("Template")
.getASubclass*()
.getACall()
}

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

/** A call to `chevron.render`. */
class ChevronRenderConstruction extends TemplateConstruction::Range, API::CallNode {
ChevronRenderConstruction() { this = API::moduleImport("chevron").getMember("render").getACall() }

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

/** A call to `django.template.Template` */
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 django.template.engines["django"]].from_string
/** A call to `flask.render_template_string`. */
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) }
}

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

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

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

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

/** A call to `jinja2.Template`. */
class Jinja2TemplateConstruction extends TemplateConstruction::Range, API::CallNode {
Jinja2TemplateConstruction() {
this = API::moduleImport("jinja2").getMember("Template").getACall()
}

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

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

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

/** A call to `mako.template.Template`. */
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) }
}

/** A call to `trender.TRender`. */
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 @@ -11,25 +11,10 @@
*/

import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import experimental.semmle.python.templates.Ssti
/* Flow */
import semmle.python.security.strings.Untrusted
import TemplateInjectionQuery
import TemplateInjectionFlow::PathGraph

class TemplateInjectionConfiguration extends TaintTracking::Configuration {
TemplateInjectionConfiguration() { this = "Template injection configuration" }

deprecated override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}

deprecated override predicate isSink(TaintTracking::Sink sink) { sink instanceof SSTISink }
}

from TemplateInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "This Template depends on $@.", src.getSource(),
"a user-provided value"
from TemplateInjectionFlow::PathNode source, TemplateInjectionFlow::PathNode sink
where TemplateInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This Template depends on $@.", source.getNode(),
"user-provided value"
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "template injection"
* vulnerabilities, as well as extension points for adding your own.
*/

private import python
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
* "template injection"
* vulnerabilities, as well as extension points for adding your own.
*/
module TemplateInjection {
/**
* A data flow source for "template injection" vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }

/**
* A data flow sink for "template injection" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }

/**
* A sanitizer for "template injection" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }

/**
* A source of remote user input, considered as a flow source.
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }

/**
* A SQL statement of a SQL construction, considered as a flow sink.
*/
class TemplateConstructionAsSink extends Sink {
TemplateConstructionAsSink() { this = any(TemplateConstruction c).getSourceArg() }
}

/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Provides a taint-tracking configuration for detecting "template injection" vulnerabilities.
*/

private import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import TemplateInjectionCustomizations::TemplateInjection

module TemplateInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { node instanceof Source }

predicate isSink(DataFlow::Node node) { node instanceof Sink }

predicate isBarrierIn(DataFlow::Node node) { node instanceof Sanitizer }
}

module TemplateInjectionFlow = TaintTracking::Global<TemplateInjectionConfig>;
36 changes: 0 additions & 36 deletions python/ql/src/experimental/Security/CWE-091/Xslt.ql

This file was deleted.

Loading