Merge branch 'main' into python-add-typetracker

This commit is contained in:
Taus Brock-Nannestad
2020-08-27 17:05:26 +02:00
708 changed files with 33214 additions and 5768 deletions

View File

@@ -0,0 +1,3 @@
name: codeql-python-examples
version: 0.0.0
libraryPathDependencies: codeql-python

View File

@@ -14,8 +14,8 @@ arguments on the right hand side of the expression. Otherwise, a <code>TypeError
</p></recommendation>
<example>
<p>In the following example the right hand side of the formatting operation can be of length 2, which does not match the format string&lt;./p>
</p><sample src="WrongNumberArgumentsForFormat.py" />
<p>In the following example the right hand side of the formatting operation can be of length 2, which does not match the format string.</p>
<sample src="WrongNumberArgumentsForFormat.py" />
</example>
<references>

View File

@@ -26,7 +26,7 @@ predicate all_defined(ModuleValue exporter) {
}
from ImportStar imp, ModuleValue exporter
where import_star(imp, exporter) and not all_defined(exporter)
where import_star(imp, exporter) and not all_defined(exporter) and not exporter.isAbsent()
select imp,
"Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.",
exporter, exporter.getName()

View File

@@ -6,7 +6,7 @@
<overview>
<p>
Sanitizing untrusted URLs is an important technique for
Sanitizing untrusted URLs is a common technique for
preventing attacks such as request forgeries and malicious
redirections. Often, this is done by checking that the host of a URL
is in a set of allowed hosts.

View File

@@ -6,7 +6,7 @@
<overview>
<p>
Sanitizing untrusted URLs is an important technique for
Sanitizing untrusted URLs is a common technique for
preventing attacks such as request forgeries and malicious
redirections. Usually, this is done by checking that the host of a URL
is in a set of allowed hosts.

View File

@@ -7,3 +7,11 @@
tags contain:
- ide-contextual-queries/local-definitions
- ide-contextual-queries/local-references
- query: Lexical/FCommentedOutCode.ql
- query: Metrics/Dependencies/ExternalDependencies.ql
- query: Metrics/Dependencies/ExternalDependenciesSourceLinks.ql
- query: Metrics/FLinesOfCode.ql
- query: Metrics/FLinesOfComments.ql
- query: Metrics/FLinesOfDuplicatedCode.ql
- query: Metrics/FLinesOfSimilarCode.ql
- query: Metrics/FNumberOfTests.ql

View File

@@ -0,0 +1,19 @@
from django.urls import path
from django.http import HttpResponse
from jinja2 import Template as Jinja2_Template
from jinja2 import Environment, DictLoader, escape
def a(request):
# Load the template
template = request.GET['template']
t = Jinja2_Template(template)
name = request.GET['name']
# Render the template with the context data
html = t.render(name=escape(name))
return HttpResponse(html)
urlpatterns = [
path('a', a),
]

View File

@@ -0,0 +1,20 @@
from django.urls import path
from django.http import HttpResponse
from jinja2 import Template as Jinja2_Template
from jinja2 import Environment, DictLoader, escape
def a(request):
# Load the template
template = request.GET['template']
env = SandboxedEnvironment(undefined=StrictUndefined)
t = env.from_string(template)
name = request.GET['name']
# Render the template with the context data
html = t.render(name=escape(name))
return HttpResponse(html)
urlpatterns = [
path('a', a),
]

View File

@@ -0,0 +1,24 @@
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp>
<overview>
<p>
Template Injection occurs when user input is embedded in a template in an unsafe manner.
When an attacker is able to use native template syntax to inject a malicious payload into a template, which is then executed server-side is results in Server Side Template Injection.
</p>
</overview>
<recommendation>
<p>
To fix this, ensure that an untrusted value is not used as a template. If the application requirements do not alow this, use a sandboxed environment where access to unsafe attributes and methods is prohibited.
</p>
</recommendation>
<example>
<p>Consider the example given below, an untrusted HTTP parameter `template` is used to generate a Jinja2 template string. This can lead to remote code execution. </p>
<sample src="JinjaBad.py" />
<p>Here we have fixed the problem by using the Jinja sandbox environment for evaluating untrusted code.</p>
<sample src="JinjaGood.py" />
</example>
<references>
<li>Portswigger : [Server Side Template Injection](https://portswigger.net/web-security/server-side-template-injection)</li>
</references>
</qhelp>

View File

@@ -0,0 +1,34 @@
/**
* @name Server Side Template Injection
* @description Using user-controlled data to create a template can cause security issues.
* @kind path-problem
* @problem.severity error
* @precision high
* @id py/template-injection
* @tags security
* external/cwe/cwe-074
*/
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
class TemplateInjectionConfiguration extends TaintTracking::Configuration {
TemplateInjectionConfiguration() { this = "Template injection configuration" }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
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"

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -512,13 +512,19 @@ abstract class CallContext extends TCallContext {
abstract predicate relevantFor(DataFlowCallable callable);
}
class CallContextAny extends CallContext, TAnyCallContext {
abstract class CallContextNoCall extends CallContext { }
class CallContextAny extends CallContextNoCall, TAnyCallContext {
override string toString() { result = "CcAny" }
override predicate relevantFor(DataFlowCallable callable) { any() }
}
abstract class CallContextCall extends CallContext { }
abstract class CallContextCall extends CallContext {
/** Holds if this call context may be `call`. */
bindingset[call]
abstract predicate matchesCall(DataFlowCall call);
}
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() {
@@ -529,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
recordDataFlowCallSite(getCall(), callable)
}
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
DataFlowCall getCall() { this = TSpecificCall(result) }
}
@@ -538,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override predicate relevantFor(DataFlowCallable callable) {
exists(ParameterNode p | p.getEnclosingCallable() = callable)
}
override predicate matchesCall(DataFlowCall call) { any() }
}
class CallContextReturn extends CallContext, TReturn {
class CallContextReturn extends CallContextNoCall, TReturn {
override string toString() {
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
}

View File

@@ -1,5 +1,6 @@
private import python
private import DataFlowPublic
import semmle.python.SpecialMethods
//--------
// Data flow graph
@@ -7,6 +8,13 @@ private import DataFlowPublic
//--------
// Nodes
//--------
predicate isExpressionNode(ControlFlowNode node) { node.getNode() instanceof Expr }
/** A control flow node which is also a dataflow node */
class DataFlowCfgNode extends ControlFlowNode {
DataFlowCfgNode() { isExpressionNode(this) }
}
/**
* A node associated with an object after an operation that might have
* changed its state.
@@ -97,20 +105,127 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
//--------
// Global flow
//--------
/**
* IPA type for DataFlowCallable.
* A callable is either a callable value or a class.
*/
newtype TDataFlowCallable =
TCallableValue(CallableValue callable) or
TClassValue(ClassValue c)
/** Represents a callable */
class DataFlowCallable = CallableValue;
abstract class DataFlowCallable extends TDataFlowCallable {
/** Gets a textual representation of this element. */
abstract string toString();
/** Represents a call to a callable */
class DataFlowCall extends CallNode {
DataFlowCallable callable;
/** Gets a call to this callable. */
abstract CallNode getACall();
DataFlowCall() { this = callable.getACall() }
/** Gets the scope of this callable */
abstract Scope getScope();
/** Gets the specified parameter of this callable */
abstract NameNode getParameter(int n);
/** Gets the name of this callable. */
abstract string getName();
}
class DataFlowCallableValue extends DataFlowCallable, TCallableValue {
CallableValue callable;
DataFlowCallableValue() { this = TCallableValue(callable) }
override string toString() { result = callable.toString() }
override CallNode getACall() { result = callable.getACall() }
override Scope getScope() { result = callable.getScope() }
override NameNode getParameter(int n) { result = callable.getParameter(n) }
override string getName() { result = callable.getName() }
}
class DataFlowClassValue extends DataFlowCallable, TClassValue {
ClassValue c;
DataFlowClassValue() { this = TClassValue(c) }
override string toString() { result = c.toString() }
override CallNode getACall() { result = c.getACall() }
override Scope getScope() { result = c.getScope() }
override NameNode getParameter(int n) {
result.getNode() = c.getScope().getInitMethod().getArg(n + 1).asName()
}
override string getName() { result = c.getName() }
}
newtype TDataFlowCall =
TCallNode(CallNode call) or
TSpecialCall(SpecialMethodCallNode special)
abstract class DataFlowCall extends TDataFlowCall {
/** Gets a textual representation of this element. */
abstract string toString();
/** Get the callable to which this call goes. */
DataFlowCallable getCallable() { result = callable }
abstract DataFlowCallable getCallable();
/** Get the specified argument to this call. */
abstract ControlFlowNode getArg(int n);
/** Get the control flow node representing this call. */
abstract ControlFlowNode getNode();
/** Gets the enclosing callable of this call. */
DataFlowCallable getEnclosingCallable() { result.getScope() = this.getNode().getScope() }
abstract DataFlowCallable getEnclosingCallable();
}
/** Represents a call to a callable. */
class CallNodeCall extends DataFlowCall, TCallNode {
CallNode call;
DataFlowCallable callable;
CallNodeCall() {
this = TCallNode(call) and
call = callable.getACall()
}
override string toString() { result = call.toString() }
override ControlFlowNode getArg(int n) { result = call.getArg(n) }
override ControlFlowNode getNode() { result = call }
override DataFlowCallable getCallable() { result = callable }
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
}
/** Represents a call to a special method. */
class SpecialCall extends DataFlowCall, TSpecialCall {
SpecialMethodCallNode special;
SpecialCall() { this = TSpecialCall(special) }
override string toString() { result = special.toString() }
override ControlFlowNode getArg(int n) { result = special.(SpecialMethod::Potential).getArg(n) }
override ControlFlowNode getNode() { result = special }
override DataFlowCallable getCallable() {
result = TCallableValue(special.getResolvedSpecialMethod())
}
override DataFlowCallable getEnclosingCallable() {
result.getScope() = special.getNode().getScope()
}
}
/** A data flow node that represents a call argument. */
@@ -163,7 +278,7 @@ class OutNode extends CfgNode {
* `kind`.
*/
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
call = result.getNode() and
call.getNode() = result.getNode() and
kind = TNormalReturnKind()
}
@@ -220,15 +335,193 @@ predicate jumpStep(Node pred, Node succ) {
// Field flow
//--------
/**
* Holds if data can flow from `node1` to `node2` via an assignment to
* Holds if data can flow from `nodeFrom` to `nodeTo` via an assignment to
* content `c`.
*/
predicate storeStep(Node node1, Content c, Node node2) { none() }
predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
listStoreStep(nodeFrom, c, nodeTo)
or
setStoreStep(nodeFrom, c, nodeTo)
or
tupleStoreStep(nodeFrom, c, nodeTo)
or
dictStoreStep(nodeFrom, c, nodeTo)
or
comprehensionStoreStep(nodeFrom, c, nodeTo)
}
/** Data flows from an element of a list to the list. */
predicate listStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo) {
// List
// `[..., 42, ...]`
// nodeFrom is `42`, cfg node
// nodeTo is the list, `[..., 42, ...]`, cfg node
// c denotes element of list
nodeTo.getNode().(ListNode).getAnElement() = nodeFrom.getNode()
}
/** Data flows from an element of a set to the set. */
predicate setStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo) {
// Set
// `{..., 42, ...}`
// nodeFrom is `42`, cfg node
// nodeTo is the set, `{..., 42, ...}`, cfg node
// c denotes element of list
nodeTo.getNode().(SetNode).getAnElement() = nodeFrom.getNode()
}
/** Data flows from an element of a tuple to the tuple at a specific index. */
predicate tupleStoreStep(CfgNode nodeFrom, TupleElementContent c, CfgNode nodeTo) {
// Tuple
// `(..., 42, ...)`
// nodeFrom is `42`, cfg node
// nodeTo is the tuple, `(..., 42, ...)`, cfg node
// c denotes element of tuple and index of nodeFrom
exists(int n |
nodeTo.getNode().(TupleNode).getElement(n) = nodeFrom.getNode() and
c.getIndex() = n
)
}
/** Data flows from an element of a dictionary to the dictionary at a specific key. */
predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, CfgNode nodeTo) {
// Dictionary
// `{..., "key" = 42, ...}`
// nodeFrom is `42`, cfg node
// nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node
// c denotes element of dictionary and the key `"key"`
exists(KeyValuePair item |
item = nodeTo.getNode().(DictNode).getNode().(Dict).getAnItem() and
nodeFrom.getNode().getNode() = item.getValue() and
c.getKey() = item.getKey().(StrConst).getS()
)
}
/** Data flows from an element expression in a comprehension to the comprehension. */
predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// Comprehension
// `[x+1 for x in l]`
// nodeFrom is `x+1`, cfg node
// nodeTo is `[x+1 for x in l]`, cfg node
// c denotes list or set or dictionary without index
//
// List
nodeTo.getNode().getNode().(ListComp).getElt() = nodeFrom.getNode().getNode() and
c instanceof ListElementContent
or
// Set
nodeTo.getNode().getNode().(SetComp).getElt() = nodeFrom.getNode().getNode() and
c instanceof SetElementContent
or
// Dictionary
nodeTo.getNode().getNode().(DictComp).getElt() = nodeFrom.getNode().getNode() and
c instanceof DictionaryElementAnyContent
}
/**
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
* Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`.
*/
predicate readStep(Node node1, Content c, Node node2) { none() }
predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
subscriptReadStep(nodeFrom, c, nodeTo)
or
popReadStep(nodeFrom, c, nodeTo)
or
comprehensionReadStep(nodeFrom, c, nodeTo)
}
/** Data flows from a sequence to a subscript of the sequence. */
predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// Subscript
// `l[3]`
// nodeFrom is `l`, cfg node
// nodeTo is `l[3]`, cfg node
// c is compatible with 3
nodeFrom.getNode() = nodeTo.getNode().(SubscriptNode).getObject() and
(
c instanceof ListElementContent
or
c instanceof SetElementContent
or
c instanceof DictionaryElementAnyContent
or
c.(TupleElementContent).getIndex() =
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
or
c.(DictionaryElementContent).getKey() =
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(StrConst).getS()
)
}
/** Data flows from a sequence to a call to `pop` on the sequence. */
predicate popReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// set.pop or list.pop
// `s.pop()`
// nodeFrom is `s`, cfg node
// nodeTo is `s.pop()`, cfg node
// c denotes element of list or set
exists(CallNode call, AttrNode a |
call.getFunction() = a and
a.getName() = "pop" and // Should match appropriate call since we tracked a sequence here.
not exists(call.getAnArg()) and
nodeFrom.getNode() = a.getObject() and
nodeTo.getNode() = call and
(
c instanceof ListElementContent
or
c instanceof SetElementContent
)
)
or
// dict.pop
// `d.pop("key")`
// nodeFrom is `d`, cfg node
// nodeTo is `d.pop("key")`, cfg node
// c denotes the key `"key"`
exists(CallNode call, AttrNode a |
call.getFunction() = a and
a.getName() = "pop" and // Should match appropriate call since we tracked a dictionary here.
nodeFrom.getNode() = a.getObject() and
nodeTo.getNode() = call and
c.(DictionaryElementContent).getKey() = call.getArg(0).getNode().(StrConst).getS()
)
}
/** Data flows from a iterated sequence to the variable iterating over the sequence. */
predicate comprehensionReadStep(CfgNode nodeFrom, Content c, EssaNode nodeTo) {
// Comprehension
// `[x+1 for x in l]`
// nodeFrom is `l`, cfg node
// nodeTo is `x`, essa var
// c denotes element of list or set
exists(For f, Comp comp |
f = getCompFor(comp) and
nodeFrom.getNode().getNode() = getCompIter(comp) and
nodeTo.getVar().getDefinition().(AssignmentDefinition).getDefiningNode().getNode() =
f.getTarget() and
(
c instanceof ListElementContent
or
c instanceof SetElementContent
)
)
}
/** This seems to compensate for extractor shortcomings */
For getCompFor(Comp c) {
c.contains(result) and
c.getFunction() = result.getScope()
}
/** This seems to compensate for extractor shortcomings */
AstNode getCompIter(Comp c) {
c.contains(result) and
c.getScope() = result.getScope() and
not result = c.getFunction() and
not exists(AstNode between |
c.contains(between) and
between.contains(result)
)
}
/**
* Holds if values stored inside content `c` are cleared at node `n`. For example,

View File

@@ -20,7 +20,7 @@ newtype TNode =
/** A node corresponding to an SSA variable. */
TEssaNode(EssaVariable var) or
/** A node corresponding to a control flow node. */
TCfgNode(ControlFlowNode node)
TCfgNode(DataFlowCfgNode node)
/**
* An element, viewed as a node in a data flow graph. Either an SSA variable
@@ -33,8 +33,15 @@ class Node extends TNode {
/** Gets the scope of this node. */
Scope getScope() { none() }
private DataFlowCallable getCallableScope(Scope s) {
result.getScope() = s
or
not exists(DataFlowCallable c | c.getScope() = s) and
result = getCallableScope(s.getEnclosingScope())
}
/** Gets the enclosing callable of this node. */
DataFlowCallable getEnclosingCallable() { result.getScope() = this.getScope() }
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
/** Gets the location of this node */
Location getLocation() { none() }
@@ -51,6 +58,15 @@ class Node extends TNode {
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Convenience method for casting to EssaNode and calling getVar. */
EssaVariable asVar() { none() }
/** Convenience method for casting to CfgNode and calling getNode. */
ControlFlowNode asCfgNode() { none() }
/** Convenience method for casting to ExprNode and calling getNode and getNode again. */
Expr asExpr() { none() }
}
class EssaNode extends Node, TEssaNode {
@@ -60,6 +76,8 @@ class EssaNode extends Node, TEssaNode {
EssaVariable getVar() { result = var }
override EssaVariable asVar() { result = var }
/** Gets a textual representation of this element. */
override string toString() { result = var.toString() }
@@ -69,12 +87,14 @@ class EssaNode extends Node, TEssaNode {
}
class CfgNode extends Node, TCfgNode {
ControlFlowNode node;
DataFlowCfgNode node;
CfgNode() { this = TCfgNode(node) }
ControlFlowNode getNode() { result = node }
override ControlFlowNode asCfgNode() { result = node }
/** Gets a textual representation of this element. */
override string toString() { result = node.toString() }
@@ -90,10 +110,14 @@ class CfgNode extends Node, TCfgNode {
* to multiple `ExprNode`s, just like it may correspond to multiple
* `ControlFlow::Node`s.
*/
class ExprNode extends Node { }
class ExprNode extends CfgNode {
ExprNode() { isExpressionNode(node) }
override Expr asExpr() { result = node.getNode() }
}
/** Gets a node corresponding to expression `e`. */
ExprNode exprNode(DataFlowExpr e) { none() }
ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
/**
* The value of a parameter at function entry, viewed as a node in a data
@@ -138,6 +162,62 @@ class BarrierGuard extends Expr {
/**
* A reference contained in an object. This is either a field or a property.
*/
class Content extends string {
Content() { this = "Content" }
newtype TContent =
/** An element of a list. */
TListElementContent() or
/** An element of a set. */
TSetElementContent() or
/** An element of a tuple at a specifik index. */
TTupleElementContent(int index) { exists(any(TupleNode tn).getElement(index)) } or
/** An element of a dictionary under a specific key. */
TDictionaryElementContent(string key) {
key = any(KeyValuePair kvp).getKey().(StrConst).getS()
or
key = any(Keyword kw).getArg()
} or
/** An element of a dictionary at any key. */
TDictionaryElementAnyContent()
class Content extends TContent {
/** Gets a textual representation of this element. */
string toString() { result = "Content" }
}
class ListElementContent extends TListElementContent, Content {
/** Gets a textual representation of this element. */
override string toString() { result = "List element" }
}
class SetElementContent extends TSetElementContent, Content {
/** Gets a textual representation of this element. */
override string toString() { result = "Set element" }
}
class TupleElementContent extends TTupleElementContent, Content {
int index;
TupleElementContent() { this = TTupleElementContent(index) }
/** Gets the index for this tuple element */
int getIndex() { result = index }
/** Gets a textual representation of this element. */
override string toString() { result = "Tuple element at index " + index.toString() }
}
class DictionaryElementContent extends TDictionaryElementContent, Content {
string key;
DictionaryElementContent() { this = TDictionaryElementContent(key) }
/** Gets the index for this tuple element */
string getKey() { result = key }
/** Gets a textual representation of this element. */
override string toString() { result = "Dictionary element at key " + key }
}
class DictionaryElementAnyContent extends TDictionaryElementAnyContent, Content {
/** Gets a textual representation of this element. */
override string toString() { result = "Any dictionary element" }
}

View File

@@ -1,7 +1,7 @@
private import python
private import TaintTrackingPublic
private import experimental.dataflow.DataFlow
private import experimental.dataflow.internal.DataFlowPrivate
private import experimental.dataflow.internal.TaintTrackingPublic
/**
* Holds if `node` should be a barrier in all global taint flow configurations
@@ -10,12 +10,116 @@ private import experimental.dataflow.internal.DataFlowPrivate
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
/**
* Holds if the additional step from `pred` to `succ` should be included in all
* Holds if the additional step from `nodeFrom` to `nodeTo` should be included in all
* global taint flow configurations.
*/
predicate defaultAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
none()
// localAdditionalTaintStep(pred, succ)
// or
// succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
localAdditionalTaintStep(nodeFrom, nodeTo)
or
any(AdditionalTaintStep a).step(nodeFrom, nodeTo)
}
/**
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
* different objects.
*/
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
concatStep(nodeFrom, nodeTo)
or
subscriptStep(nodeFrom, nodeTo)
or
stringManipulation(nodeFrom, nodeTo)
}
/**
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to concatenation.
*
* Note that since we cannot easily distinguish interesting types (like string, list, tuple),
* we consider any `+` operation to propagate taint. After consulting with the JS team, this
* doesn't sound like it is a big problem in practice.
*/
predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
exists(BinaryExprNode add | add = nodeTo.getNode() |
add.getOp() instanceof Add and add.getAnOperand() = nodeFrom.getNode()
)
}
/**
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to subscripting.
*/
predicate subscriptStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
nodeTo.getNode().(SubscriptNode).getObject() = nodeFrom.getNode()
}
/**
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to string
* manipulation.
*
* Note that since we cannot easily distinguish when something is a string, this can
* also make taint flow on `<non string>.replace(foo, bar)`.
*/
predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
// transforming something tainted into a string will make the string tainted
exists(CallNode call | call = nodeTo.getNode() |
call.getFunction().(NameNode).getId() in ["str", "bytes", "unicode"] and
(
nodeFrom.getNode() = call.getArg(0)
or
nodeFrom.getNode() = call.getArgByName("object")
)
)
or
// String methods. Note that this doesn't recognize `meth = "foo".upper; meth()`
exists(CallNode call, string method_name, ControlFlowNode object |
call = nodeTo.getNode() and
object = call.getFunction().(AttrNode).getObject(method_name)
|
nodeFrom.getNode() = object and
method_name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map",
"join", "ljust", "lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase",
"title", "upper", "zfill", "encode", "decode"]
or
method_name = "replace" and
nodeFrom.getNode() = call.getArg(1)
or
method_name = "format" and
nodeFrom.getNode() = call.getAnArg()
or
// str -> List[str]
// TODO: check if these should be handled differently in regards to content
nodeFrom.getNode() = object and
method_name in ["partition", "rpartition", "rsplit", "split", "splitlines"]
or
// List[str] -> str
// TODO: check if these should be handled differently in regards to content
method_name = "join" and
nodeFrom.getNode() = call.getArg(0)
or
// Mapping[str, Any] -> str
method_name = "format_map" and
nodeFrom.getNode() = call.getArg(0)
)
or
// % formatting
exists(BinaryExprNode fmt | fmt = nodeTo.getNode() |
fmt.getOp() instanceof Mod and
(
fmt.getLeft() = nodeFrom.getNode()
or
fmt.getRight() = nodeFrom.getNode()
)
)
or
// string multiplication -- `"foo" * 10`
exists(BinaryExprNode mult | mult = nodeTo.getNode() |
mult.getOp() instanceof Mult and
mult.getLeft() = nodeFrom.getNode()
)
or
// f-strings
nodeTo.getNode().getNode().(Fstring).getAValue() = nodeFrom.getNode().getNode()
// TODO: Handle encode/decode from base64/quopri
// TODO: Handle os.path.join
// TODO: Handle functions in https://docs.python.org/3/library/binascii.html
}

View File

@@ -6,27 +6,52 @@
private import python
private import TaintTrackingPrivate
private import experimental.dataflow.DataFlow
// /**
// * Holds if taint propagates from `source` to `sink` in zero or more local
// * (intra-procedural) steps.
// */
// predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
// // /**
// // * Holds if taint can flow from `e1` to `e2` in zero or more
// // * local (intra-procedural) steps.
// // */
// // predicate localExprTaint(Expr e1, Expr e2) {
// // localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
// // }
// // /** A member (property or field) that is tainted if its containing object is tainted. */
// // abstract class TaintedMember extends AssignableMember { }
// /**
// * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
// * (intra-procedural) step.
// */
// predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// // Ordinary data flow
// DataFlow::localFlowStep(nodeFrom, nodeTo)
// or
// localAdditionalTaintStep(nodeFrom, nodeTo)
// }
// Local taint flow and helpers
/**
* Holds if taint propagates from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
/**
* Holds if taint can flow from `e1` to `e2` in zero or more local (intra-procedural)
* steps.
*/
predicate localExprTaint(Expr e1, Expr e2) {
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Ordinary data flow
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
localAdditionalTaintStep(nodeFrom, nodeTo)
}
// AdditionalTaintStep for global taint flow
private newtype TUnit = TMkUnit()
/** A singleton class containing a single dummy "unit" value. */
private class Unit extends TUnit {
/** Gets a textual representation of this element. */
string toString() { result = "unit" }
}
/**
* A unit class for adding additional taint steps.
*
* Extend this class to add additional taint steps that should apply to all
* taint configurations.
*/
class AdditionalTaintStep extends Unit {
/**
* Holds if the step from `nodeFrom` to `nodeTo` should be considered a taint
* step for all configurations.
*/
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
}

View File

@@ -0,0 +1,27 @@
/** Provides classes which model the `airspeed` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `airspeed.Template` */
ClassValue theAirspeedTemplateClass() { result = Value::named("airspeed.Template") }
/**
* Sink representing the `airspeed.Template` class instantiation argument.
*
* import airspeed
* temp = airspeed.Template(`"sink"`)
*/
class AirspeedTemplateSink extends SSTISink {
override string toString() { result = "argument to airspeed.Template()" }
AirspeedTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theAirspeedTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,46 @@
/** Provides classes which model the `bottle` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `bottle.SimpleTemplate` */
ClassValue theBottleSimpleTemplateClass() { result = Value::named("bottle.SimpleTemplate") }
/**
* Sink representing the `bottle.SimpleTemplate` class instantiation argument.
*
* from bottle import SimpleTemplate
* template = SimpleTemplate(`sink`)
*/
class BottleSimpleTemplateSink extends SSTISink {
override string toString() { result = "argument to bottle.SimpleTemplate()" }
BottleSimpleTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theBottleSimpleTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* Sink representing the `bottle.template` function call argument.
*
* from bottle import template
* tmp = template(`sink`)
*/
class BottleTemplateSink extends SSTISink {
override string toString() { result = "argument to bottle.template()" }
BottleTemplateSink() {
exists(CallNode call |
call.getFunction() = theBottleModule().attr("template").getAReference() and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,27 @@
/** Provides classes which model the `Chameleon` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `chameleon.PageTemplate` */
ClassValue theChameleonPageTemplateClass() { result = Value::named("chameleon.PageTemplate") }
/**
* Sink representing the `chameleon.PageTemplate` class instantiation argument.
*
* from chameleon import PageTemplate
* template = PageTemplate(`sink`)
*/
class ChameleonTemplateSink extends SSTISink {
override string toString() { result = "argument to Chameleon.PageTemplate()" }
ChameleonTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theChameleonPageTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,37 @@
/** Provides classes which model the `Cheetah3` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `Cheetah.Template.Template` */
ClassValue theCheetahTemplateClass() { result = Value::named("Cheetah.Template.Template") }
/**
* Sink representing the instantiation argument of any class which derives from
* the `Cheetah.Template.Template` class .
*
* from Cheetah.Template import Template
* class Template3(Template):
* title = 'Hello World Example!'
* contents = 'Hello World!'
* t3 = Template3("sink")
*
* This will also detect cases of the following type :
*
* from Cheetah.Template import Template
* t3 = Template("sink")
*/
class CheetahTemplateInstantiationSink extends SSTISink {
override string toString() { result = "argument to Cheetah.Template.Template()" }
CheetahTemplateInstantiationSink() {
exists(CallNode call, ClassValue cv |
cv.getASuperType() = theCheetahTemplateClass() and
call.getFunction().pointsTo(cv) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,36 @@
/** Provides classes which model the `chevron` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the Value representing `chevron.render` function */
Value theChevronRenderFunc() { result = Value::named("chevron.render") }
/**
* Sink representing the `chevron.render` function call argument.
*
* import chevron
* tmp = chevron.render(`sink`,{ 'key' : 'value' })
*/
class ChevronRenderSink extends SSTISink {
override string toString() { result = "argument to chevron.render()" }
ChevronRenderSink() {
exists(CallNode call |
call.getFunction() = theChevronRenderFunc().getAReference() and
call.getArg(0) = this
)
// TODO: this should also detect :
// import chevron
// args = {
// 'template': 'sink',
// 'data': {
// 'mustache': 'World'
// }
// }
// chevron.render(**args)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,35 @@
/** Provides classes which model the `DjangoTemplate` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
ClassValue theDjangoTemplateClass() { result = Value::named("django.template.Template") }
/**
* Sink representng `django.template.Template` class instantiation argument.
*
* from django.template import Template
* template = Template(`sink`)
*/
class DjangoTemplateTemplateSink extends SSTISink {
override string toString() { result = "argument to Django.template()" }
DjangoTemplateTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theDjangoTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
// TODO (intentionally commented out QLDoc, since qlformat will delete those lines otherwise)
// /**
// * Sinks representng the django.template.Template class instantiation.
// *
// * from django.template import engines
// *
// * django_engine = engines["django"]
// * template = django_engine.from_string(`sink`)
// */

View File

@@ -0,0 +1,26 @@
/** Provides classes which model templates in the`flask` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
Value theFlaskRenderTemplateClass() { result = Value::named("flask.render_template_string") }
/**
* Sink representng `flask.render_template_string` function call argument.
*
* from flask import render_template_string
* render_template_string(`sink`)
*/
class FlaskTemplateSink extends SSTISink {
override string toString() { result = "argument to flask.render_template_string()" }
FlaskTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theFlaskRenderTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,51 @@
/** Provides classes which model the `Genshi` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `Genshi.template.TextTemplate` */
ClassValue theGenshiTextTemplateClass() { result = Value::named("genshi.template.TextTemplate") }
/** returns the ClassValue representing `Genshi.template.MarkupTemplate` */
ClassValue theGenshiMarkupTemplateClass() {
result = Value::named("genshi.template.MarkupTemplate")
}
/**
* Sink representing the `genshi.template.TextTemplate` class instantiation argument.
*
* from genshi.template import TextTemplate
* tmpl = TextTemplate('sink')
*/
class GenshiTextTemplateSink extends SSTISink {
override string toString() { result = "argument to genshi.template.TextTemplate()" }
GenshiTextTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theGenshiTextTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* Sink representing the `genshi.template.MarkupTemplate` class instantiation argument.
*
* from genshi.template import MarkupTemplate
* tmpl = MarkupTemplate('sink')
*/
class GenshiMarkupTemplateSink extends SSTISink {
override string toString() { result = "argument to genshi.template.MarkupTemplate()" }
GenshiMarkupTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theGenshiMarkupTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,49 @@
/** Provides classes which model the `Jinja2` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `jinja2.Template` */
ClassValue theJinja2TemplateClass() { result = Value::named("jinja2.Template") }
/** returns the ClassValue representing `jinja2.Template` */
Value theJinja2FromStringValue() { result = Value::named("jinja2.from_string") }
/**
* Sink representing the `jinja2.Template` class instantiation argument.
*
* from jinja2 import Template
* template = Template(`sink`)
*/
class Jinja2TemplateSink extends SSTISink {
override string toString() { result = "argument to jinja2.Template()" }
Jinja2TemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theJinja2TemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* Sink representing the `jinja2.from_string` function call argument.
*
* from jinja2 import from_string
* template = from_string(`sink`)
*/
class Jinja2FromStringSink extends SSTISink {
override string toString() { result = "argument to jinja2.from_string()" }
Jinja2FromStringSink() {
exists(CallNode call |
call.getFunction().pointsTo(theJinja2FromStringValue()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,27 @@
/** Provides classes which model the `Mako` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `mako.template.Template` */
ClassValue theMakoTemplateClass() { result = Value::named("mako.template.Template") }
/**
* Sink representing the `mako.template.Template` class instantiation argument.
*
* from mako.template import Template
* mytemplate = Template("hello world!")
*/
class MakoTemplateSink extends SSTISink {
override string toString() { result = "argument to mako.template.Template()" }
MakoTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theMakoTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,7 @@
import semmle.python.dataflow.TaintTracking
/**
* A generic taint sink that is vulnerable to template inclusions.
* The `temp` in `jinja2.Template(temp)` and similar.
*/
abstract class SSTISink extends TaintSink { }

View File

@@ -0,0 +1,13 @@
/** Imports all files which model potential SSTI sinks */
import experimental.semmle.python.templates.Airspeed
import experimental.semmle.python.templates.Bottle
import experimental.semmle.python.templates.Chameleon
import experimental.semmle.python.templates.Cheetah
import experimental.semmle.python.templates.Chevron
import experimental.semmle.python.templates.DjangoTemplate
import experimental.semmle.python.templates.FlaskTemplate
import experimental.semmle.python.templates.Genshi
import experimental.semmle.python.templates.Jinja
import experimental.semmle.python.templates.Mako
import experimental.semmle.python.templates.TRender

View File

@@ -0,0 +1,27 @@
/** Provides classes which model the `TRender` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `trender.TRender` */
ClassValue theTRenderTemplateClass() { result = Value::named("trender.TRender") }
/**
* Sink representing the `trender.TRender` class instantiation argument.
*
* from trender import TRender
* template = TRender(`sink`)
*/
class TRenderTemplateSink extends SSTISink {
override string toString() { result = "argument to trender.TRender()" }
TRenderTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theTRenderTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,117 @@
/**
* Provides support for special methods.
* This is done in two steps:
* - A subset of `ControlFlowNode`s are labelled as potentially corresponding to
* a special method call (by being an instance of `SpecialMethod::Potential`).
* - A subset of the potential special method calls are labelled as being actual
* special method calls (`SpecialMethodCallNode`) if the appropriate method is defined.
* Extend `SpecialMethod::Potential` to capture more cases.
*/
import python
/** A control flow node which might correspond to a special method call. */
class PotentialSpecialMethodCallNode extends ControlFlowNode {
PotentialSpecialMethodCallNode() { this instanceof SpecialMethod::Potential }
}
/**
* Machinery for detecting special method calls.
* Extend `SpecialMethod::Potential` to capture more cases.
*/
module SpecialMethod {
/** A control flow node which might correspond to a special method call. */
abstract class Potential extends ControlFlowNode {
/** Gets the name of the method that would be called. */
abstract string getSpecialMethodName();
/** Gets the control flow node that would be passed as the specified argument. */
abstract ControlFlowNode getArg(int n);
/**
* Gets the control flow node corresponding to the instance
* that would define the special method.
*/
ControlFlowNode getSelf() { result = this.getArg(0) }
}
/** A binary expression node that might correspond to a special method call. */
class SpecialBinOp extends Potential, BinaryExprNode {
Operator operator;
SpecialBinOp() { this.getOp() = operator }
override string getSpecialMethodName() { result = operator.getSpecialMethodName() }
override ControlFlowNode getArg(int n) {
n = 0 and result = this.getLeft()
or
n = 1 and result = this.getRight()
}
}
/** A subscript expression node that might correspond to a special method call. */
abstract class SpecialSubscript extends Potential, SubscriptNode {
override ControlFlowNode getArg(int n) {
n = 0 and result = this.getObject()
or
n = 1 and result = this.getIndex()
}
}
/** A subscript expression node that might correspond to a call to __getitem__. */
class SpecialGetItem extends SpecialSubscript {
SpecialGetItem() { this.isLoad() }
override string getSpecialMethodName() { result = "__getitem__" }
}
/** A subscript expression node that might correspond to a call to __setitem__. */
class SpecialSetItem extends SpecialSubscript {
SpecialSetItem() { this.isStore() }
override string getSpecialMethodName() { result = "__setitem__" }
override ControlFlowNode getArg(int n) {
n = 0 and result = this.getObject()
or
n = 1 and result = this.getIndex()
or
n = 2 and result = this.getValueNode()
}
private ControlFlowNode getValueNode() {
exists(AssignStmt a |
a.getATarget() = this.getNode() and
result.getNode() = a.getValue()
)
or
exists(AugAssign a |
a.getTarget() = this.getNode() and
result.getNode() = a.getValue()
)
}
}
/** A subscript expression node that might correspond to a call to __delitem__. */
class SpecialDelItem extends SpecialSubscript {
SpecialDelItem() { this.isDelete() }
override string getSpecialMethodName() { result = "__delitem__" }
}
}
/** A control flow node corresponding to a special method call. */
class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
Value resolvedSpecialMethod;
SpecialMethodCallNode() {
exists(SpecialMethod::Potential pot |
this.(SpecialMethod::Potential) = pot and
pot.getSelf().pointsTo().getClass().lookup(pot.getSpecialMethodName()) = resolvedSpecialMethod
)
}
/** The method that is called. */
Value getResolvedSpecialMethod() { result = resolvedSpecialMethod }
}

View File

@@ -27,7 +27,8 @@ abstract class StringKind extends TaintKind {
os_path_join(fromnode, tonode) or
str_format(fromnode, tonode) or
encode_decode(fromnode, tonode) or
to_str(fromnode, tonode)
to_str(fromnode, tonode) or
f_string(fromnode, tonode)
)
or
result = this and copy_call(fromnode, tonode)
@@ -61,13 +62,13 @@ private class StringEqualitySanitizer extends Sanitizer {
}
}
/* tonode = ....format(fromnode) */
/** tonode = ....format(fromnode) */
private predicate str_format(ControlFlowNode fromnode, CallNode tonode) {
tonode.getFunction().(AttrNode).getName() = "format" and
tonode.getAnArg() = fromnode
}
/* tonode = codec.[en|de]code(fromnode)*/
/** tonode = codec.[en|de]code(fromnode) */
private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) {
exists(FunctionObject func, string name |
not func.getFunction().isMethod() and
@@ -81,7 +82,7 @@ private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) {
)
}
/* tonode = str(fromnode)*/
/** tonode = str(fromnode) */
private predicate to_str(ControlFlowNode fromnode, CallNode tonode) {
tonode.getAnArg() = fromnode and
(
@@ -91,7 +92,7 @@ private predicate to_str(ControlFlowNode fromnode, CallNode tonode) {
)
}
/* tonode = fromnode[:] */
/** tonode = fromnode[:] */
private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
exists(Slice all |
all = tonode.getIndex().getNode() and
@@ -101,12 +102,17 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
)
}
/* tonode = os.path.join(..., fromnode, ...) */
/** tonode = os.path.join(..., fromnode, ...) */
private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
tonode = Value::named("os.path.join").getACall() and
tonode.getAnArg() = fromnode
}
/** tonode = f"... {fromnode} ..." */
private predicate f_string(ControlFlowNode fromnode, ControlFlowNode tonode) {
tonode.getNode().(Fstring).getAValue() = fromnode.getNode()
}
/**
* A kind of "taint", representing a dictionary mapping str->"taint"
*

View File

@@ -0,0 +1,44 @@
import python
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.Exceptions
class SimpleSource extends TaintSource {
SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" }
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
override string toString() { result = "taint source" }
}
class ListSource extends TaintSource {
ListSource() { this.(NameNode).getId() = "TAINTED_LIST" }
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind }
override string toString() { result = "list taint source" }
}
class DictSource extends TaintSource {
DictSource() { this.(NameNode).getId() = "TAINTED_DICT" }
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind }
override string toString() { result = "dict taint source" }
}
class ExceptionInfoSource extends TaintSource {
ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" }
override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo }
override string toString() { result = "Exception info source" }
}
class ExternalFileObjectSource extends TaintSource {
ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" }
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject }
override string toString() { result = "Tainted file source" }
}

View File

@@ -0,0 +1 @@
| test.py:4 | ok | fstring | Fstring | externally controlled string |

View File

@@ -0,0 +1,33 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.web.HttpRequest
import semmle.python.security.strings.Untrusted
import Taint
from
Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res,
string taint_string
where
call.getLocation().getFile().getShortName() = "test.py" and
(
call.getFunc().(Name).getId() = "ensure_tainted" and
expected_taint = true
or
call.getFunc().(Name).getId() = "ensure_not_tainted" and
expected_taint = false
) and
arg = call.getAnArg() and
(
not exists(TaintedNode tainted | tainted.getAstNode() = arg) and
taint_string = "<NO TAINT>" and
has_taint = false
or
exists(TaintedNode tainted | tainted.getAstNode() = arg |
taint_string = tainted.getTaintKind().toString()
) and
has_taint = true
) and
if expected_taint = has_taint then test_res = "ok " else test_res = "fail"
// if expected_taint = has_taint then test_res = "✓" else test_res = "✕"
select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(),
taint_string

View File

@@ -0,0 +1,5 @@
def fstring():
tainted_string = TAINTED_STRING
ensure_tainted(
f"foo {tainted_string} bar"
)

View File

@@ -0,0 +1,290 @@
/**
* Provides a library for writing QL tests whose success or failure is based on expected results
* embedded in the test source code as comments, rather than a `.expected` file.
*
* To add this framework to a new language:
* - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class
* must support a `getContents` method that returns the contents of the given comment, _excluding_
* the comment indicator itself. It should also define `toString` and `getLocation` as usual.
*
* To create a new inline expectations test:
* - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the
* new class, bind `this` to a unique string (usually the name of the test).
* - Override the `hasActualResult()` predicate to produce the actual results of the query. For each
* result, specify a `Location`, a text description of the element for which the result was
* reported, a short string to serve as the tag to identify expected results for this test, and the
* expected value of the result.
* - Override `getARelevantTag()` to return the set of tags that can be produced by
* `hasActualResult()`. Often this is just a single tag.
*
* Example:
* ```ql
* class ConstantValueTest extends InlineExpectationsTest {
* ConstantValueTest() { this = "ConstantValueTest" }
*
* override string getARelevantTag() {
* // We only use one tag for this test.
* result = "const"
* }
*
* override predicate hasActualResult(
* Location location, string element, string tag, string valuesasas
* ) {
* exists(Expr e |
* tag = "const" and // The tag for this test.
* valuesasas = e.getValue() and // The expected value. Will only hold for constant expressions.
* location = e.getLocation() and // The location of the result to be reported.
* element = e.toString() // The display text for the result.
* )
* }
* }
* ```
*
* There is no need to write a `select` clause or query predicate. All of the differences between
* expected results and actual results will be reported in the `failures()` query predicate.
*
* To annotate the test source code with an expected result, place a comment on the
* same line as the expected result, with text of the following format as the body of the comment:
*
* `$tag=expected-value`
*
* Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is
* the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be
* omitted, in which case `expected-value` is treated as the empty string. Multiple expectations may
* be placed in the same comment, as long as each is prefixed by a `$`. Any actual result that
* appears on a line that does not contain a matching expected result comment will be reported with
* a message of the form "Unexpected result: tag=value". Any expected result comment for which there
* is no matching actual result will be reported with a message of the form
* "Missing result: tag=expected-value".
*
* Example:
* ```cpp
* int i = x + 5; // $const=5
* int j = y + (7 - 3) // $const=7 $const=3 $const=4 // The result of the subtraction is a constant.
* ```
*
* For tests that contain known false positives and false negatives, it is possible to further
* annotate that a particular expected result is known to be a false positive, or that a particular
* missing result is known to be a false negative:
*
* `$f+:tag=expected-value` // False positive
* `$f-:tag=expected-value` // False negative
*
* A false positive expectation is treated as any other expected result, except that if there is no
* matching actual result, the message will be of the form "Fixed false positive: tag=value". A
* false negative expectation is treated as if there were no expected result, except that if a
* matching expected result is found, the message will be of the form
* "Fixed false negative: tag=value".
*
* If the same result value is expected for two or more tags on the same line, there is a shorthand
* notation available:
*
* `$tag1,tag2=expected-value`
*
* is equivalent to:
*
* `$tag1=expected-value $tag2=expected-value`
*/
private import InlineExpectationsTestPrivate
/**
* Base class for tests with inline expectations. The test extends this class to provide the actual
* results of the query, which are then compared with the expected results in comments to produce a
* list of failure messages that point out where the actual results differ from the expected
* results.
*/
abstract class InlineExpectationsTest extends string {
bindingset[this]
InlineExpectationsTest() { any() }
/**
* Returns all tags that can be generated by this test. Most tests will only ever produce a single
* tag. Any expected result comments for a tag that is not returned by the `getARelevantTag()`
* predicate for an active test will be ignored. This makes it possible to write multiple tests in
* different `.ql` files that all query the same source code.
*/
abstract string getARelevantTag();
/**
* Returns the actual results of the query that is being tested. Each result consist of the
* following values:
* - `location` - The source code location of the result. Any expected result comment must appear
* on the start line of this location.
* - `element` - Display text for the element on which the result is reported.
* - `tag` - The tag that marks this result as coming from this test. This must be one of the tags
* returned by `getARelevantTag()`.
* - `value` - The value of the result, which will be matched against the value associated with
* `tag` in any expected result comment on that line.
*/
abstract predicate hasActualResult(Location location, string element, string tag, string value);
final predicate hasFailureMessage(FailureLocatable element, string message) {
exists(ActualResult actualResult |
actualResult.getTest() = this and
element = actualResult and
(
exists(FalseNegativeExpectation falseNegative |
falseNegative.matchesActualResult(actualResult) and
message = "Fixed false negative:" + falseNegative.getExpectationText()
)
or
not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and
message = "Unexpected result: " + actualResult.getExpectationText()
)
)
or
exists(ValidExpectation expectation |
not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and
expectation.getTag() = getARelevantTag() and
element = expectation and
(
expectation instanceof GoodExpectation and
message = "Missing result:" + expectation.getExpectationText()
or
expectation instanceof FalsePositiveExpectation and
message = "Fixed false positive:" + expectation.getExpectationText()
)
)
or
exists(InvalidExpectation expectation |
element = expectation and
message = "Invalid expectation syntax: " + expectation.getExpectation()
)
}
}
/**
* RegEx pattern to match a comment containing one or more expected results. The comment must have
* `$` as its first non-whitespace character. Any subsequent character
* is treated as part of the expected results, except that the comment may contain a `//` sequence
* to treat the remainder of the line as a regular (non-interpreted) comment.
*/
private string expectationCommentPattern() { result = "\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" }
/**
* RegEx pattern to match a single expected result, not including the leading `$`. It starts with an
* optional `f+:` or `f-:`, followed by one or more comma-separated tags containing only letters,
* `-`, and `_`, optionally followed by `=` and the expected value.
*/
private string expectationPattern() {
result = "(?:(f(?:\\+|-)):)?((?:[A-Za-z-_]+)(?:\\s*,\\s*[A-Za-z-_]+)*)(?:=(.*))?"
}
private string getAnExpectation(LineComment comment) {
result = comment.getContents().regexpCapture(expectationCommentPattern(), 1).splitAt("$").trim() and
result != ""
}
private newtype TFailureLocatable =
TActualResult(
InlineExpectationsTest test, Location location, string element, string tag, string value
) {
test.hasActualResult(location, element, tag, value)
} or
TValidExpectation(LineComment comment, string tag, string value, string knownFailure) {
exists(string expectation |
expectation = getAnExpectation(comment) and
expectation.regexpMatch(expectationPattern()) and
tag = expectation.regexpCapture(expectationPattern(), 2).splitAt(",").trim() and
(
if exists(expectation.regexpCapture(expectationPattern(), 3))
then value = expectation.regexpCapture(expectationPattern(), 3)
else value = ""
) and
(
if exists(expectation.regexpCapture(expectationPattern(), 1))
then knownFailure = expectation.regexpCapture(expectationPattern(), 1)
else knownFailure = ""
)
)
} or
TInvalidExpectation(LineComment comment, string expectation) {
expectation = getAnExpectation(comment) and
not expectation.regexpMatch(expectationPattern())
}
class FailureLocatable extends TFailureLocatable {
string toString() { none() }
Location getLocation() { none() }
final string getExpectationText() { result = getTag() + "=" + getValue() }
string getTag() { none() }
string getValue() { none() }
}
class ActualResult extends FailureLocatable, TActualResult {
InlineExpectationsTest test;
Location location;
string element;
string tag;
string value;
ActualResult() { this = TActualResult(test, location, element, tag, value) }
override string toString() { result = element }
override Location getLocation() { result = location }
InlineExpectationsTest getTest() { result = test }
override string getTag() { result = tag }
override string getValue() { result = value }
}
abstract private class Expectation extends FailureLocatable {
LineComment comment;
override string toString() { result = comment.toString() }
override Location getLocation() { result = comment.getLocation() }
}
private class ValidExpectation extends Expectation, TValidExpectation {
string tag;
string value;
string knownFailure;
ValidExpectation() { this = TValidExpectation(comment, tag, value, knownFailure) }
override string getTag() { result = tag }
override string getValue() { result = value }
string getKnownFailure() { result = knownFailure }
predicate matchesActualResult(ActualResult actualResult) {
getLocation().getStartLine() = actualResult.getLocation().getStartLine() and
getLocation().getFile() = actualResult.getLocation().getFile() and
getTag() = actualResult.getTag() and
getValue() = actualResult.getValue()
}
}
class GoodExpectation extends ValidExpectation {
GoodExpectation() { getKnownFailure() = "" }
}
class FalsePositiveExpectation extends ValidExpectation {
FalsePositiveExpectation() { getKnownFailure() = "f+" }
}
class FalseNegativeExpectation extends ValidExpectation {
FalseNegativeExpectation() { getKnownFailure() = "f-" }
}
class InvalidExpectation extends Expectation, TInvalidExpectation {
string expectation;
InvalidExpectation() { this = TInvalidExpectation(comment, expectation) }
string getExpectation() { result = expectation }
}
query predicate failures(FailureLocatable element, string message) {
exists(InlineExpectationsTest test | test.hasFailureMessage(element, message))
}

View File

@@ -0,0 +1,7 @@
import python
/**
* A class representing line comments in Python. As this is the only form of comment Python
* permits, we simply reuse the `Comment` class.
*/
class LineComment = Comment;

View File

@@ -0,0 +1,11 @@
import airspeed
from flask import Flask, request
app = Flask(__name__)
@route('/other')
def a():
template = request.args.get('template')
return airspeed.Template(template)

View File

@@ -0,0 +1,20 @@
from bottle import Bottle, route, request, redirect, response, SimpleTemplate
from bottle import template as temp
app = Bottle()
@route('/other')
def a():
template = request.query.template
tpl = SimpleTemplate(template)
tpl.render(name='World')
return tmp
@route('/other2')
def b():
template = request.query.template
temp(template, name='World')
return tmp

View File

@@ -0,0 +1,10 @@
from chameleon import PageTemplate
from django.urls import path
from django.http import HttpResponse
def chameleon(request):
template = request.GET['template']
tmpl = PageTemplate(template)
return HttpResponse(tmpl)

View File

@@ -0,0 +1,24 @@
from flask import Flask, request
import chevron
app = Flask(__name__)
@route('/other')
def a():
template = request.args.get('template')
return chevron.render(template, {"key": "value"})
@route('/other2')
def b():
template = request.args.get('template')
args = {
'template': template,
'data': {
'key': 'value'
}
}
return chevron.render(**args)

View File

@@ -0,0 +1,41 @@
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def dj(request):
# Load the template
template = request.GET['template']
t = Template(template)
ctx = Context(locals())
html = t.render(ctx)
return HttpResponse(html)
def djEngine(request):
# Load the template
template = request.GET['template']
django_engine = engines['django']
t = django_engine.from_string(template)
ctx = Context(locals())
html = t.render(ctx)
return HttpResponse(html)
def djEngineJinja(request):
# Load the template
template = request.GET['template']
django_engine = engines['jinja']
t = django_engine.from_string(template)
ctx = Context(locals())
html = t.render(ctx)
return HttpResponse(html)
urlpatterns = [
path('', dj),
path('', djEngine),
path('', djEngineJinja),
]

View File

@@ -0,0 +1,22 @@
from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def home():
from flask import render_template_string
if request.args.get('template'):
return render_template_string(request.args.get('template'))
@app.route("/a")
def a():
import flask
return flask.render_template_string(request.args.get('template'))
if __name__ == "__main__":
app.run(debug=True)

View File

@@ -0,0 +1,18 @@
from django.urls import path
from django.http import HttpResponse
from genshi.template import TextTemplate,MarkupTemplate
def genshi1():
template = request.GET['template']
tmpl = MarkupTemplate(template)
return HttpResponse(tmpl)
def genshi2():
template = request.GET['template']
tmpl = TextTemplate(template)
return HttpResponse(tmpl)
urlpatterns = [
path('', genshi1),
path('', genshi2)
]

View File

@@ -0,0 +1,30 @@
from django.urls import path
from django.http import HttpResponse
from jinja2 import Template as Jinja2_Template
from jinja2 import Environment, DictLoader, escape
def a(request):
# Load the template
template = request.GET['template']
t = Jinja2_Template(template)
name = request.GET['name']
# Render the template with the context data
html = t.render(name=escape(name))
return HttpResponse(html)
def b(request):
import jinja2
# Load the template
template = request.GET['template']
t = jinja2.from_string(template)
name = request.GET['name']
# Render the template with the context data
html = t.render(name=escape(name))
return HttpResponse(html)
urlpatterns = [
path('a', a),
path('b', b)
]

View File

@@ -0,0 +1,15 @@
from django.urls import path
from django.http import HttpResponse
from mako.template import Template
def mako(request):
# Load the template
template = request.GET['template']
mytemplate = Template(template)
return HttpResponse(mytemplate)
urlpatterns = [
path('', mako)
]

View File

@@ -0,0 +1,12 @@
from django.urls import path
from django.http import HttpResponse
from trender import TRender
def trender(request):
template = request.GET['template']
compiled = TRender(template)
return HttpResponse(compiled)
urlpatterns = [
path('', trender)
]

View File

@@ -0,0 +1,60 @@
edges
| AirspeedSsti.py:10:16:10:27 | dict of externally controlled string | AirspeedSsti.py:10:16:10:43 | externally controlled string |
| AirspeedSsti.py:10:16:10:27 | dict of externally controlled string | AirspeedSsti.py:10:16:10:43 | externally controlled string |
| AirspeedSsti.py:10:16:10:43 | externally controlled string | AirspeedSsti.py:11:30:11:37 | externally controlled string |
| AirspeedSsti.py:10:16:10:43 | externally controlled string | AirspeedSsti.py:11:30:11:37 | externally controlled string |
| ChevronSsti.py:10:16:10:27 | dict of externally controlled string | ChevronSsti.py:10:16:10:43 | externally controlled string |
| ChevronSsti.py:10:16:10:27 | dict of externally controlled string | ChevronSsti.py:10:16:10:43 | externally controlled string |
| ChevronSsti.py:10:16:10:43 | externally controlled string | ChevronSsti.py:11:27:11:34 | externally controlled string |
| ChevronSsti.py:10:16:10:43 | externally controlled string | ChevronSsti.py:11:27:11:34 | externally controlled string |
| DjangoTemplates.py:6:8:6:14 | django.request.HttpRequest | DjangoTemplates.py:8:16:8:22 | django.request.HttpRequest |
| DjangoTemplates.py:6:8:6:14 | django.request.HttpRequest | DjangoTemplates.py:8:16:8:22 | django.request.HttpRequest |
| DjangoTemplates.py:8:16:8:22 | django.request.HttpRequest | DjangoTemplates.py:8:16:8:26 | django.http.request.QueryDict |
| DjangoTemplates.py:8:16:8:22 | django.request.HttpRequest | DjangoTemplates.py:8:16:8:26 | django.http.request.QueryDict |
| DjangoTemplates.py:8:16:8:26 | django.http.request.QueryDict | DjangoTemplates.py:8:16:8:38 | externally controlled string |
| DjangoTemplates.py:8:16:8:26 | django.http.request.QueryDict | DjangoTemplates.py:8:16:8:38 | externally controlled string |
| DjangoTemplates.py:8:16:8:38 | externally controlled string | DjangoTemplates.py:9:18:9:25 | externally controlled string |
| DjangoTemplates.py:8:16:8:38 | externally controlled string | DjangoTemplates.py:9:18:9:25 | externally controlled string |
| FlaskTemplate.py:17:41:17:52 | dict of externally controlled string | FlaskTemplate.py:17:41:17:68 | externally controlled string |
| FlaskTemplate.py:17:41:17:52 | dict of externally controlled string | FlaskTemplate.py:17:41:17:68 | externally controlled string |
| JinjaSsti.py:7:7:7:13 | django.request.HttpRequest | JinjaSsti.py:9:16:9:22 | django.request.HttpRequest |
| JinjaSsti.py:7:7:7:13 | django.request.HttpRequest | JinjaSsti.py:9:16:9:22 | django.request.HttpRequest |
| JinjaSsti.py:9:16:9:22 | django.request.HttpRequest | JinjaSsti.py:9:16:9:26 | django.http.request.QueryDict |
| JinjaSsti.py:9:16:9:22 | django.request.HttpRequest | JinjaSsti.py:9:16:9:26 | django.http.request.QueryDict |
| JinjaSsti.py:9:16:9:26 | django.http.request.QueryDict | JinjaSsti.py:9:16:9:38 | externally controlled string |
| JinjaSsti.py:9:16:9:26 | django.http.request.QueryDict | JinjaSsti.py:9:16:9:38 | externally controlled string |
| JinjaSsti.py:9:16:9:38 | externally controlled string | JinjaSsti.py:10:25:10:32 | externally controlled string |
| JinjaSsti.py:9:16:9:38 | externally controlled string | JinjaSsti.py:10:25:10:32 | externally controlled string |
| JinjaSsti.py:16:7:16:13 | django.request.HttpRequest | JinjaSsti.py:19:16:19:22 | django.request.HttpRequest |
| JinjaSsti.py:16:7:16:13 | django.request.HttpRequest | JinjaSsti.py:19:16:19:22 | django.request.HttpRequest |
| JinjaSsti.py:19:16:19:22 | django.request.HttpRequest | JinjaSsti.py:19:16:19:26 | django.http.request.QueryDict |
| JinjaSsti.py:19:16:19:22 | django.request.HttpRequest | JinjaSsti.py:19:16:19:26 | django.http.request.QueryDict |
| JinjaSsti.py:19:16:19:26 | django.http.request.QueryDict | JinjaSsti.py:19:16:19:38 | externally controlled string |
| JinjaSsti.py:19:16:19:26 | django.http.request.QueryDict | JinjaSsti.py:19:16:19:38 | externally controlled string |
| JinjaSsti.py:19:16:19:38 | externally controlled string | JinjaSsti.py:20:28:20:35 | externally controlled string |
| JinjaSsti.py:19:16:19:38 | externally controlled string | JinjaSsti.py:20:28:20:35 | externally controlled string |
| MakoSsti.py:6:10:6:16 | django.request.HttpRequest | MakoSsti.py:8:16:8:22 | django.request.HttpRequest |
| MakoSsti.py:6:10:6:16 | django.request.HttpRequest | MakoSsti.py:8:16:8:22 | django.request.HttpRequest |
| MakoSsti.py:8:16:8:22 | django.request.HttpRequest | MakoSsti.py:8:16:8:26 | django.http.request.QueryDict |
| MakoSsti.py:8:16:8:22 | django.request.HttpRequest | MakoSsti.py:8:16:8:26 | django.http.request.QueryDict |
| MakoSsti.py:8:16:8:26 | django.http.request.QueryDict | MakoSsti.py:8:16:8:38 | externally controlled string |
| MakoSsti.py:8:16:8:26 | django.http.request.QueryDict | MakoSsti.py:8:16:8:38 | externally controlled string |
| MakoSsti.py:8:16:8:38 | externally controlled string | MakoSsti.py:9:27:9:34 | externally controlled string |
| MakoSsti.py:8:16:8:38 | externally controlled string | MakoSsti.py:9:27:9:34 | externally controlled string |
| TRender.py:5:13:5:19 | django.request.HttpRequest | TRender.py:6:16:6:22 | django.request.HttpRequest |
| TRender.py:5:13:5:19 | django.request.HttpRequest | TRender.py:6:16:6:22 | django.request.HttpRequest |
| TRender.py:6:16:6:22 | django.request.HttpRequest | TRender.py:6:16:6:26 | django.http.request.QueryDict |
| TRender.py:6:16:6:22 | django.request.HttpRequest | TRender.py:6:16:6:26 | django.http.request.QueryDict |
| TRender.py:6:16:6:26 | django.http.request.QueryDict | TRender.py:6:16:6:38 | externally controlled string |
| TRender.py:6:16:6:26 | django.http.request.QueryDict | TRender.py:6:16:6:38 | externally controlled string |
| TRender.py:6:16:6:38 | externally controlled string | TRender.py:7:24:7:31 | externally controlled string |
| TRender.py:6:16:6:38 | externally controlled string | TRender.py:7:24:7:31 | externally controlled string |
#select
| AirspeedSsti.py:11:30:11:37 | template | AirspeedSsti.py:10:16:10:27 | dict of externally controlled string | AirspeedSsti.py:11:30:11:37 | externally controlled string | This Template depends on $@. | AirspeedSsti.py:10:16:10:27 | Attribute | a user-provided value |
| ChevronSsti.py:11:27:11:34 | template | ChevronSsti.py:10:16:10:27 | dict of externally controlled string | ChevronSsti.py:11:27:11:34 | externally controlled string | This Template depends on $@. | ChevronSsti.py:10:16:10:27 | Attribute | a user-provided value |
| DjangoTemplates.py:9:18:9:25 | template | DjangoTemplates.py:6:8:6:14 | django.request.HttpRequest | DjangoTemplates.py:9:18:9:25 | externally controlled string | This Template depends on $@. | DjangoTemplates.py:6:8:6:14 | request | a user-provided value |
| FlaskTemplate.py:17:41:17:68 | Attribute() | FlaskTemplate.py:17:41:17:52 | dict of externally controlled string | FlaskTemplate.py:17:41:17:68 | externally controlled string | This Template depends on $@. | FlaskTemplate.py:17:41:17:52 | Attribute | a user-provided value |
| JinjaSsti.py:10:25:10:32 | template | JinjaSsti.py:7:7:7:13 | django.request.HttpRequest | JinjaSsti.py:10:25:10:32 | externally controlled string | This Template depends on $@. | JinjaSsti.py:7:7:7:13 | request | a user-provided value |
| JinjaSsti.py:20:28:20:35 | template | JinjaSsti.py:16:7:16:13 | django.request.HttpRequest | JinjaSsti.py:20:28:20:35 | externally controlled string | This Template depends on $@. | JinjaSsti.py:16:7:16:13 | request | a user-provided value |
| MakoSsti.py:9:27:9:34 | template | MakoSsti.py:6:10:6:16 | django.request.HttpRequest | MakoSsti.py:9:27:9:34 | externally controlled string | This Template depends on $@. | MakoSsti.py:6:10:6:16 | request | a user-provided value |
| TRender.py:7:24:7:31 | template | TRender.py:5:13:5:19 | django.request.HttpRequest | TRender.py:7:24:7:31 | externally controlled string | This Template depends on $@. | TRender.py:5:13:5:19 | request | a user-provided value |

View File

@@ -0,0 +1 @@
experimental/CWE-074/TemplateInjection.ql

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=3 -p ../../query-tests/Security/lib/

View File

@@ -1,4 +1,4 @@
import callGraphConfig
import experimental.dataflow.callGraphConfig
from DataFlow::Node source, DataFlow::Node sink
where exists(CallGraphConfig cfg | cfg.hasFlow(source, sink))

View File

@@ -1,4 +1,4 @@
import callGraphConfig
import experimental.dataflow.callGraphConfig
from DataFlow::Node sink
where exists(CallGraphConfig cfg | cfg.isSink(sink))

View File

@@ -1,4 +1,4 @@
import callGraphConfig
import experimental.dataflow.callGraphConfig
from DataFlow::Node source
where exists(CallGraphConfig cfg | cfg.isSource(source))

View File

@@ -1,23 +1,16 @@
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:1:7:1 | GSSA Variable b |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:1:7:1 | GSSA Variable b |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:1:7:1 | GSSA Variable b |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:0:0:0:0 | Exit node for Module test |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:1:7:1 | GSSA Variable b |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:1:7:1 | GSSA Variable b |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:1:19:1:19 | SSA variable x | test.py:0:0:0:0 | Exit node for Module test |
| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z |
@@ -25,37 +18,26 @@
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:7:1:7:1 | GSSA Variable b |
| test.py:1:19:1:19 | SSA variable x | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:2:3:2:3 | SSA variable y | test.py:0:0:0:0 | Exit node for Module test |
| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:7:1:7:1 | GSSA Variable b |
| test.py:2:3:2:3 | SSA variable y | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:0:0:0:0 | Exit node for Module test |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:7:1:7:1 | GSSA Variable b |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:3:3:3:3 | SSA variable z | test.py:0:0:0:0 | Exit node for Module test |
| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:7:1:7:1 | GSSA Variable b |
| test.py:3:3:3:3 | SSA variable z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:0:0:0:0 | Exit node for Module test |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:7:1:7:1 | GSSA Variable b |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:0:0:0:0 | Exit node for Module test |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:1:7:1 | GSSA Variable b |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:1:6:1 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test |
| test.py:6:1:6:1 | GSSA Variable a | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:6:1:6:1 | GSSA Variable a | test.py:1:19:1:19 | SSA variable x |
| test.py:6:1:6:1 | GSSA Variable a | test.py:2:3:2:3 | SSA variable y |
| test.py:6:1:6:1 | GSSA Variable a | test.py:2:7:2:7 | ControlFlowNode for x |
@@ -66,8 +48,6 @@
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a |
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | Exit node for Module test |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:1:19:1:19 | SSA variable x |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:2:3:2:3 | SSA variable y |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:2:7:2:7 | ControlFlowNode for x |
@@ -79,12 +59,7 @@
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | GSSA Variable a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b |
| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:3:2:3 | SSA variable y |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:7:2:7 | ControlFlowNode for x |

View File

@@ -1,25 +1,15 @@
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:0:0:0:0 | GSSA Variable b | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:0:0:0:0 | Exit node for Module test |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
@@ -40,10 +30,6 @@
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
@@ -56,10 +42,6 @@
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
@@ -76,18 +58,10 @@
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
@@ -106,12 +80,8 @@
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a |
| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:1:7:1 | GSSA Variable b | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b |
| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:5:7:20 | GSSA Variable a | test.py:0:0:0:0 | Exit node for Module test |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | SSA variable x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |

View File

@@ -1,17 +1,11 @@
| test.py:0:0:0:0 | Entry node for Module test | test.py:0:0:0:0 | Entry node for Module test |
| test.py:0:0:0:0 | Exit node for Module test | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b |
| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | SSA variable $ |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:1:1:21 | Entry node for Function obfuscated_id | test.py:1:1:1:21 | Entry node for Function obfuscated_id |
| test.py:1:1:1:21 | Exit node for Function obfuscated_id | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | ControlFlowNode for x |
| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:1:19:1:19 | SSA variable x |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
@@ -19,26 +13,21 @@
| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | ControlFlowNode for y | test.py:2:3:2:3 | ControlFlowNode for y |
| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:2:3:2:3 | SSA variable y |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | ControlFlowNode for z | test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:3:3:3:3 | SSA variable z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:4:3:4:10 | ControlFlowNode for Return | test.py:4:3:4:10 | ControlFlowNode for Return |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:1:6:1 | GSSA Variable a | test.py:6:1:6:1 | GSSA Variable a |

View File

@@ -1,9 +1,5 @@
| test.py:0:0:0:0 | SSA variable $ | test.py:0:0:0:0 | Exit node for Module test |
| test.py:1:19:1:19 | SSA variable x | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:2:3:2:3 | SSA variable y | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:3:3:3:3 | SSA variable z | test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |

View File

@@ -1,12 +1,8 @@
| test.py:0:0:0:0 | Entry node for Module test |
| test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |
| test.py:0:0:0:0 | SSA variable $ |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:1:1:21 | Entry node for Function obfuscated_id |
| test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x |
@@ -17,7 +13,6 @@
| test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:4:3:4:10 | ControlFlowNode for Return |
| test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:1:6:1 | GSSA Variable a |

View File

@@ -1,12 +1,8 @@
| test.py:0:0:0:0 | Entry node for Module test |
| test.py:0:0:0:0 | Exit node for Module test |
| test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |
| test.py:0:0:0:0 | SSA variable $ |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:1:1:21 | Entry node for Function obfuscated_id |
| test.py:1:1:1:21 | Exit node for Function obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x |
@@ -17,7 +13,6 @@
| test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:4:3:4:10 | ControlFlowNode for Return |
| test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:1:6:1 | GSSA Variable a |

View File

@@ -1,12 +1,10 @@
uniqueEnclosingCallable
| test.py:0:0:0:0 | Exit node for Module test | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable __name__ | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable __package__ | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test23 | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test24 | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. |
| test.py:0:0:0:0 | SSA variable $ | Node should have one enclosing callable but has 0. |
| test.py:6:1:6:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:6:5:6:9 | GSSA Variable test1 | Node should have one enclosing callable but has 0. |
| test.py:9:1:9:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
@@ -88,6 +86,8 @@ uniquePostUpdate
postIsInSameCallable
reverseRead
storeIsPostUpdate
| test.py:152:9:152:16 | ControlFlowNode for List | Store targets should be PostUpdateNodes. |
| test.py:153:9:153:24 | ControlFlowNode for Dict | Store targets should be PostUpdateNodes. |
argHasPostUpdate
| test.py:25:10:25:10 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. |
| test.py:29:10:29:10 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. |
@@ -101,3 +101,5 @@ argHasPostUpdate
| test.py:74:17:74:17 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. |
| test.py:81:13:81:13 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. |
| test.py:86:13:86:13 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. |
| test.py:158:15:158:15 | ControlFlowNode for l | ArgumentNode is missing PostUpdateNode. |
| test.py:159:15:159:15 | ControlFlowNode for d | ArgumentNode is missing PostUpdateNode. |

View File

@@ -0,0 +1,16 @@
| classes.py:620:5:620:16 | SSA variable with_getitem | classes.py:614:15:614:18 | ControlFlowNode for self |
| classes.py:637:5:637:16 | SSA variable with_setitem | classes.py:632:15:632:18 | ControlFlowNode for self |
| classes.py:654:5:654:16 | SSA variable with_delitem | classes.py:649:15:649:18 | ControlFlowNode for self |
| classes.py:735:5:735:12 | SSA variable with_add | classes.py:729:15:729:18 | ControlFlowNode for self |
| classes.py:752:5:752:12 | SSA variable with_sub | classes.py:746:15:746:18 | ControlFlowNode for self |
| classes.py:769:5:769:12 | SSA variable with_mul | classes.py:763:15:763:18 | ControlFlowNode for self |
| classes.py:786:5:786:15 | SSA variable with_matmul | classes.py:780:15:780:18 | ControlFlowNode for self |
| classes.py:803:5:803:16 | SSA variable with_truediv | classes.py:797:15:797:18 | ControlFlowNode for self |
| classes.py:820:5:820:17 | SSA variable with_floordiv | classes.py:814:15:814:18 | ControlFlowNode for self |
| classes.py:837:5:837:12 | SSA variable with_mod | classes.py:831:15:831:18 | ControlFlowNode for self |
| classes.py:877:5:877:12 | SSA variable with_pow | classes.py:865:15:865:18 | ControlFlowNode for self |
| classes.py:894:5:894:15 | SSA variable with_lshift | classes.py:888:15:888:18 | ControlFlowNode for self |
| classes.py:911:5:911:15 | SSA variable with_rshift | classes.py:905:15:905:18 | ControlFlowNode for self |
| classes.py:928:5:928:12 | SSA variable with_and | classes.py:922:15:922:18 | ControlFlowNode for self |
| classes.py:945:5:945:12 | SSA variable with_xor | classes.py:939:15:939:18 | ControlFlowNode for self |
| classes.py:962:5:962:11 | SSA variable with_or | classes.py:956:15:956:18 | ControlFlowNode for self |

View File

@@ -0,0 +1,31 @@
import experimental.dataflow.DataFlow
/**
* A configuration to check routing of arguments through magic methods.
*/
class ArgumentRoutingConfig extends DataFlow::Configuration {
ArgumentRoutingConfig() { this = "ArgumentRoutingConfig" }
override predicate isSource(DataFlow::Node node) {
exists(AssignmentDefinition def, DataFlow::DataFlowCall call |
def.getVariable() = node.(DataFlow::EssaNode).getVar() and
def.getValue() = call.getNode() and
call.getCallable().getName().matches("With\\_%")
) and
node.(DataFlow::EssaNode).getVar().getName().matches("with\\_%")
}
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "SINK1" and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
}
from DataFlow::Node source, DataFlow::Node sink
where
source.getLocation().getFile().getBaseName() = "classes.py" and
sink.getLocation().getFile().getBaseName() = "classes.py" and
exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink))
select source, sink

View File

@@ -0,0 +1,16 @@
| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:613:15:613:17 | ControlFlowNode for key |
| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:631:15:631:17 | ControlFlowNode for key |
| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:648:15:648:17 | ControlFlowNode for key |
| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:728:15:728:19 | ControlFlowNode for other |
| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:745:15:745:19 | ControlFlowNode for other |
| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:762:15:762:19 | ControlFlowNode for other |
| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:779:15:779:19 | ControlFlowNode for other |
| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:796:15:796:19 | ControlFlowNode for other |
| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:813:15:813:19 | ControlFlowNode for other |
| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:830:15:830:19 | ControlFlowNode for other |
| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:864:15:864:19 | ControlFlowNode for other |
| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:887:15:887:19 | ControlFlowNode for other |
| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:904:15:904:19 | ControlFlowNode for other |
| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:921:15:921:19 | ControlFlowNode for other |
| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:938:15:938:19 | ControlFlowNode for other |
| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:955:15:955:19 | ControlFlowNode for other |

View File

@@ -0,0 +1,26 @@
import experimental.dataflow.DataFlow
/**
* A configuration to check routing of arguments through magic methods.
*/
class ArgumentRoutingConfig extends DataFlow::Configuration {
ArgumentRoutingConfig() { this = "ArgumentRoutingConfig" }
override predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg2"
}
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "SINK2" and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
}
from DataFlow::Node source, DataFlow::Node sink
where
source.getLocation().getFile().getBaseName() = "classes.py" and
sink.getLocation().getFile().getBaseName() = "classes.py" and
exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink))
select source, sink

View File

@@ -0,0 +1 @@
| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:630:15:630:19 | ControlFlowNode for value |

View File

@@ -0,0 +1,26 @@
import experimental.dataflow.DataFlow
/**
* A configuration to check routing of arguments through magic methods.
*/
class ArgumentRoutingConfig extends DataFlow::Configuration {
ArgumentRoutingConfig() { this = "ArgumentRoutingConfig" }
override predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg3"
}
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "SINK3" and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
}
from DataFlow::Node source, DataFlow::Node sink
where
source.getLocation().getFile().getBaseName() = "classes.py" and
sink.getLocation().getFile().getBaseName() = "classes.py" and
exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink))
select source, sink

View File

@@ -0,0 +1,26 @@
import experimental.dataflow.DataFlow
/**
* A configuration to check routing of arguments through magic methods.
*/
class ArgumentRoutingConfig extends DataFlow::Configuration {
ArgumentRoutingConfig() { this = "ArgumentRoutingConfig" }
override predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg4"
}
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "SINK4" and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
}
from DataFlow::Node source, DataFlow::Node sink
where
source.getLocation().getFile().getBaseName() = "classes.py" and
sink.getLocation().getFile().getBaseName() = "classes.py" and
exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink))
select source, sink

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
| classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() |
| classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() |
| classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() |
| classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() |
| classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() |
| classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self |
| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key |
| classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self |
| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key |
| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value |
| classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self |
| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key |
| classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() |
| classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self |
| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other |
| classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self |
| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other |
| classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self |
| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other |
| classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self |
| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other |
| classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self |
| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other |
| classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self |
| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other |
| classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self |
| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other |
| classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self |
| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other |
| classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self |
| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other |
| classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self |
| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other |
| classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self |
| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other |
| classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self |
| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other |
| classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self |
| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other |

View File

@@ -0,0 +1,36 @@
import experimental.dataflow.DataFlow
/**
* A configuration to find the call graph edges.
*/
class CallGraphConfig extends DataFlow::Configuration {
CallGraphConfig() { this = "CallGraphConfig" }
override predicate isSource(DataFlow::Node node) {
node instanceof DataFlow::ReturnNode
or
// These sources should allow for the non-standard call syntax
node instanceof DataFlow::ArgumentNode
}
override predicate isSink(DataFlow::Node node) {
node instanceof DataFlow::OutNode
or
node instanceof DataFlow::ParameterNode and
// exclude parameters to the SINK-functions
not exists(DataFlow::DataFlowCallable c |
node.(DataFlow::ParameterNode).isParameterOf(c, _) and
c.getName().matches("SINK_")
)
}
}
from DataFlow::Node source, DataFlow::Node sink
where
source.getLocation().getFile().getBaseName() = "classes.py" and
sink.getLocation().getFile().getBaseName() = "classes.py" and
exists(CallGraphConfig cfg | cfg.hasFlow(source, sink))
select source, sink
// Ideally, we would just have 1-step paths either from argument to parameter
// or from return to call. This gives a bit more, so should be rewritten.
// We should also consider splitting this into two, one for each direction.

View File

@@ -1,6 +1,303 @@
| test.py:20:9:20:14 | ControlFlowNode for SOURCE | test.py:21:10:21:10 | ControlFlowNode for x |
| test.py:25:9:25:16 | ControlFlowNode for Str | test.py:26:10:26:10 | ControlFlowNode for x |
| test.py:29:9:29:17 | ControlFlowNode for Str | test.py:30:10:30:10 | ControlFlowNode for x |
| test.py:33:9:33:10 | ControlFlowNode for IntegerLiteral | test.py:34:10:34:10 | ControlFlowNode for x |
| test.py:37:9:37:12 | ControlFlowNode for FloatLiteral | test.py:38:10:38:10 | ControlFlowNode for x |
| test.py:46:10:46:15 | ControlFlowNode for SOURCE | test.py:47:10:47:10 | ControlFlowNode for x |
edges
| datamodel.py:13:1:13:6 | GSSA Variable SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() |
| datamodel.py:13:1:13:6 | GSSA Variable SOURCE | datamodel.py:38:6:38:17 | GSSA Variable SOURCE |
| datamodel.py:13:1:13:6 | GSSA Variable SOURCE | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE |
| datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:13:1:13:6 | GSSA Variable SOURCE |
| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | datamodel.py:65:5:65:7 | ControlFlowNode for C() |
| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() |
| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | datamodel.py:71:6:71:24 | GSSA Variable SOURCE |
| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE |
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() |
| datamodel.py:65:1:65:1 | GSSA Variable c | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() |
| datamodel.py:65:1:65:1 | GSSA Variable c | datamodel.py:71:6:71:24 | GSSA Variable c |
| datamodel.py:65:5:65:7 | ControlFlowNode for C() | datamodel.py:65:1:65:1 | GSSA Variable c |
| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() |
| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE |
| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() |
| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() |
| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:80:6:80:26 | GSSA Variable SOURCE |
| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE |
| datamodel.py:71:6:71:24 | GSSA Variable c | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() |
| datamodel.py:71:6:71:24 | GSSA Variable c | datamodel.py:72:6:72:27 | GSSA Variable c |
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() |
| datamodel.py:72:6:72:27 | GSSA Variable c | datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() |
| datamodel.py:72:6:72:27 | GSSA Variable c | datamodel.py:73:6:73:27 | GSSA Variable c |
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() |
| datamodel.py:73:6:73:27 | GSSA Variable c | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() |
| datamodel.py:73:6:73:27 | GSSA Variable c | datamodel.py:80:6:80:26 | GSSA Variable c |
| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() |
| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | datamodel.py:81:6:81:26 | GSSA Variable SOURCE |
| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE |
| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() |
| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() |
| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:92:8:92:21 | ControlFlowNode for gen() |
| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() |
| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() |
| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:96:9:96:24 | GSSA Variable c |
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() |
| datamodel.py:81:6:81:26 | GSSA Variable SOURCE | datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() |
| datamodel.py:81:6:81:26 | GSSA Variable SOURCE | datamodel.py:92:8:92:21 | ControlFlowNode for gen() |
| datamodel.py:81:6:81:26 | GSSA Variable SOURCE | datamodel.py:92:8:92:21 | GSSA Variable SOURCE |
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() |
| datamodel.py:92:1:92:4 | GSSA Variable iter | datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() |
| datamodel.py:92:1:92:4 | GSSA Variable iter | datamodel.py:93:6:93:20 | GSSA Variable iter |
| datamodel.py:92:8:92:21 | ControlFlowNode for gen() | datamodel.py:92:1:92:4 | GSSA Variable iter |
| datamodel.py:92:8:92:21 | GSSA Variable SOURCE | datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() |
| datamodel.py:92:8:92:21 | GSSA Variable SOURCE | datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() |
| datamodel.py:92:8:92:21 | GSSA Variable SOURCE | datamodel.py:96:9:96:24 | GSSA Variable SOURCE |
| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() |
| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() |
| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() |
| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() |
| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() |
| datamodel.py:96:1:96:5 | GSSA Variable oiter | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() |
| datamodel.py:96:1:96:5 | GSSA Variable oiter | datamodel.py:97:6:97:21 | GSSA Variable oiter |
| datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() | datamodel.py:96:1:96:5 | GSSA Variable oiter |
| datamodel.py:96:9:96:24 | GSSA Variable SOURCE | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() |
| datamodel.py:96:9:96:24 | GSSA Variable SOURCE | datamodel.py:106:18:106:29 | GSSA Variable SOURCE |
| datamodel.py:96:9:96:24 | GSSA Variable c | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() |
| datamodel.py:96:9:96:24 | GSSA Variable c | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() |
| datamodel.py:96:9:96:24 | GSSA Variable c | datamodel.py:107:18:107:31 | GSSA Variable c |
| datamodel.py:97:6:97:21 | GSSA Variable oiter | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() |
| datamodel.py:97:6:97:21 | GSSA Variable oiter | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() |
| datamodel.py:97:6:97:21 | GSSA Variable oiter | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() |
| datamodel.py:106:18:106:29 | GSSA Variable SOURCE | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() |
| datamodel.py:106:18:106:29 | GSSA Variable SOURCE | datamodel.py:107:18:107:31 | GSSA Variable SOURCE |
| datamodel.py:107:18:107:31 | GSSA Variable SOURCE | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() |
| datamodel.py:107:18:107:31 | GSSA Variable SOURCE | datamodel.py:119:18:119:29 | GSSA Variable SOURCE |
| datamodel.py:107:18:107:31 | GSSA Variable c | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() |
| datamodel.py:107:18:107:31 | GSSA Variable c | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() |
| datamodel.py:119:18:119:29 | GSSA Variable SOURCE | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() |
| test.py:32:10:32:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:33:9:33:9 | ControlFlowNode for x [Tuple element at index 1] |
| test.py:32:21:32:26 | ControlFlowNode for SOURCE | test.py:32:10:32:26 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:33:9:33:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:33:9:33:12 | ControlFlowNode for Subscript |
| test.py:33:9:33:12 | ControlFlowNode for Subscript | test.py:34:10:34:10 | ControlFlowNode for y |
| test.py:43:9:43:14 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for x |
| test.py:48:9:48:16 | ControlFlowNode for Str | test.py:49:10:49:10 | ControlFlowNode for x |
| test.py:52:9:52:17 | ControlFlowNode for Str | test.py:53:10:53:10 | ControlFlowNode for x |
| test.py:56:9:56:10 | ControlFlowNode for IntegerLiteral | test.py:57:10:57:10 | ControlFlowNode for x |
| test.py:60:9:60:12 | ControlFlowNode for FloatLiteral | test.py:61:10:61:10 | ControlFlowNode for x |
| test.py:69:10:69:15 | ControlFlowNode for SOURCE | test.py:70:10:70:10 | ControlFlowNode for x |
| test.py:74:9:74:16 | ControlFlowNode for List [List element] | test.py:75:10:75:10 | ControlFlowNode for x [List element] |
| test.py:74:10:74:15 | ControlFlowNode for SOURCE | test.py:74:9:74:16 | ControlFlowNode for List [List element] |
| test.py:75:10:75:10 | ControlFlowNode for x [List element] | test.py:75:10:75:13 | ControlFlowNode for Subscript |
| test.py:82:9:82:37 | ControlFlowNode for ListComp [List element] | test.py:83:10:83:10 | ControlFlowNode for x [List element] |
| test.py:82:10:82:15 | ControlFlowNode for SOURCE | test.py:82:9:82:37 | ControlFlowNode for ListComp [List element] |
| test.py:83:10:83:10 | ControlFlowNode for x [List element] | test.py:83:10:83:13 | ControlFlowNode for Subscript |
| test.py:86:9:86:29 | ControlFlowNode for ListComp [List element] | test.py:87:10:87:10 | ControlFlowNode for x [List element] |
| test.py:86:10:86:10 | ControlFlowNode for y | test.py:86:9:86:29 | ControlFlowNode for ListComp [List element] |
| test.py:86:16:86:16 | SSA variable y | test.py:86:10:86:10 | ControlFlowNode for y |
| test.py:86:21:86:28 | ControlFlowNode for List [List element] | test.py:86:16:86:16 | SSA variable y |
| test.py:86:22:86:27 | ControlFlowNode for SOURCE | test.py:86:21:86:28 | ControlFlowNode for List [List element] |
| test.py:87:10:87:10 | ControlFlowNode for x [List element] | test.py:87:10:87:13 | ControlFlowNode for Subscript |
| test.py:90:9:90:16 | ControlFlowNode for List [List element] | test.py:91:21:91:21 | ControlFlowNode for l [List element] |
| test.py:90:10:90:15 | ControlFlowNode for SOURCE | test.py:90:9:90:16 | ControlFlowNode for List [List element] |
| test.py:91:9:91:22 | ControlFlowNode for ListComp [List element] | test.py:92:10:92:10 | ControlFlowNode for x [List element] |
| test.py:91:10:91:10 | ControlFlowNode for y | test.py:91:9:91:22 | ControlFlowNode for ListComp [List element] |
| test.py:91:16:91:16 | SSA variable y | test.py:91:10:91:10 | ControlFlowNode for y |
| test.py:91:21:91:21 | ControlFlowNode for l [List element] | test.py:91:16:91:16 | SSA variable y |
| test.py:92:10:92:10 | ControlFlowNode for x [List element] | test.py:92:10:92:13 | ControlFlowNode for Subscript |
| test.py:100:9:100:16 | ControlFlowNode for Set [List element] | test.py:101:10:101:10 | ControlFlowNode for x [List element] |
| test.py:100:10:100:15 | ControlFlowNode for SOURCE | test.py:100:9:100:16 | ControlFlowNode for Set [List element] |
| test.py:101:10:101:10 | ControlFlowNode for x [List element] | test.py:101:10:101:16 | ControlFlowNode for Attribute() |
| test.py:104:9:104:37 | ControlFlowNode for SetComp [Set element] | test.py:105:10:105:10 | ControlFlowNode for x [Set element] |
| test.py:104:10:104:15 | ControlFlowNode for SOURCE | test.py:104:9:104:37 | ControlFlowNode for SetComp [Set element] |
| test.py:105:10:105:10 | ControlFlowNode for x [Set element] | test.py:105:10:105:16 | ControlFlowNode for Attribute() |
| test.py:108:9:108:29 | ControlFlowNode for SetComp [Set element] | test.py:109:10:109:10 | ControlFlowNode for x [Set element] |
| test.py:108:10:108:10 | ControlFlowNode for y | test.py:108:9:108:29 | ControlFlowNode for SetComp [Set element] |
| test.py:108:16:108:16 | SSA variable y | test.py:108:10:108:10 | ControlFlowNode for y |
| test.py:108:21:108:28 | ControlFlowNode for List [List element] | test.py:108:16:108:16 | SSA variable y |
| test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:108:21:108:28 | ControlFlowNode for List [List element] |
| test.py:109:10:109:10 | ControlFlowNode for x [Set element] | test.py:109:10:109:16 | ControlFlowNode for Attribute() |
| test.py:112:9:112:16 | ControlFlowNode for Set [List element] | test.py:113:21:113:21 | ControlFlowNode for l [List element] |
| test.py:112:10:112:15 | ControlFlowNode for SOURCE | test.py:112:9:112:16 | ControlFlowNode for Set [List element] |
| test.py:113:9:113:22 | ControlFlowNode for SetComp [Set element] | test.py:114:10:114:10 | ControlFlowNode for x [Set element] |
| test.py:113:10:113:10 | ControlFlowNode for y | test.py:113:9:113:22 | ControlFlowNode for SetComp [Set element] |
| test.py:113:16:113:16 | SSA variable y | test.py:113:10:113:10 | ControlFlowNode for y |
| test.py:113:21:113:21 | ControlFlowNode for l [List element] | test.py:113:16:113:16 | SSA variable y |
| test.py:114:10:114:10 | ControlFlowNode for x [Set element] | test.py:114:10:114:16 | ControlFlowNode for Attribute() |
| test.py:122:9:122:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:123:10:123:10 | ControlFlowNode for x [Dictionary element at key s] |
| test.py:122:15:122:20 | ControlFlowNode for SOURCE | test.py:122:9:122:21 | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:123:10:123:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:123:10:123:15 | ControlFlowNode for Subscript |
| test.py:126:9:126:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:127:10:127:10 | ControlFlowNode for x [Dictionary element at key s] |
| test.py:126:15:126:20 | ControlFlowNode for SOURCE | test.py:126:9:126:21 | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:127:10:127:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:127:10:127:19 | ControlFlowNode for Attribute() |
| test.py:252:11:252:16 | ControlFlowNode for SOURCE | test.py:252:11:252:17 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:252:11:252:17 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:252:10:252:21 | ControlFlowNode for Subscript |
| test.py:255:10:255:17 | ControlFlowNode for List [List element] | test.py:255:10:255:20 | ControlFlowNode for Subscript |
| test.py:255:11:255:16 | ControlFlowNode for SOURCE | test.py:255:10:255:17 | ControlFlowNode for List [List element] |
| test.py:258:10:258:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:258:10:258:26 | ControlFlowNode for Subscript |
| test.py:258:15:258:20 | ControlFlowNode for SOURCE | test.py:258:10:258:21 | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:276:28:276:33 | ControlFlowNode for SOURCE | test.py:276:10:276:34 | ControlFlowNode for second() |
| test.py:335:12:335:17 | ControlFlowNode for SOURCE | test.py:335:10:335:18 | ControlFlowNode for f() |
| test.py:339:28:339:33 | ControlFlowNode for SOURCE | test.py:339:10:339:34 | ControlFlowNode for second() |
nodes
| datamodel.py:13:1:13:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:13:10:13:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| datamodel.py:38:6:38:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:65:1:65:1 | GSSA Variable c | semmle.label | GSSA Variable c |
| datamodel.py:65:5:65:7 | ControlFlowNode for C() | semmle.label | ControlFlowNode for C() |
| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:71:6:71:24 | GSSA Variable c | semmle.label | GSSA Variable c |
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:72:6:72:27 | GSSA Variable c | semmle.label | GSSA Variable c |
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() | semmle.label | ControlFlowNode for func_obj() |
| datamodel.py:73:6:73:27 | GSSA Variable c | semmle.label | GSSA Variable c |
| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:80:6:80:26 | GSSA Variable c | semmle.label | GSSA Variable c |
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:81:6:81:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() | semmle.label | ControlFlowNode for c_func_obj() |
| datamodel.py:92:1:92:4 | GSSA Variable iter | semmle.label | GSSA Variable iter |
| datamodel.py:92:8:92:21 | ControlFlowNode for gen() | semmle.label | ControlFlowNode for gen() |
| datamodel.py:92:8:92:21 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:93:6:93:20 | GSSA Variable iter | semmle.label | GSSA Variable iter |
| datamodel.py:96:1:96:5 | GSSA Variable oiter | semmle.label | GSSA Variable oiter |
| datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:96:9:96:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:96:9:96:24 | GSSA Variable c | semmle.label | GSSA Variable c |
| datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:97:6:97:21 | GSSA Variable oiter | semmle.label | GSSA Variable oiter |
| datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:106:18:106:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:107:18:107:31 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| datamodel.py:107:18:107:31 | GSSA Variable c | semmle.label | GSSA Variable c |
| datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:119:18:119:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| test.py:32:10:32:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:32:21:32:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:33:9:33:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] |
| test.py:33:9:33:12 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:34:10:34:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:43:9:43:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:44:10:44:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:48:9:48:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| test.py:49:10:49:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:52:9:52:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| test.py:53:10:53:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:56:9:56:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral |
| test.py:57:10:57:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:60:9:60:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral |
| test.py:61:10:61:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:69:10:69:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:70:10:70:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:74:9:74:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:74:10:74:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:75:10:75:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:75:10:75:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:82:9:82:37 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:82:10:82:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:83:10:83:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:83:10:83:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:86:9:86:29 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:86:10:86:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:86:16:86:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:86:21:86:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:86:22:86:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:87:10:87:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:87:10:87:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:90:9:90:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:90:10:90:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:91:9:91:22 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:91:10:91:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:91:16:91:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:91:21:91:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] |
| test.py:92:10:92:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:92:10:92:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:100:9:100:16 | ControlFlowNode for Set [List element] | semmle.label | ControlFlowNode for Set [List element] |
| test.py:100:10:100:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:101:10:101:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:101:10:101:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:104:9:104:37 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
| test.py:104:10:104:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:105:10:105:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
| test.py:105:10:105:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:108:9:108:29 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
| test.py:108:10:108:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:108:16:108:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:108:21:108:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:108:22:108:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:109:10:109:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
| test.py:109:10:109:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:112:9:112:16 | ControlFlowNode for Set [List element] | semmle.label | ControlFlowNode for Set [List element] |
| test.py:112:10:112:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:113:9:113:22 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
| test.py:113:10:113:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:113:16:113:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:113:21:113:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] |
| test.py:114:10:114:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
| test.py:114:10:114:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:122:9:122:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:122:15:122:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:123:10:123:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] |
| test.py:123:10:123:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:126:9:126:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:126:15:126:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:127:10:127:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] |
| test.py:127:10:127:19 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:252:10:252:21 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:252:11:252:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:252:11:252:17 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:255:10:255:17 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:255:10:255:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:255:11:255:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:258:10:258:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:258:10:258:26 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:258:15:258:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:276:10:276:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:276:28:276:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:335:10:335:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
| test.py:335:12:335:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:339:10:339:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:339:28:339:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
#select
| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:38:6:38:17 | ControlFlowNode for f() | <message> |
| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | <message> |
| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() | <message> |
| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() | <message> |
| datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | <message> |
| datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | <message> |
| test.py:34:10:34:10 | ControlFlowNode for y | test.py:32:21:32:26 | ControlFlowNode for SOURCE | test.py:34:10:34:10 | ControlFlowNode for y | <message> |
| test.py:44:10:44:10 | ControlFlowNode for x | test.py:43:9:43:14 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for x | <message> |
| test.py:49:10:49:10 | ControlFlowNode for x | test.py:48:9:48:16 | ControlFlowNode for Str | test.py:49:10:49:10 | ControlFlowNode for x | <message> |
| test.py:53:10:53:10 | ControlFlowNode for x | test.py:52:9:52:17 | ControlFlowNode for Str | test.py:53:10:53:10 | ControlFlowNode for x | <message> |
| test.py:57:10:57:10 | ControlFlowNode for x | test.py:56:9:56:10 | ControlFlowNode for IntegerLiteral | test.py:57:10:57:10 | ControlFlowNode for x | <message> |
| test.py:61:10:61:10 | ControlFlowNode for x | test.py:60:9:60:12 | ControlFlowNode for FloatLiteral | test.py:61:10:61:10 | ControlFlowNode for x | <message> |
| test.py:70:10:70:10 | ControlFlowNode for x | test.py:69:10:69:15 | ControlFlowNode for SOURCE | test.py:70:10:70:10 | ControlFlowNode for x | <message> |
| test.py:75:10:75:13 | ControlFlowNode for Subscript | test.py:74:10:74:15 | ControlFlowNode for SOURCE | test.py:75:10:75:13 | ControlFlowNode for Subscript | <message> |
| test.py:83:10:83:13 | ControlFlowNode for Subscript | test.py:82:10:82:15 | ControlFlowNode for SOURCE | test.py:83:10:83:13 | ControlFlowNode for Subscript | <message> |
| test.py:87:10:87:13 | ControlFlowNode for Subscript | test.py:86:22:86:27 | ControlFlowNode for SOURCE | test.py:87:10:87:13 | ControlFlowNode for Subscript | <message> |
| test.py:92:10:92:13 | ControlFlowNode for Subscript | test.py:90:10:90:15 | ControlFlowNode for SOURCE | test.py:92:10:92:13 | ControlFlowNode for Subscript | <message> |
| test.py:101:10:101:16 | ControlFlowNode for Attribute() | test.py:100:10:100:15 | ControlFlowNode for SOURCE | test.py:101:10:101:16 | ControlFlowNode for Attribute() | <message> |
| test.py:105:10:105:16 | ControlFlowNode for Attribute() | test.py:104:10:104:15 | ControlFlowNode for SOURCE | test.py:105:10:105:16 | ControlFlowNode for Attribute() | <message> |
| test.py:109:10:109:16 | ControlFlowNode for Attribute() | test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:109:10:109:16 | ControlFlowNode for Attribute() | <message> |
| test.py:114:10:114:16 | ControlFlowNode for Attribute() | test.py:112:10:112:15 | ControlFlowNode for SOURCE | test.py:114:10:114:16 | ControlFlowNode for Attribute() | <message> |
| test.py:123:10:123:15 | ControlFlowNode for Subscript | test.py:122:15:122:20 | ControlFlowNode for SOURCE | test.py:123:10:123:15 | ControlFlowNode for Subscript | <message> |
| test.py:127:10:127:19 | ControlFlowNode for Attribute() | test.py:126:15:126:20 | ControlFlowNode for SOURCE | test.py:127:10:127:19 | ControlFlowNode for Attribute() | <message> |
| test.py:252:10:252:21 | ControlFlowNode for Subscript | test.py:252:11:252:16 | ControlFlowNode for SOURCE | test.py:252:10:252:21 | ControlFlowNode for Subscript | <message> |
| test.py:255:10:255:20 | ControlFlowNode for Subscript | test.py:255:11:255:16 | ControlFlowNode for SOURCE | test.py:255:10:255:20 | ControlFlowNode for Subscript | <message> |
| test.py:258:10:258:26 | ControlFlowNode for Subscript | test.py:258:15:258:20 | ControlFlowNode for SOURCE | test.py:258:10:258:26 | ControlFlowNode for Subscript | <message> |
| test.py:276:10:276:34 | ControlFlowNode for second() | test.py:276:28:276:33 | ControlFlowNode for SOURCE | test.py:276:10:276:34 | ControlFlowNode for second() | <message> |
| test.py:335:10:335:18 | ControlFlowNode for f() | test.py:335:12:335:17 | ControlFlowNode for SOURCE | test.py:335:10:335:18 | ControlFlowNode for f() | <message> |
| test.py:339:10:339:34 | ControlFlowNode for second() | test.py:339:28:339:33 | ControlFlowNode for SOURCE | test.py:339:10:339:34 | ControlFlowNode for second() | <message> |

View File

@@ -1,5 +1,10 @@
import experimental.dataflow.testConfig
/**
* @kind path-problem
*/
from DataFlow::Node source, DataFlow::Node sink
where exists(TestConfiguration cfg | cfg.hasFlow(source, sink))
select source, sink
import experimental.dataflow.testConfig
import DataFlow::PathGraph
from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "<message>"

View File

@@ -0,0 +1,159 @@
# User-defined methods, both instance methods and class methods, can be called in many non-standard ways
# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` method on a
# class `C` will be called by the syntactic construct `await c` when `c` is an instance of `C`.
#
# These tests are based on the first part of https://docs.python.org/3/reference/datamodel.html.
# A thorough covering of methods in that document is found in classes.py.
#
# Intended sources should be the variable `SOURCE` and intended sinks should be
# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll).
# These are defined so that we can evaluate the test code.
NONSOURCE = "not a source"
SOURCE = "source"
def is_source(x):
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
def SINK(x):
if is_source(x):
print("OK")
else:
print("Unexpected flow", x)
def SINK_F(x):
if is_source(x):
print("Unexpected flow", x)
else:
print("OK")
# Callable types
# These are the types to which the function call operation (see section Calls) can be applied:
# User-defined functions
# A user-defined function object is created by a function definition (see section Function definitions). It should be called with an argument list containing the same number of items as the function's formal parameter list.
def f(a, b):
return a
SINK(f(SOURCE, 3))
# Instance methods
# An instance method object combines a class, a class instance and any callable object (normally a user-defined function).
class C(object):
def method(self, x, cls):
assert cls is self.__class__
return x
@classmethod
def classmethod(cls, x):
return x
@staticmethod
def staticmethod(x):
return x
def gen(self, x, count):
n = count
while n > 0:
yield x
n -= 1
async def coro(self, x):
return x
c = C()
# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new methods __func__ attribute is the original function object.
func_obj = c.method.__func__
# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1).
SINK(c.method(SOURCE, C))
SINK(C.method(c, SOURCE, C))
SINK(func_obj(c, SOURCE, C))
# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method.
c_func_obj = C.classmethod.__func__
# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.
SINK(c.classmethod(SOURCE))
SINK(C.classmethod(SOURCE))
SINK(c_func_obj(C, SOURCE))
# Generator functions
# A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterators iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned.
def gen(x, count):
n = count
while n > 0:
yield x
n -= 1
iter = gen(SOURCE, 1)
SINK(iter.__next__())
# SINK_F(iter.__next__()) # throws StopIteration, FP
oiter = c.gen(SOURCE, 1)
SINK(oiter.__next__())
# SINK_F(oiter.__next__()) # throws StopIteration, FP
# Coroutine functions
# A function or method which is defined using async def is called a coroutine function. Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section.
async def coro(x):
return x
import asyncio
SINK(asyncio.run(coro(SOURCE)))
SINK(asyncio.run(c.coro(SOURCE)))
class A:
def __await__(self):
# yield SOURCE -- see https://groups.google.com/g/dev-python/c/_lrrc-vp9TI?pli=1
return (yield from asyncio.coroutine(lambda: SOURCE)())
async def agen(x):
a = A()
return await a
SINK(asyncio.run(agen(SOURCE)))
# Asynchronous generator functions
# A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function.
# Calling the asynchronous iterators aiterator.__anext__() method will return an awaitable which when awaited will execute until it provides a value using the yield expression. When the function executes an empty return statement or falls off the end, a StopAsyncIteration exception is raised and the asynchronous iterator will have reached the end of the set of values to be yielded.
# Built-in functions
# A built-in function object is a wrapper around a C function. Examples of built-in functions are len() and math.sin() (math is a standard built-in module). The number and type of the arguments are determined by the C function. Special read-only attributes: __doc__ is the functions documentation string, or None if unavailable; __name__ is the functions name; __self__ is set to None (but see the next item); __module__ is the name of the module the function was defined in or None if unavailable.
# Built-in methods
# This is really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument. An example of a built-in method is alist.append(), assuming alist is a list object. In this case, the special read-only attribute __self__ is set to the object denoted by alist.
# Classes
# Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override __new__(). The arguments of the call are passed to __new__() and, in the typical case, to __init__() to initialize the new instance.
# Class Instances
# Instances of arbitrary classes can be made callable by defining a __call__() method in their class.
# If a class sets __iter__() to None, calling iter() on its instances will raise a TypeError (without falling back to __getitem__()).
# 3.3.1. Basic customization
class Customized:
a = NONSOURCE
b = NONSOURCE
def __new__(cls):
cls.a = SOURCE
return super().__new__(cls)
def __init__(self):
self.b = SOURCE
# testing __new__ and __init__
customized = Customized()
SINK(Customized.a)
SINK_F(Customized.b)
SINK(customized.a)
SINK(customized.b)

View File

@@ -1,7 +1,5 @@
| test.py:13:5:13:5 | SSA variable x | test.py:12:1:12:33 | Exit node for Function test_tuple_with_local_flow |
| test.py:13:5:13:5 | SSA variable x | test.py:14:9:14:9 | ControlFlowNode for x |
| test.py:13:10:13:18 | ControlFlowNode for Tuple | test.py:13:5:13:5 | SSA variable x |
| test.py:14:5:14:5 | SSA variable y | test.py:15:5:15:11 | SSA variable y |
| test.py:14:5:14:5 | SSA variable y | test.py:15:10:15:10 | ControlFlowNode for y |
| test.py:14:9:14:12 | ControlFlowNode for Subscript | test.py:14:5:14:5 | SSA variable y |
| test.py:15:5:15:11 | SSA variable y | test.py:12:1:12:33 | Exit node for Function test_tuple_with_local_flow |
| test.py:32:5:32:5 | SSA variable x | test.py:33:9:33:9 | ControlFlowNode for x |
| test.py:32:10:32:26 | ControlFlowNode for Tuple | test.py:32:5:32:5 | SSA variable x |
| test.py:33:5:33:5 | SSA variable y | test.py:34:5:34:11 | SSA variable y |
| test.py:33:5:33:5 | SSA variable y | test.py:34:10:34:10 | ControlFlowNode for y |
| test.py:33:9:33:12 | ControlFlowNode for Subscript | test.py:33:5:33:5 | SSA variable y |

View File

@@ -1,20 +1,43 @@
# This should cover all the syntactical constructs that we hope to support
# This should cover all the syntactical constructs that we hope to support.
# Headings refer to https://docs.python.org/3/reference/expressions.html,
# and are selected whenever they incur dataflow.
# Intended sources should be the variable `SOURCE` and intended sinks should be
# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll).
#
# Functions whose name ends with "_with_local_flow" will also be tested for local flow.
#
# All functions starting with "test_" should run and print `"OK"`.
# This can be checked by running validTest.py.
# These are included so that we can easily evaluate the test code
# These are defined so that we can evaluate the test code.
NONSOURCE = "not a source"
SOURCE = "source"
def is_source(x):
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
def SINK(x):
print(x)
if is_source(x):
print("OK")
else:
print("Unexpected flow", x)
def SINK_F(x):
if is_source(x):
print("Unexpected flow", x)
else:
print("OK")
def test_tuple_with_local_flow():
x = (3, SOURCE)
x = (NONSOURCE, SOURCE)
y = x[1]
SINK(y)
# List taken from https://docs.python.org/3/reference/expressions.html
def test_tuple_negative():
x = (NONSOURCE, SOURCE)
y = x[0]
SINK_F(y)
# 6.2.1. Identifiers (Names)
def test_names():
x = SOURCE
@@ -39,7 +62,7 @@ def test_floatnumber_literal():
def test_imagnumber_literal():
x = 42j
SINK(x)
SINK(x) # Flow missing
# 6.2.3. Parenthesized forms
def test_parenthesized_form():
@@ -51,13 +74,26 @@ def test_list_display():
x = [SOURCE]
SINK(x[0])
def test_list_display_negative():
x = [SOURCE]
SINK_F(x)
def test_list_comprehension():
x = [SOURCE for y in [3]]
x = [SOURCE for y in [NONSOURCE]]
SINK(x[0])
def test_list_comprehension_flow():
x = [y for y in [SOURCE]]
SINK(x[0])
def test_list_comprehension_inflow():
l = [SOURCE]
x = [y for y in l]
SINK(x[0])
def test_nested_list_display():
x = [* [SOURCE]]
SINK(x[0])
SINK(x[0]) # Flow missing
# 6.2.6. Set displays
def test_set_display():
@@ -65,67 +101,268 @@ def test_set_display():
SINK(x.pop())
def test_set_comprehension():
x = {SOURCE for y in [3]}
x = {SOURCE for y in [NONSOURCE]}
SINK(x.pop())
def test_set_comprehension_flow():
x = {y for y in [SOURCE]}
SINK(x.pop())
def test_set_comprehension_inflow():
l = {SOURCE}
x = {y for y in l}
SINK(x.pop())
def test_nested_set_display():
x = {* {SOURCE}}
SINK(x.pop())
SINK(x.pop()) # Flow missing
# 6.2.7. Dictionary displays
def test_dict_display():
x = {"s": SOURCE}
SINK(x["s"])
def test_dict_display_pop():
x = {"s": SOURCE}
SINK(x.pop("s"))
def test_dict_comprehension():
x = {y: SOURCE for y in ["s"]}
SINK(x["s"])
SINK(x["s"]) # Flow missing
def test_dict_comprehension_pop():
x = {y: SOURCE for y in ["s"]}
SINK(x.pop("s")) # Flow missing
def test_nested_dict_display():
x = {** {"s": SOURCE}}
SINK(x["s"])
SINK(x["s"]) # Flow missing
def test_nested_dict_display_pop():
x = {** {"s": SOURCE}}
SINK(x.pop("s")) # Flow missing
# 6.2.8. Generator expressions
def test_generator():
x = (SOURCE for y in [3])
SINK([*x][0])
x = (SOURCE for y in [NONSOURCE])
SINK([*x][0]) # Flow missing
# List taken from https://docs.python.org/3/reference/expressions.html
# 6. Expressions
# 6.1. Arithmetic conversions
# 6.2. Atoms
# 6.2.1. Identifiers (Names)
# 6.2.2. Literals
# 6.2.3. Parenthesized forms
# 6.2.4. Displays for lists, sets and dictionaries
# 6.2.5. List displays
# 6.2.6. Set displays
# 6.2.7. Dictionary displays
# 6.2.8. Generator expressions
# 6.2.9. Yield expressions
def gen(x):
yield x
def test_yield():
g = gen(SOURCE)
SINK(next(g)) # Flow missing
def gen_from(x):
yield from gen(x)
def test_yield_from():
g = gen_from(SOURCE)
SINK(next(g)) # Flow missing
# a statement rather than an expression, but related to generators
def test_for():
for x in gen(SOURCE):
SINK(x) # Flow missing
# 6.2.9.1. Generator-iterator methods
# 6.2.9.2. Examples
def test___next__():
g = gen(SOURCE)
SINK(g.__next__()) # Flow missing
def gen2(x):
m = yield x # argument of `send` has to flow to value of `yield x` (and so to `m`)
yield m
def test_send():
g = gen2(NONSOURCE)
n = next(g)
SINK(g.send(SOURCE)) # Flow missing
def gen_ex(x):
try:
yield NONSOURCE
except:
yield x # `x` has to flow to call to `throw`
def test_throw():
g = gen_ex(SOURCE)
n = next(g)
SINK(g.throw(TypeError)) # Flow missing
# no `test_close` as `close` involves no data flow
# 6.2.9.3. Asynchronous generator functions
async def agen(x):
yield x
# 6.2.9.4. Asynchronous generator-iterator methods
# 6.3. Primaries
# helper to run async test functions
def runa(a):
import asyncio
asyncio.run(a)
async def atest___anext__():
g = agen(SOURCE)
SINK(await g.__anext__()) # Flow missing
def test___anext__():
runa(atest___anext__())
async def agen2(x):
m = yield x # argument of `send` has to flow to value of `yield x` (and so to `m`)
yield m
async def atest_asend():
g = agen2(NONSOURCE)
n = await g.__anext__()
SINK(await g.asend(SOURCE)) # Flow missing
def test_asend():
runa(atest_asend())
async def agen_ex(x):
try:
yield NONSOURCE
except:
yield x # `x` has to flow to call to `athrow`
async def atest_athrow():
g = agen_ex(SOURCE)
n = await g.__anext__()
SINK(await g.athrow(TypeError)) # Flow missing
def test_athrow():
runa(atest_athrow())
# 6.3.1. Attribute references
class C:
a = SOURCE
def test_attribute_reference():
SINK(C.a) # Flow missing
# overriding __getattr__ should be tested by the class coverage tests
# 6.3.2. Subscriptions
def test_subscription_tuple():
SINK((SOURCE,)[0])
def test_subscription_list():
SINK([SOURCE][0])
def test_subscription_mapping():
SINK({"s":SOURCE}["s"])
# overriding __getitem__ should be tested by the class coverage tests
# 6.3.3. Slicings
l = [SOURCE]
def test_slicing():
s = l[0:1:1]
SINK(s[0]) # Flow missing
# The grammar seems to allow `l[0:1:1, 0:1]`, but the interpreter does not like it
# 6.3.4. Calls
# 6.4. Await expression
# 6.5. The power operator
# 6.6. Unary arithmetic and bitwise operations
# 6.7. Binary arithmetic operations
# 6.8. Shifting operations
# 6.9. Binary bitwise operations
# 6.10. Comparisons
# 6.10.1. Value comparisons
# 6.10.2. Membership test operations
# 6.10.3. Identity comparisons
# 6.11. Boolean operations
def second(a, b):
return b
def test_call_positional():
SINK(second(NONSOURCE, SOURCE))
def test_call_positional_negative():
SINK_F(second(SOURCE, NONSOURCE))
def test_call_keyword():
SINK(second(NONSOURCE, b=SOURCE)) # Flow missing
def test_call_unpack_iterable():
SINK(second(NONSOURCE, *[SOURCE])) # Flow missing
def test_call_unpack_mapping():
SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing
def f_extra_pos(a, *b):
return b[0]
def test_call_extra_pos():
SINK(f_extra_pos(NONSOURCE, SOURCE)) # Flow missing
def f_extra_keyword(a, **b):
return b["b"]
def test_call_extra_keyword():
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) # Flow missing
# return the name of the first extra keyword argument
def f_extra_keyword_flow(**a):
return [*a][0]
# call the function with our source as the name of the keyword arguemnt
def test_call_extra_keyword_flow():
SINK(f_extra_keyword_flow(**{SOURCE: None})) # Flow missing
# 6.12. Assignment expressions
def test_assignment_expression():
x = NONSOURCE
SINK(x := SOURCE) # Flow missing
# 6.13. Conditional expressions
def test_conditional_true():
SINK(SOURCE if True else NONSOURCE) # Flow missing
def test_conditional_false():
SINK(NONSOURCE if False else SOURCE) # Flow missing
# Condition is evaluated first, so x is SOURCE once chosen
def test_conditional_evaluation_true():
x = NONSOURCE
SINK(x if (SOURCE == (x := SOURCE)) else NONSOURCE) # Flow missing
# Condition is evaluated first, so x is SOURCE once chosen
def test_conditional_evaluation_false():
x = NONSOURCE
SINK(NONSOURCE if (NONSOURCE == (x := SOURCE)) else x) # Flow missing
# 6.14. Lambdas
# 6.15. Expression lists
# 6.16. Evaluation order
# 6.17. Operator precedence
def test_lambda():
f = lambda x : x
SINK(f(SOURCE))
def test_lambda_positional():
second = lambda a, b : b
SINK(second(NONSOURCE, SOURCE))
def test_lambda_positional_negative():
second = lambda a, b : b
SINK_F(second(SOURCE, NONSOURCE))
def test_lambda_keyword():
second = lambda a, b : b
SINK(second(NONSOURCE, b=SOURCE)) # Flow missing
def test_lambda_unpack_iterable():
second = lambda a, b : b
SINK(second(NONSOURCE, *[SOURCE])) # Flow missing
def test_lambda_unpack_mapping():
second = lambda a, b : b
SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing
def test_lambda_extra_pos():
f_extra_pos = lambda a, *b : b[0]
SINK(f_extra_pos(NONSOURCE, SOURCE)) # Flow missing
def test_lambda_extra_keyword():
f_extra_keyword = lambda a, **b : b["b"]
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) # Flow missing
# call the function with our source as the name of the keyword arguemnt
def test_lambda_extra_keyword_flow():
f_extra_keyword_flow = lambda **a : [*a][0] # return the name of the first extra keyword argument
SINK(f_extra_keyword_flow(**{SOURCE: None})) # Flow missing

View File

@@ -0,0 +1,49 @@
def check_output(s, f):
if s == "OK\n":
pass
else:
raise RuntimeError("Function failed", s, f)
def check_test_function(f):
from io import StringIO
import sys
capturer = StringIO()
old_stdout = sys.stdout
sys.stdout = capturer
f()
sys.stdout = old_stdout
check_output(capturer.getvalue(), f)
def check_async_test_function(f):
from io import StringIO
import sys
import asyncio
capturer = StringIO()
old_stdout = sys.stdout
sys.stdout = capturer
asyncio.run(f())
sys.stdout = old_stdout
check_output(capturer.getvalue(), f)
def check_tests_valid(testFile):
import importlib
tests = importlib.import_module(testFile)
for i in dir(tests):
# print("Considering", i)
if i.startswith("test_"):
item = getattr(tests,i)
if callable(item):
print("Checking", testFile, item)
check_test_function(item)
elif i.startswith("atest_"):
item = getattr(tests,i)
if callable(item):
print("Checking", testFile, item)
check_async_test_function(item)
if __name__ == '__main__':
check_tests_valid("classes")
check_tests_valid("test")

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1

View File

@@ -0,0 +1,72 @@
import python
import experimental.dataflow.TaintTracking
import experimental.dataflow.DataFlow
class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.(DataFlow::CfgNode).getNode().(NameNode).getId() in ["TAINTED_STRING", "TAINTED_BYTES"]
}
override predicate isSink(DataFlow::Node sink) {
exists(CallNode call |
call.getFunction().(NameNode).getId() in ["ensure_tainted", "ensure_not_tainted"] and
sink.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
}
private string repr(Expr e) {
not e instanceof Num and
not e instanceof StrConst and
not e instanceof Subscript and
not e instanceof Call and
not e instanceof Attribute and
result = e.toString()
or
result = e.(Num).getN()
or
result =
e.(StrConst).getPrefix() + e.(StrConst).getText() +
e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "")
or
result = repr(e.(Subscript).getObject()) + "[" + repr(e.(Subscript).getIndex()) + "]"
or
(
if exists(e.(Call).getAnArg()) or exists(e.(Call).getANamedArg())
then result = repr(e.(Call).getFunc()) + "(..)"
else result = repr(e.(Call).getFunc()) + "()"
)
or
result = repr(e.(Attribute).getObject()) + "." + e.(Attribute).getName()
}
query predicate test_taint(string arg_location, string test_res, string function_name, string repr) {
exists(Call call, Expr arg, boolean expected_taint, boolean has_taint |
call.getLocation().getFile().getShortName() = "test.py" and
(
call.getFunc().(Name).getId() = "ensure_tainted" and
expected_taint = true
or
call.getFunc().(Name).getId() = "ensure_not_tainted" and
expected_taint = false
) and
arg = call.getAnArg() and
(
// TODO: Replace with `hasFlowToExpr` once that is working
if
exists(TaintTracking::Configuration c |
c.hasFlowTo(any(DataFlow::Node n | n.(DataFlow::CfgNode).getNode() = arg.getAFlowNode()))
)
then has_taint = true
else has_taint = false
) and
(if expected_taint = has_taint then test_res = "ok " else test_res = "fail") and
// select
arg_location = arg.getLocation().toString() and
test_res = test_res and
function_name = call.getScope().(Function).getName() and
repr = repr(arg)
)
}

View File

@@ -0,0 +1,2 @@
| test.py:3:11:3:16 | ControlFlowNode for SOURCE | test.py:4:6:4:12 | ControlFlowNode for tainted |
| test.py:7:20:7:25 | ControlFlowNode for SOURCE | test.py:8:10:8:21 | ControlFlowNode for also_tainted |

View File

@@ -0,0 +1,22 @@
import python
import experimental.dataflow.TaintTracking
import experimental.dataflow.DataFlow
class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
}
override predicate isSink(DataFlow::Node sink) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "SINK" and
sink.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
}
from TestTaintTrackingConfiguration config, DataFlow::Node source, DataFlow::Node sink
where config.hasFlow(source, sink)
select source, sink

View File

@@ -0,0 +1,3 @@
| test.py:7:5:7:16 | SSA variable also_tainted | test.py:8:5:8:22 | SSA variable also_tainted |
| test.py:7:5:7:16 | SSA variable also_tainted | test.py:8:10:8:21 | ControlFlowNode for also_tainted |
| test.py:7:20:7:25 | ControlFlowNode for SOURCE | test.py:7:5:7:16 | SSA variable also_tainted |

View File

@@ -0,0 +1,7 @@
import python
import experimental.dataflow.TaintTracking
import experimental.dataflow.DataFlow
from DataFlow::Node nodeFrom, DataFlow::Node nodeTo
where TaintTracking::localTaintStep(nodeFrom, nodeTo)
select nodeFrom, nodeTo

View File

@@ -0,0 +1,8 @@
# Module level taint is different from inside functions, since shared dataflow library
# relies on `getEnclosingCallable`
tainted = SOURCE
SINK(tainted)
def func():
also_tainted = SOURCE
SINK(also_tainted)

View File

@@ -0,0 +1,10 @@
| test.py:26 | ok | str_methods | ts.casefold() |
| test.py:28 | ok | str_methods | ts.format_map(..) |
| test.py:29 | fail | str_methods | "{unsafe}".format_map(..) |
| test.py:40 | fail | binary_decode_encode | base64.a85encode(..) |
| test.py:41 | fail | binary_decode_encode | base64.a85decode(..) |
| test.py:44 | fail | binary_decode_encode | base64.b85encode(..) |
| test.py:45 | fail | binary_decode_encode | base64.b85decode(..) |
| test.py:48 | fail | binary_decode_encode | base64.encodebytes(..) |
| test.py:49 | fail | binary_decode_encode | base64.decodebytes(..) |
| test.py:57 | ok | f_strings | Fstring |

View File

@@ -0,0 +1 @@
import experimental.dataflow.tainttracking.TestTaintLib

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1 --lang=3

View File

@@ -0,0 +1,64 @@
# Python 3 specific taint tracking for string
TAINTED_STRING = "TAINTED_STRING"
TAINTED_BYTES = b"TAINTED_BYTES"
def ensure_tainted(*args):
print("- ensure_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
def ensure_not_tainted(*args):
print("- ensure_not_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
# Actual tests
def str_methods():
print("\n# str_methods")
ts = TAINTED_STRING
tb = TAINTED_BYTES
ensure_tainted(
ts.casefold(),
ts.format_map({}),
"{unsafe}".format_map({"unsafe": ts}),
)
def binary_decode_encode():
print("\n#percent_fmt")
tb = TAINTED_BYTES
import base64
ensure_tainted(
# New in Python 3.4
base64.a85encode(tb),
base64.a85decode(base64.a85encode(tb)),
# New in Python 3.4
base64.b85encode(tb),
base64.b85decode(base64.b85encode(tb)),
# New in Python 3.1
base64.encodebytes(tb),
base64.decodebytes(base64.encodebytes(tb)),
)
def f_strings():
print("\n#f_strings")
ts = TAINTED_STRING
ensure_tainted(f"foo {ts} bar")
# Make tests runable
str_methods()
binary_decode_encode()
f_strings()

View File

@@ -0,0 +1,62 @@
| test.py:32 | ok | str_operations | ts |
| test.py:33 | ok | str_operations | BinaryExpr |
| test.py:34 | ok | str_operations | BinaryExpr |
| test.py:35 | ok | str_operations | BinaryExpr |
| test.py:36 | ok | str_operations | ts[Slice] |
| test.py:37 | ok | str_operations | ts[Slice] |
| test.py:38 | ok | str_operations | ts[Slice] |
| test.py:39 | ok | str_operations | ts[0] |
| test.py:40 | ok | str_operations | str(..) |
| test.py:41 | ok | str_operations | bytes(..) |
| test.py:42 | ok | str_operations | unicode(..) |
| test.py:51 | ok | str_methods | ts.capitalize() |
| test.py:52 | ok | str_methods | ts.center(..) |
| test.py:53 | ok | str_methods | ts.expandtabs() |
| test.py:55 | ok | str_methods | ts.format() |
| test.py:56 | ok | str_methods | "{}".format(..) |
| test.py:57 | ok | str_methods | "{unsafe}".format(..) |
| test.py:59 | ok | str_methods | ts.join(..) |
| test.py:60 | fail | str_methods | "".join(..) |
| test.py:62 | ok | str_methods | ts.ljust(..) |
| test.py:63 | ok | str_methods | ts.lstrip() |
| test.py:64 | ok | str_methods | ts.lower() |
| test.py:66 | ok | str_methods | ts.replace(..) |
| test.py:67 | ok | str_methods | "safe".replace(..) |
| test.py:69 | ok | str_methods | ts.rjust(..) |
| test.py:70 | ok | str_methods | ts.rstrip() |
| test.py:71 | ok | str_methods | ts.strip() |
| test.py:72 | ok | str_methods | ts.swapcase() |
| test.py:73 | ok | str_methods | ts.title() |
| test.py:74 | ok | str_methods | ts.upper() |
| test.py:75 | ok | str_methods | ts.zfill(..) |
| test.py:77 | ok | str_methods | ts.encode(..) |
| test.py:78 | ok | str_methods | ts.encode(..).decode(..) |
| test.py:80 | ok | str_methods | tb.decode(..) |
| test.py:81 | ok | str_methods | tb.decode(..).encode(..) |
| test.py:84 | ok | str_methods | ts.partition(..) |
| test.py:85 | ok | str_methods | ts.rpartition(..) |
| test.py:86 | ok | str_methods | ts.rsplit(..) |
| test.py:87 | ok | str_methods | ts.split(..) |
| test.py:88 | ok | str_methods | ts.splitlines() |
| test.py:93 | ok | str_methods | "safe".replace(..) |
| test.py:95 | fail | str_methods | ts.join(..) |
| test.py:96 | fail | str_methods | ts.join(..) |
| test.py:106 | fail | non_syntactic | meth() |
| test.py:107 | fail | non_syntactic | _str(..) |
| test.py:116 | ok | percent_fmt | BinaryExpr |
| test.py:117 | ok | percent_fmt | BinaryExpr |
| test.py:118 | fail | percent_fmt | BinaryExpr |
| test.py:128 | fail | binary_decode_encode | base64.b64encode(..) |
| test.py:129 | fail | binary_decode_encode | base64.b64decode(..) |
| test.py:131 | fail | binary_decode_encode | base64.standard_b64encode(..) |
| test.py:132 | fail | binary_decode_encode | base64.standard_b64decode(..) |
| test.py:134 | fail | binary_decode_encode | base64.urlsafe_b64encode(..) |
| test.py:135 | fail | binary_decode_encode | base64.urlsafe_b64decode(..) |
| test.py:137 | fail | binary_decode_encode | base64.b32encode(..) |
| test.py:138 | fail | binary_decode_encode | base64.b32decode(..) |
| test.py:140 | fail | binary_decode_encode | base64.b16encode(..) |
| test.py:141 | fail | binary_decode_encode | base64.b16decode(..) |
| test.py:156 | fail | binary_decode_encode | base64.encodestring(..) |
| test.py:157 | fail | binary_decode_encode | base64.decodestring(..) |
| test.py:162 | fail | binary_decode_encode | quopri.encodestring(..) |
| test.py:163 | fail | binary_decode_encode | quopri.decodestring(..) |

View File

@@ -0,0 +1 @@
import experimental.dataflow.tainttracking.TestTaintLib

View File

@@ -0,0 +1,173 @@
import sys
if sys.version_info[0] == 3:
unicode = str
TAINTED_STRING = "TAINTED_STRING"
TAINTED_BYTES = b"TAINTED_BYTES"
def ensure_tainted(*args):
print("- ensure_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
def ensure_not_tainted(*args):
print("- ensure_not_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
# Actual tests
def str_operations():
print("\n# str_operations")
ts = TAINTED_STRING
tb = TAINTED_BYTES
ensure_tainted(
ts,
ts + "foo",
"foo" + ts,
ts * 5,
ts[0 : len(ts)],
ts[:],
ts[0:1000],
ts[0],
str(ts),
bytes(tb),
unicode(ts),
)
def str_methods():
print("\n# str_methods")
ts = TAINTED_STRING
tb = TAINTED_BYTES
ensure_tainted(
ts.capitalize(),
ts.center(100),
ts.expandtabs(),
ts.format(),
"{}".format(ts),
"{unsafe}".format(unsafe=ts),
ts.join(["", ""]),
"".join([ts]),
ts.ljust(100),
ts.lstrip(),
ts.lower(),
ts.replace("old", "new"),
"safe".replace("safe", ts),
ts.rjust(100),
ts.rstrip(),
ts.strip(),
ts.swapcase(),
ts.title(),
ts.upper(),
ts.zfill(100),
ts.encode("utf-8"),
ts.encode("utf-8").decode("utf-8"),
tb.decode("utf-8"),
tb.decode("utf-8").encode("utf-8"),
# string methods that return a list
ts.partition("_"),
ts.rpartition("_"),
ts.rsplit("_"),
ts.split("_"),
ts.splitlines(),
)
ensure_not_tainted(
# Intuitively I think this should be safe, but better discuss it
"safe".replace(ts, "also-safe"),
ts.join([]), # FP due to separator not being used with zero/one elements
ts.join(["safe"]), # FP due to separator not being used with zero/one elements
)
def non_syntactic():
print("\n# non_syntactic")
ts = TAINTED_STRING
meth = ts.upper
_str = str
ensure_tainted(
meth(),
_str(ts),
)
def percent_fmt():
print("\n#percent_fmt")
ts = TAINTED_STRING
tainted_fmt = ts + " %s %s"
ensure_tainted(
tainted_fmt % (1, 2),
"%s foo bar" % ts,
"%s %s %s" % (1, 2, ts),
)
def binary_decode_encode():
print("\n#percent_fmt")
tb = TAINTED_BYTES
import base64
ensure_tainted(
base64.b64encode(tb),
base64.b64decode(base64.b64encode(tb)),
base64.standard_b64encode(tb),
base64.standard_b64decode(base64.standard_b64encode(tb)),
base64.urlsafe_b64encode(tb),
base64.urlsafe_b64decode(base64.urlsafe_b64encode(tb)),
base64.b32encode(tb),
base64.b32decode(base64.b32encode(tb)),
base64.b16encode(tb),
base64.b16decode(base64.b16encode(tb)),
# # New in Python 3.4
# base64.a85encode(tb),
# base64.a85decode(base64.a85encode(tb)),
# # New in Python 3.4
# base64.b85encode(tb),
# base64.b85decode(base64.b85encode(tb)),
# # New in Python 3.1
# base64.encodebytes(tb),
# base64.decodebytes(base64.encodebytes(tb)),
# deprecated since Python 3.1, but still works
base64.encodestring(tb),
base64.decodestring(base64.encodestring(tb)),
)
import quopri
ensure_tainted(
quopri.encodestring(tb),
quopri.decodestring(quopri.encodestring(tb)),
)
# Make tests runable
str_operations()
str_methods()
non_syntactic()
percent_fmt()
binary_decode_encode()

View File

@@ -1 +0,0 @@
../CallGraph/CallGraphTest.qll

View File

@@ -1 +0,0 @@
../CallGraph/PointsTo.ql

View File

@@ -0,0 +1 @@
../CallGraph/PointsTo.ql

View File

@@ -0,0 +1,10 @@
from bottle import Bottle, route, request, redirect, response
import airspeed
app = Bottle()
@route('/other')
def a():
return airspeed.Template("sink")

View File

@@ -0,0 +1 @@
| Airspeed.py:10:30:10:35 | argument to airspeed.Template() |

View File

@@ -0,0 +1,5 @@
import python
import experimental.semmle.python.templates.Airspeed
from SSTISink s
select s

View File

@@ -0,0 +1,17 @@
from bottle import Bottle, route, request, redirect, response, SimpleTemplate
from bottle import template as temp
app = Bottle()
@route('/other')
def a():
template = "test"
tpl = SimpleTemplate(template)
@route('/other2')
def b():
template = "test"
return temp(template, name='World')

Some files were not shown because too many files have changed in this diff Show More