Skip to content

Commit

Permalink
Merge pull request #13990 from RasmusWL/experimental-cleanup
Browse files Browse the repository at this point in the history
Python: Port old experimental points-to based queries
  • Loading branch information
RasmusWL authored Aug 28, 2023
2 parents 9957e26 + 7cba6cd commit 38b7812
Show file tree
Hide file tree
Showing 83 changed files with 693 additions and 1,009 deletions.
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) }
}
27 changes: 6 additions & 21 deletions python/ql/src/experimental/Security/CWE-074/TemplateInjection.ql
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

0 comments on commit 38b7812

Please sign in to comment.