mirror of
https://github.com/github/codeql.git
synced 2026-05-01 11:45:14 +02:00
Merge branch 'main' into promote-sqlalchemy
This commit is contained in:
48
python/.vscode/ql.code-snippets
vendored
48
python/.vscode/ql.code-snippets
vendored
@@ -180,8 +180,6 @@
|
||||
"",
|
||||
" /** A direct instantiation of `${TM_SELECTED_TEXT}`. */",
|
||||
" private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {",
|
||||
" override CallNode node;",
|
||||
"",
|
||||
" ClassInstantiation() { this = classRef().getACall() }",
|
||||
" }",
|
||||
"",
|
||||
@@ -195,11 +193,55 @@
|
||||
"",
|
||||
" /** Gets a reference to an instance of `${TM_SELECTED_TEXT}`. */",
|
||||
" DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }",
|
||||
"",
|
||||
" /**",
|
||||
" * Taint propagation for `${TM_SELECTED_TEXT}`.",
|
||||
" */",
|
||||
" private class InstanceTaintSteps extends InstanceTaintStepsHelper {",
|
||||
" InstanceTaintSteps() { this = \"${TM_SELECTED_TEXT}\" }",
|
||||
" ",
|
||||
" override DataFlow::Node getInstance() { result = instance() }",
|
||||
" ",
|
||||
" override string getAttributeName() { none() }",
|
||||
" ",
|
||||
" override string getMethodName() { none() }",
|
||||
" ",
|
||||
" override string getAsyncMethodName() { none() }",
|
||||
" }",
|
||||
"",
|
||||
" /**",
|
||||
" * Extra taint propagation for `${TM_SELECTED_TEXT}`, not covered by `InstanceTaintSteps`.",
|
||||
" */",
|
||||
" private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {",
|
||||
" override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {",
|
||||
" // TODO",
|
||||
" none()",
|
||||
" }",
|
||||
" }",
|
||||
"}",
|
||||
],
|
||||
"description": "Type tracking class (select full class path before inserting)",
|
||||
},
|
||||
|
||||
"foo": {
|
||||
"scope": "ql",
|
||||
"prefix": "foo",
|
||||
"body": [
|
||||
" /**",
|
||||
" * Taint propagation for `$1`.",
|
||||
" */",
|
||||
" private class InstanceTaintSteps extends InstanceTaintStepsHelper {",
|
||||
" InstanceTaintSteps() { this = \"$1\" }",
|
||||
"",
|
||||
" override DataFlow::Node getInstance() { result = instance() }",
|
||||
"",
|
||||
" override string getAttributeName() { none() }",
|
||||
"",
|
||||
" override string getMethodName() { none() }",
|
||||
"",
|
||||
" override string getAsyncMethodName() { none() }",
|
||||
" }",
|
||||
],
|
||||
},
|
||||
"API graph .getMember chain": {
|
||||
"scope": "ql",
|
||||
"prefix": "api graph .getMember chain",
|
||||
|
||||
4
python/change-notes/2021-07-16-deprecate-importnode.md
Normal file
4
python/change-notes/2021-07-16-deprecate-importnode.md
Normal file
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* The `importNode` predicate from the data-flow library has been deprecated. In its place, we
|
||||
recommend using the API graphs library, accessible via `import semmle.python.ApiGraphs`.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Function parameters with default values will now see flow from those values.
|
||||
@@ -1,3 +1,4 @@
|
||||
name: codeql/python-examples
|
||||
version: 0.0.0
|
||||
libraryPathDependencies: codeql/python-all
|
||||
version: 0.0.2
|
||||
dependencies:
|
||||
codeql/python-all: "*"
|
||||
|
||||
@@ -4,4 +4,4 @@ dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
library: true
|
||||
dependencies:
|
||||
codeql/python-upgrades: ~0.0.2
|
||||
codeql/python-upgrades: 0.0.2
|
||||
|
||||
@@ -424,13 +424,8 @@ module API {
|
||||
* a value in the module `m`.
|
||||
*/
|
||||
private predicate possible_builtin_defined_in_module(string name, Module m) {
|
||||
exists(NameNode n |
|
||||
not exists(LocalVariable v | n.defines(v)) and
|
||||
n.isStore() and
|
||||
name = n.getId() and
|
||||
name = getBuiltInName() and
|
||||
m = n.getEnclosingModule()
|
||||
)
|
||||
global_name_defined_in_module(name, m) and
|
||||
name = getBuiltInName()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -445,6 +440,51 @@ module API {
|
||||
m = n.getEnclosingModule()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is an access of a variable called `name` (which is _not_ the name of a
|
||||
* built-in, and which is _not_ a global defined in the enclosing module) inside the scope `s`.
|
||||
*/
|
||||
private predicate name_possibly_defined_in_import_star(NameNode n, string name, Scope s) {
|
||||
n.isLoad() and
|
||||
name = n.getId() and
|
||||
// Not already defined in an enclosing scope.
|
||||
not exists(LocalVariable v |
|
||||
v.getId() = name and v.getScope() = n.getScope().getEnclosingScope*()
|
||||
) and
|
||||
not name = getBuiltInName() and
|
||||
s = n.getScope().getEnclosingScope*() and
|
||||
exists(potential_import_star_base(s)) and
|
||||
not global_name_defined_in_module(name, n.getEnclosingModule())
|
||||
}
|
||||
|
||||
/** Holds if a global variable called `name` is assigned a value in the module `m`. */
|
||||
private predicate global_name_defined_in_module(string name, Module m) {
|
||||
exists(NameNode n |
|
||||
not exists(LocalVariable v | n.defines(v)) and
|
||||
n.isStore() and
|
||||
name = n.getId() and
|
||||
m = n.getEnclosingModule()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API graph node for all modules imported with `from ... import *` inside the scope `s`.
|
||||
*
|
||||
* For example, given
|
||||
*
|
||||
* `from foo.bar import *`
|
||||
*
|
||||
* this would be the API graph node with the path
|
||||
*
|
||||
* `moduleImport("foo").getMember("bar")`
|
||||
*/
|
||||
private TApiNode potential_import_star_base(Scope s) {
|
||||
exists(DataFlow::Node ref |
|
||||
ref.asCfgNode() = any(ImportStarNode n | n.getScope() = s).getModule() and
|
||||
use(result, ref)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
|
||||
* `lbl` in the API graph.
|
||||
@@ -487,6 +527,15 @@ module API {
|
||||
// Built-ins, treated as members of the module `builtins`
|
||||
base = MkModuleImport("builtins") and
|
||||
lbl = Label::member(any(string name | ref = likely_builtin(name)))
|
||||
or
|
||||
// Unknown variables that may belong to a module imported with `import *`
|
||||
exists(Scope s |
|
||||
base = potential_import_star_base(s) and
|
||||
lbl =
|
||||
Label::member(any(string name |
|
||||
name_possibly_defined_in_import_star(ref.asCfgNode(), name, s)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import python
|
||||
|
||||
/** A file */
|
||||
class File extends Container {
|
||||
File() { files(this, _, _, _, _) }
|
||||
|
||||
class File extends Container, @file {
|
||||
/** DEPRECATED: Use `getAbsolutePath` instead. */
|
||||
deprecated override string getName() { result = this.getAbsolutePath() }
|
||||
|
||||
@@ -34,9 +32,7 @@ class File extends Container {
|
||||
}
|
||||
|
||||
/** Gets a short name for this file (just the file name) */
|
||||
string getShortName() {
|
||||
exists(string simple, string ext | files(this, _, simple, ext, _) | result = simple + ext)
|
||||
}
|
||||
string getShortName() { result = this.getBaseName() }
|
||||
|
||||
private int lastLine() {
|
||||
result = max(int i | exists(Location l | l.getFile() = this and l.getEndLine() = i))
|
||||
@@ -55,7 +51,7 @@ class File extends Container {
|
||||
)
|
||||
}
|
||||
|
||||
override string getAbsolutePath() { files(this, result, _, _, _) }
|
||||
override string getAbsolutePath() { files(this, result) }
|
||||
|
||||
/** Gets the URL of this file. */
|
||||
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
@@ -118,15 +114,10 @@ private predicate occupied_line(File f, int n) {
|
||||
}
|
||||
|
||||
/** A folder (directory) */
|
||||
class Folder extends Container {
|
||||
Folder() { folders(this, _, _) }
|
||||
|
||||
class Folder extends Container, @folder {
|
||||
/** DEPRECATED: Use `getAbsolutePath` instead. */
|
||||
deprecated override string getName() { result = this.getAbsolutePath() }
|
||||
|
||||
/** DEPRECATED: Use `getBaseName` instead. */
|
||||
deprecated string getSimple() { folders(this, _, result) }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
@@ -144,7 +135,7 @@ class Folder extends Container {
|
||||
endcolumn = 0
|
||||
}
|
||||
|
||||
override string getAbsolutePath() { folders(this, result, _) }
|
||||
override string getAbsolutePath() { folders(this, result) }
|
||||
|
||||
/** Gets the URL of this folder. */
|
||||
override string getURL() { result = "folder://" + this.getAbsolutePath() }
|
||||
|
||||
@@ -653,6 +653,8 @@ class DefinitionNode extends ControlFlowNode {
|
||||
DefinitionNode() {
|
||||
exists(Assign a | a.getATarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
|
||||
or
|
||||
exists(Alias a | a.getAsname().getAFlowNode() = this)
|
||||
or
|
||||
augstore(_, this)
|
||||
@@ -795,6 +797,9 @@ private AstNode assigned_value(Expr lhs) {
|
||||
/* lhs = result */
|
||||
exists(Assign a | a.getATarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs : annotation = result */
|
||||
exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* import result as lhs */
|
||||
exists(Alias a | a.getAsname() = lhs and result = a.getValue())
|
||||
or
|
||||
|
||||
@@ -7,6 +7,10 @@ private import semmle.python.regex
|
||||
* An element containing a regular expression term, that is, either
|
||||
* a string literal (parsed as a regular expression)
|
||||
* or another regular expression term.
|
||||
*
|
||||
* For sequences and alternations, we require at least one child.
|
||||
* Otherwise, we wish to represent the term differently.
|
||||
* This avoids multiple representations of the same term.
|
||||
*/
|
||||
newtype TRegExpParent =
|
||||
/** A string literal used as a regular expression */
|
||||
@@ -14,9 +18,18 @@ newtype TRegExpParent =
|
||||
/** A quantified term */
|
||||
TRegExpQuantifier(Regex re, int start, int end) { re.qualifiedItem(start, end, _, _) } or
|
||||
/** A sequence term */
|
||||
TRegExpSequence(Regex re, int start, int end) { re.sequence(start, end) } or
|
||||
/** An alternatio term */
|
||||
TRegExpAlt(Regex re, int start, int end) { re.alternation(start, end) } or
|
||||
TRegExpSequence(Regex re, int start, int end) {
|
||||
re.sequence(start, end) and
|
||||
exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead.
|
||||
} or
|
||||
/** An alternation term */
|
||||
TRegExpAlt(Regex re, int start, int end) {
|
||||
re.alternation(start, end) and
|
||||
exists(int part_end |
|
||||
re.alternationOption(start, end, start, part_end) and
|
||||
part_end < end
|
||||
) // if an alternation does not have more than one element, it should be treated as that element instead.
|
||||
} or
|
||||
/** A character class term */
|
||||
TRegExpCharacterClass(Regex re, int start, int end) { re.charSet(start, end) } or
|
||||
/** A character range term */
|
||||
@@ -61,6 +74,10 @@ class RegExpLiteral extends TRegExpLiteral, RegExpParent {
|
||||
|
||||
predicate isDotAll() { re.getAMode() = "DOTALL" }
|
||||
|
||||
predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" }
|
||||
|
||||
string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") }
|
||||
|
||||
override Regex getRegex() { result = re }
|
||||
|
||||
string getPrimaryQLClass() { result = "RegExpLiteral" }
|
||||
@@ -89,8 +106,7 @@ class RegExpTerm extends RegExpParent {
|
||||
or
|
||||
this = TRegExpQuantifier(re, start, end)
|
||||
or
|
||||
this = TRegExpSequence(re, start, end) and
|
||||
exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead.
|
||||
this = TRegExpSequence(re, start, end)
|
||||
or
|
||||
this = TRegExpSpecialChar(re, start, end)
|
||||
}
|
||||
@@ -337,10 +353,7 @@ class RegExpRange extends RegExpQuantifier {
|
||||
* This is a sequence with the elements `(ECMA|Java)` and `Script`.
|
||||
*/
|
||||
class RegExpSequence extends RegExpTerm, TRegExpSequence {
|
||||
RegExpSequence() {
|
||||
this = TRegExpSequence(re, start, end) and
|
||||
exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead.
|
||||
}
|
||||
RegExpSequence() { this = TRegExpSequence(re, start, end) }
|
||||
|
||||
override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) }
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ class ExceptStmt extends ExceptStmt_ {
|
||||
override Stmt getASubStatement() { result = this.getAStmt() }
|
||||
|
||||
override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() }
|
||||
|
||||
override Expr getType() {
|
||||
result = super.getType() and not result instanceof Tuple
|
||||
or
|
||||
result = super.getType().(Tuple).getAnElt()
|
||||
}
|
||||
}
|
||||
|
||||
/** An assert statement, such as `assert a == b, "A is not equal to b"` */
|
||||
|
||||
@@ -923,28 +923,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1173,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1860,7 +1862,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2117,7 +2120,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2621,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -3258,24 +3262,16 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
this.(PathNodeImpl).isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
not this.(PathNodeImpl).isHidden() and
|
||||
not result.(PathNodeImpl).isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
@@ -3287,6 +3283,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -3313,10 +3317,15 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
private predicate directReach(PathNode n) {
|
||||
n instanceof PathNodeSink or directReach(n.getASuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3325,12 +3334,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
query predicate subpaths = Subpaths::subpaths/4;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3622,6 +3633,86 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
|
||||
)
|
||||
}
|
||||
|
||||
private module Subpaths {
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
|
||||
* `kind`, `sc`, `apout`, and `innercc`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths01(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
|
||||
* `kind`, `sc`, `apout`, and `innercc`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, apout) and
|
||||
out.asNode() = kind.getAnOutNode(_)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, apout) and
|
||||
ret.getNodeEx() = retnode and
|
||||
kind = retnode.getKind() and
|
||||
innercc = ret.getCallContext() and
|
||||
sc = ret.getSummaryCtx() and
|
||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||
apout = ret.getAp() and
|
||||
not ret.isHidden()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
out.getAp() = apout
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` can reach a return node in a summarized subpath.
|
||||
*/
|
||||
predicate retReach(PathNode n) {
|
||||
subpaths(_, _, n, _)
|
||||
or
|
||||
exists(PathNode mid |
|
||||
retReach(mid) and
|
||||
n.getASuccessor() = mid and
|
||||
not subpaths(_, mid, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
|
||||
*
|
||||
|
||||
@@ -923,28 +923,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1173,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1860,7 +1862,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2117,7 +2120,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2621,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -3258,24 +3262,16 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
this.(PathNodeImpl).isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
not this.(PathNodeImpl).isHidden() and
|
||||
not result.(PathNodeImpl).isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
@@ -3287,6 +3283,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -3313,10 +3317,15 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
private predicate directReach(PathNode n) {
|
||||
n instanceof PathNodeSink or directReach(n.getASuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3325,12 +3334,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
query predicate subpaths = Subpaths::subpaths/4;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3622,6 +3633,86 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
|
||||
)
|
||||
}
|
||||
|
||||
private module Subpaths {
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
|
||||
* `kind`, `sc`, `apout`, and `innercc`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths01(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
|
||||
* `kind`, `sc`, `apout`, and `innercc`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, apout) and
|
||||
out.asNode() = kind.getAnOutNode(_)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, apout) and
|
||||
ret.getNodeEx() = retnode and
|
||||
kind = retnode.getKind() and
|
||||
innercc = ret.getCallContext() and
|
||||
sc = ret.getSummaryCtx() and
|
||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||
apout = ret.getAp() and
|
||||
not ret.isHidden()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
out.getAp() = apout
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` can reach a return node in a summarized subpath.
|
||||
*/
|
||||
predicate retReach(PathNode n) {
|
||||
subpaths(_, _, n, _)
|
||||
or
|
||||
exists(PathNode mid |
|
||||
retReach(mid) and
|
||||
n.getASuccessor() = mid and
|
||||
not subpaths(_, mid, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
|
||||
*
|
||||
|
||||
@@ -923,28 +923,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1173,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1860,7 +1862,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2117,7 +2120,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2621,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -3258,24 +3262,16 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
this.(PathNodeImpl).isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
not this.(PathNodeImpl).isHidden() and
|
||||
not result.(PathNodeImpl).isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
@@ -3287,6 +3283,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -3313,10 +3317,15 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
private predicate directReach(PathNode n) {
|
||||
n instanceof PathNodeSink or directReach(n.getASuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3325,12 +3334,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
query predicate subpaths = Subpaths::subpaths/4;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3622,6 +3633,86 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
|
||||
)
|
||||
}
|
||||
|
||||
private module Subpaths {
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
|
||||
* `kind`, `sc`, `apout`, and `innercc`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths01(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
|
||||
* `kind`, `sc`, `apout`, and `innercc`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, apout) and
|
||||
out.asNode() = kind.getAnOutNode(_)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, apout) and
|
||||
ret.getNodeEx() = retnode and
|
||||
kind = retnode.getKind() and
|
||||
innercc = ret.getCallContext() and
|
||||
sc = ret.getSummaryCtx() and
|
||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||
apout = ret.getAp() and
|
||||
not ret.isHidden()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
out.getAp() = apout
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` can reach a return node in a summarized subpath.
|
||||
*/
|
||||
predicate retReach(PathNode n) {
|
||||
subpaths(_, _, n, _)
|
||||
or
|
||||
exists(PathNode mid |
|
||||
retReach(mid) and
|
||||
n.getASuccessor() = mid and
|
||||
not subpaths(_, mid, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
|
||||
*
|
||||
|
||||
@@ -923,28 +923,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1173,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1860,7 +1862,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2117,7 +2120,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2621,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -3258,24 +3262,16 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
this.(PathNodeImpl).isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
not this.(PathNodeImpl).isHidden() and
|
||||
not result.(PathNodeImpl).isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
@@ -3287,6 +3283,14 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
predicate isHidden() {
|
||||
hiddenNode(this.getNodeEx().asNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
or
|
||||
this.getNodeEx() instanceof TNodeImplicitRead
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -3313,10 +3317,15 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
private predicate directReach(PathNode n) {
|
||||
n instanceof PathNodeSink or directReach(n.getASuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3325,12 +3334,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
query predicate subpaths = Subpaths::subpaths/4;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3622,6 +3633,86 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
|
||||
)
|
||||
}
|
||||
|
||||
private module Subpaths {
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
|
||||
* `kind`, `sc`, `apout`, and `innercc`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths01(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
|
||||
* `kind`, `sc`, `apout`, and `innercc`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, apout) and
|
||||
out.asNode() = kind.getAnOutNode(_)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, apout) and
|
||||
ret.getNodeEx() = retnode and
|
||||
kind = retnode.getKind() and
|
||||
innercc = ret.getCallContext() and
|
||||
sc = ret.getSummaryCtx() and
|
||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||
apout = ret.getAp() and
|
||||
not ret.isHidden()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
out.getAp() = apout
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` can reach a return node in a summarized subpath.
|
||||
*/
|
||||
predicate retReach(PathNode n) {
|
||||
subpaths(_, _, n, _)
|
||||
or
|
||||
exists(PathNode mid |
|
||||
retReach(mid) and
|
||||
n.getASuccessor() = mid and
|
||||
not subpaths(_, mid, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
|
||||
*
|
||||
|
||||
@@ -786,13 +786,18 @@ private module Cached {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
* Holds if the call context `call` improves virtual dispatch in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
or
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
|
||||
}
|
||||
|
||||
@@ -846,6 +851,15 @@ private module Cached {
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
recordDataFlowCallSiteDispatch(call, callable) or
|
||||
recordDataFlowCallSiteUnreachable(call, callable)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` at which a cast can occur such that the type should be checked.
|
||||
*/
|
||||
|
||||
@@ -869,6 +869,9 @@ predicate jumpStep(Node nodeFrom, Node nodeTo) {
|
||||
module_export(mv.getScope(), r.getAttributeName(), nodeFrom) and
|
||||
nodeTo = r
|
||||
)
|
||||
or
|
||||
// Default value for parameter flows to that parameter
|
||||
defaultValueFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1033,6 +1036,19 @@ predicate kwOverflowStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node
|
||||
)
|
||||
}
|
||||
|
||||
predicate defaultValueFlowStep(CfgNode nodeFrom, CfgNode nodeTo) {
|
||||
exists(Function f, Parameter p, ParameterDefinition def |
|
||||
// `getArgByName` supports, unlike `getAnArg`, keyword-only parameters
|
||||
p = f.getArgByName(_) and
|
||||
nodeFrom.asExpr() = p.getDefault() and
|
||||
// The following expresses
|
||||
// nodeTo.(ParameterNode).getParameter() = p
|
||||
// without non-monotonic recursion
|
||||
def.getParameter() = p and
|
||||
nodeTo.getNode() = def.getDefiningNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`.
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,10 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use the API graphs library (`semmle.python.ApiGraphs`) instead.
|
||||
*
|
||||
* For a drop-in replacement, use `API::moduleImport(name).getAUse()`.
|
||||
*
|
||||
* Gets a `Node` that refers to the module referenced by `name`.
|
||||
* Note that for the statement `import pkg.mod`, the new variable introduced is `pkg` that is a
|
||||
* reference to the module `pkg`.
|
||||
@@ -37,7 +41,7 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
* `mypkg/foo.py` but the variable `foo` containing `42` -- however, `import mypkg.foo` will always cause `mypkg.foo`
|
||||
* to refer to the module.
|
||||
*/
|
||||
Node importNode(string name) {
|
||||
deprecated Node importNode(string name) {
|
||||
exists(Variable var, Import imp, Alias alias |
|
||||
alias = imp.getAName() and
|
||||
alias.getAsname() = var.getAStore() and
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides helper predicates for pretty-printing `DataFlow::Node`s.
|
||||
*
|
||||
* Since these have not been performance optimized, please only use them for
|
||||
* debug-queries or in tests.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the pretty-printed version of the Expr `e`.
|
||||
*/
|
||||
string prettyExpr(Expr e) {
|
||||
not e instanceof Num and
|
||||
not e instanceof StrConst and
|
||||
@@ -27,7 +41,9 @@ string prettyExpr(Expr e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pretty-printed version of the DataFlow::Node `node`
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the pretty-printed version of the DataFlow::Node `node`
|
||||
*/
|
||||
bindingset[node]
|
||||
string prettyNode(DataFlow::Node node) {
|
||||
@@ -35,7 +51,9 @@ string prettyNode(DataFlow::Node node) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pretty-printed version of the DataFlow::Node `node`, that is suitable for use
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the pretty-printed version of the DataFlow::Node `node`, that is suitable for use
|
||||
* with `TestUtilities.InlineExpectationsTest` (that is, no spaces unless required).
|
||||
*/
|
||||
bindingset[node]
|
||||
@@ -46,9 +46,13 @@ private module Cached {
|
||||
or
|
||||
copyStep(nodeFrom, nodeTo)
|
||||
or
|
||||
forStep(nodeFrom, nodeTo)
|
||||
DataFlowPrivate::forReadStep(nodeFrom, _, nodeTo)
|
||||
or
|
||||
unpackingAssignmentStep(nodeFrom, nodeTo)
|
||||
DataFlowPrivate::iterableUnpackingReadStep(nodeFrom, _, nodeTo)
|
||||
or
|
||||
DataFlowPrivate::iterableUnpackingStoreStep(nodeFrom, _, nodeTo)
|
||||
or
|
||||
awaitStep(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,26 +205,9 @@ predicate copyStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to `for`-iteration,
|
||||
* for example `for x in xs`, or `for x,y in points`.
|
||||
* Holds if taint can flow from `nodeFrom` to `nodeTo` with an `await`-step,
|
||||
* such that the whole expression `await x` is tainted if `x` is tainted.
|
||||
*/
|
||||
predicate forStep(DataFlow::CfgNode nodeFrom, DataFlow::EssaNode nodeTo) {
|
||||
exists(EssaNodeDefinition defn, For for |
|
||||
for.getTarget().getAChildNode*() = defn.getDefiningNode().getNode() and
|
||||
nodeTo.getVar() = defn and
|
||||
nodeFrom.asExpr() = for.getIter()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to iterable unpacking.
|
||||
* Only handles normal assignment (`x,y = calc_point()`), since `for x,y in points` is handled by `forStep`.
|
||||
*/
|
||||
predicate unpackingAssignmentStep(DataFlow::CfgNode nodeFrom, DataFlow::EssaNode nodeTo) {
|
||||
// `a, b = myiterable` or `head, *tail = myiterable` (only Python 3)
|
||||
exists(MultiAssignmentDefinition defn, Assign assign |
|
||||
assign.getATarget().contains(defn.getDefiningNode().getNode()) and
|
||||
nodeTo.getVar() = defn and
|
||||
nodeFrom.asExpr() = assign.getValue()
|
||||
)
|
||||
predicate awaitStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
nodeTo.asExpr().(Await).getValue() = nodeFrom.asExpr()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
||||
private import semmle.python.frameworks.internal.SelfRefMixin
|
||||
private import semmle.python.frameworks.Multidict
|
||||
private import semmle.python.frameworks.Yarl
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -293,6 +294,65 @@ module AiohttpWebModel {
|
||||
|
||||
/** Gets a reference to an instance of `aiohttp.web.Request`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `aiohttp.web.Request`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "aiohttp.web.Request" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
"url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
|
||||
"headers", "transport", "cookies", "content", "_payload", "content_type", "charset",
|
||||
"http_range", "if_modified_since", "if_unmodified_since", "if_range", "match_info"
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() { result in ["clone", "get_extra_info"] }
|
||||
|
||||
override string getAsyncMethodName() {
|
||||
result in ["read", "text", "json", "multipart", "post"]
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute read on an `aiohttp.web.Request` that is a `MultiDictProxy` instance. */
|
||||
class AiohttpRequestMultiDictProxyInstances extends Multidict::MultiDictProxy::InstanceSource {
|
||||
AiohttpRequestMultiDictProxyInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = Request::instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() in ["query", "headers"]
|
||||
or
|
||||
// Handle the common case of `x = await request.post()`
|
||||
// but don't try to handle anything else, since we don't have an easy way to do this yet.
|
||||
// TODO: more complete handling of `await request.post()`
|
||||
exists(Await await, DataFlow::CallCfgNode call, DataFlow::AttrRead read |
|
||||
this.asExpr() = await
|
||||
|
|
||||
read.(DataFlow::AttrRead).getObject() = Request::instance() and
|
||||
read.(DataFlow::AttrRead).getAttributeName() = "post" and
|
||||
call.getFunction() = read and
|
||||
await.getValue() = call.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute read on an `aiohttp.web.Request` that is a `yarl.URL` instance. */
|
||||
class AiohttpRequestYarlUrlInstances extends Yarl::Url::InstanceSource {
|
||||
AiohttpRequestYarlUrlInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = Request::instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() in ["url", "rel_url"]
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute read on an `aiohttp.web.Request` that is a `aiohttp.StreamReader` instance. */
|
||||
class AiohttpRequestStreamReaderInstances extends StreamReader::InstanceSource {
|
||||
AiohttpRequestStreamReaderInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = Request::instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() in ["content", "_payload"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,30 +417,20 @@ module AiohttpWebModel {
|
||||
/**
|
||||
* Taint propagation for `aiohttp.StreamReader`.
|
||||
*/
|
||||
private class AiohttpStreamReaderAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Methods
|
||||
//
|
||||
// TODO: When we have tools that make it easy, model these properly to handle
|
||||
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
|
||||
// (since it allows us to at least capture the most common cases).
|
||||
nodeFrom = StreamReader::instance() and
|
||||
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
|
||||
// normal methods
|
||||
attr.getAttributeName() in ["read_nowait"] and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
|
||||
or
|
||||
// async methods
|
||||
exists(Await await, DataFlow::CallCfgNode call |
|
||||
attr.getAttributeName() in [
|
||||
"read", "readany", "readexactly", "readline", "readchunk", "iter_chunked",
|
||||
"iter_any", "iter_chunks"
|
||||
] and
|
||||
call.getFunction() = attr and
|
||||
await.getValue() = call.asExpr() and
|
||||
nodeTo.asExpr() = await
|
||||
)
|
||||
)
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "aiohttp.StreamReader" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { none() }
|
||||
|
||||
override string getMethodName() { result in ["read_nowait"] }
|
||||
|
||||
override string getAsyncMethodName() {
|
||||
result in [
|
||||
"read", "readany", "readexactly", "readline", "readchunk", "iter_chunked", "iter_any",
|
||||
"iter_chunks"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -431,80 +481,6 @@ module AiohttpWebModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint propagation for `aiohttp.web.Request`.
|
||||
*
|
||||
* See https://docs.aiohttp.org/en/stable/web_reference.html#request-and-base-request
|
||||
*/
|
||||
private class AiohttpRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Methods
|
||||
//
|
||||
// TODO: When we have tools that make it easy, model these properly to handle
|
||||
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
|
||||
// (since it allows us to at least capture the most common cases).
|
||||
nodeFrom = Request::instance() and
|
||||
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
|
||||
// normal methods
|
||||
attr.getAttributeName() in ["clone", "get_extra_info"] and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
|
||||
or
|
||||
// async methods
|
||||
exists(Await await, DataFlow::CallCfgNode call |
|
||||
attr.getAttributeName() in ["read", "text", "json", "multipart", "post"] and
|
||||
call.getFunction() = attr and
|
||||
await.getValue() = call.asExpr() and
|
||||
nodeTo.asExpr() = await
|
||||
)
|
||||
)
|
||||
or
|
||||
// Attributes
|
||||
nodeFrom = Request::instance() and
|
||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
||||
"url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
|
||||
"headers", "transport", "cookies", "content", "_payload", "content_type", "charset",
|
||||
"http_range", "if_modified_since", "if_unmodified_since", "if_range", "match_info"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute read on an `aiohttp.web.Request` that is a `MultiDictProxy` instance. */
|
||||
class AiohttpRequestMultiDictProxyInstances extends Multidict::MultiDictProxy::InstanceSource {
|
||||
AiohttpRequestMultiDictProxyInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = Request::instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() in ["query", "headers"]
|
||||
or
|
||||
// Handle the common case of `x = await request.post()`
|
||||
// but don't try to handle anything else, since we don't have an easy way to do this yet.
|
||||
// TODO: more complete handling of `await request.post()`
|
||||
exists(Await await, DataFlow::CallCfgNode call, DataFlow::AttrRead read |
|
||||
this.asExpr() = await
|
||||
|
|
||||
read.(DataFlow::AttrRead).getObject() = Request::instance() and
|
||||
read.(DataFlow::AttrRead).getAttributeName() = "post" and
|
||||
call.getFunction() = read and
|
||||
await.getValue() = call.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute read on an `aiohttp.web.Request` that is a `yarl.URL` instance. */
|
||||
class AiohttpRequestYarlUrlInstances extends Yarl::Url::InstanceSource {
|
||||
AiohttpRequestYarlUrlInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = Request::instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() in ["url", "rel_url"]
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute read on an `aiohttp.web.Request` that is a `aiohttp.StreamReader` instance. */
|
||||
class AiohttpRequestStreamReaderInstances extends StreamReader::InstanceSource {
|
||||
AiohttpRequestStreamReaderInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = Request::instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() in ["content", "_payload"]
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// aiohttp.web Response modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -10,9 +10,11 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.PEP249
|
||||
private import semmle.python.frameworks.Stdlib
|
||||
private import semmle.python.regex
|
||||
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
||||
private import semmle.python.frameworks.internal.SelfRefMixin
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* Provides models for the `django` PyPI package.
|
||||
@@ -289,6 +291,178 @@ private module Django {
|
||||
API::Node subclassRef() { result = any(ModeledSubclass subclass).getASubclass*() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `django.utils.datastructures.MultiValueDict` class
|
||||
*
|
||||
* See
|
||||
* - https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.QueryDict (subclass that has proper docs)
|
||||
* - https://www.kite.com/python/docs/django.utils.datastructures.MultiValueDict
|
||||
*/
|
||||
module MultiValueDict {
|
||||
/** Gets a reference to the `django.utils.datastructures.MultiValueDict` class. */
|
||||
private API::Node classRef() {
|
||||
result =
|
||||
API::moduleImport("django")
|
||||
.getMember("utils")
|
||||
.getMember("datastructures")
|
||||
.getMember("MultiValueDict")
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of instances of `django.utils.datastructures.MultiValueDict`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `MultiValueDict::instance()` to get references to instances of `django.utils.datastructures.MultiValueDict`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** A direct instantiation of `django.utils.datastructures.MultiValueDict`. */
|
||||
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
||||
ClassInstantiation() { this = classRef().getACall() }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.utils.datastructures.MultiValueDict`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.utils.datastructures.MultiValueDict`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `django.utils.datastructures.MultiValueDict`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "django.utils.datastructures.MultiValueDict" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { none() }
|
||||
|
||||
override string getMethodName() {
|
||||
result in ["getlist", "lists", "popitem", "dict", "urlencode"]
|
||||
}
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra taint propagation for `django.utils.datastructures.MultiValueDict`, not covered by `InstanceTaintSteps`.
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// class instantiation
|
||||
exists(ClassInstantiation call |
|
||||
nodeFrom = call.getArg(0) and
|
||||
nodeTo = call
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `django.core.files.uploadedfile.UploadedFile` class
|
||||
*
|
||||
* See https://docs.djangoproject.com/en/3.0/ref/files/uploads/#django.core.files.uploadedfile.UploadedFile.
|
||||
*/
|
||||
module UploadedFile {
|
||||
/**
|
||||
* A source of instances of `django.core.files.uploadedfile.UploadedFile`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `UploadedFile::instance()` to get references to instances of `django.core.files.uploadedfile.UploadedFile`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `django.core.files.uploadedfile.UploadedFile`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.core.files.uploadedfile.UploadedFile`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `django.core.files.uploadedfile.UploadedFile`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "django.core.files.uploadedfile.UploadedFile" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
"content_type", "content_type_extra", "content_type_extra", "charset", "name", "file"
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() { none() }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/** A file-like object instance that originates from a `UploadedFile`. */
|
||||
class UploadedFileFileLikeInstances extends Stdlib::FileLikeObject::InstanceSource {
|
||||
UploadedFileFileLikeInstances() { this.(DataFlow::AttrRead).accesses(instance(), "file") }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `django.urls.ResolverMatch` class
|
||||
*
|
||||
* See https://docs.djangoproject.com/en/3.0/ref/urlresolvers/#django.urls.ResolverMatch.
|
||||
*/
|
||||
module ResolverMatch {
|
||||
/**
|
||||
* A source of instances of `django.urls.ResolverMatch`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `ResolverMatch::instance()` to get references to instances of `django.urls.ResolverMatch`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `django.urls.ResolverMatch`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.urls.ResolverMatch`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `django.urls.ResolverMatch`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "django.urls.ResolverMatch" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { result in ["args", "kwargs"] }
|
||||
|
||||
override string getMethodName() { none() }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -587,6 +761,118 @@ private module PrivateDjango {
|
||||
|
||||
/** Gets a reference to an instance of `django.http.request.HttpRequest`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `django.http.request.HttpRequest`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "django.http.request.HttpRequest" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
// str / bytes
|
||||
"body", "path", "path_info", "method", "encoding", "content_type",
|
||||
// django.http.QueryDict
|
||||
"GET", "POST",
|
||||
// dict[str, str]
|
||||
"content_params", "COOKIES",
|
||||
// dict[str, Any]
|
||||
"META",
|
||||
// HttpHeaders (case insensitive dict-like)
|
||||
"headers",
|
||||
// MultiValueDict[str, UploadedFile]
|
||||
"FILES",
|
||||
// django.urls.ResolverMatch
|
||||
"resolver_match"
|
||||
]
|
||||
// TODO: Handle that a HttpRequest is iterable
|
||||
}
|
||||
|
||||
override string getMethodName() {
|
||||
result in ["get_full_path", "get_full_path_info", "read", "readline", "readlines"]
|
||||
}
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra taint propagation for `django.http.request.HttpRequest`, not covered by `InstanceTaintSteps`.
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// special handling of the `build_absolute_uri` method, see
|
||||
// https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.build_absolute_uri
|
||||
exists(DataFlow::AttrRead attr, DataFlow::CallCfgNode call, DataFlow::Node instance |
|
||||
instance = django::http::request::HttpRequest::instance() and
|
||||
attr.getObject() = instance
|
||||
|
|
||||
attr.getAttributeName() = "build_absolute_uri" and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr and
|
||||
call = nodeTo and
|
||||
(
|
||||
not exists(call.getArg(_)) and
|
||||
not exists(call.getArgByName(_)) and
|
||||
nodeFrom = instance
|
||||
or
|
||||
nodeFrom = call.getArg(0)
|
||||
or
|
||||
nodeFrom = call.getArgByName("location")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute read on an django request that is a `MultiValueDict` instance. */
|
||||
private class DjangoHttpRequestMultiValueDictInstances extends Django::MultiValueDict::InstanceSource {
|
||||
DjangoHttpRequestMultiValueDictInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() in ["GET", "POST", "FILES"]
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute read on an django request that is a `ResolverMatch` instance. */
|
||||
private class DjangoHttpRequestResolverMatchInstances extends Django::ResolverMatch::InstanceSource {
|
||||
DjangoHttpRequestResolverMatchInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() = "resolver_match"
|
||||
}
|
||||
}
|
||||
|
||||
/** An `UploadedFile` instance that originates from a django request. */
|
||||
private class DjangoHttpRequestUploadedFileInstances extends Django::UploadedFile::InstanceSource {
|
||||
DjangoHttpRequestUploadedFileInstances() {
|
||||
// TODO: this currently only works in local-scope, since writing type-trackers for
|
||||
// this is a little too much effort. Once API-graphs are available for more
|
||||
// things, we can rewrite this.
|
||||
//
|
||||
// TODO: This approach for identifying member-access is very adhoc, and we should
|
||||
// be able to do something more structured for providing modeling of the members
|
||||
// of a container-object.
|
||||
//
|
||||
// dicts
|
||||
exists(DataFlow::AttrRead files, DataFlow::Node dict |
|
||||
files.accesses(instance(), "FILES") and
|
||||
(
|
||||
dict = files
|
||||
or
|
||||
dict.(DataFlow::MethodCallNode).calls(files, "dict")
|
||||
)
|
||||
|
|
||||
this.asCfgNode().(SubscriptNode).getObject() = dict.asCfgNode()
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(dict, "get")
|
||||
)
|
||||
or
|
||||
// getlist
|
||||
exists(DataFlow::AttrRead files, DataFlow::MethodCallNode getlistCall |
|
||||
files.accesses(instance(), "FILES") and
|
||||
getlistCall.calls(files, "getlist") and
|
||||
this.asCfgNode().(SubscriptNode).getObject() = getlistCall.asCfgNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1455,9 +1741,6 @@ private module PrivateDjango {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// Form and form field modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1883,36 +2166,6 @@ private module PrivateDjango {
|
||||
}
|
||||
}
|
||||
|
||||
private class DjangoHttpRequstAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
nodeFrom = django::http::request::HttpRequest::instance() and
|
||||
exists(DataFlow::AttrRead read | nodeTo = read and read.getObject() = nodeFrom |
|
||||
read.getAttributeName() in [
|
||||
// str / bytes
|
||||
"body", "path", "path_info", "method", "encoding", "content_type",
|
||||
// django.http.QueryDict
|
||||
// TODO: Model QueryDict
|
||||
"GET", "POST",
|
||||
// dict[str, str]
|
||||
"content_params", "COOKIES",
|
||||
// dict[str, Any]
|
||||
"META",
|
||||
// HttpHeaders (case insensitive dict-like)
|
||||
"headers",
|
||||
// MultiValueDict[str, UploadedFile]
|
||||
// TODO: Model MultiValueDict
|
||||
// TODO: Model UploadedFile
|
||||
"FILES",
|
||||
// django.urls.ResolverMatch
|
||||
// TODO: Model ResolverMatch
|
||||
"resolver_match"
|
||||
]
|
||||
// TODO: Handle calls to methods
|
||||
// TODO: Handle that a HttpRequest is iterable
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// django.shortcuts.redirect
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -10,6 +10,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.frameworks.Werkzeug
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* Provides models for the `flask` PyPI package.
|
||||
@@ -341,83 +342,101 @@ module Flask {
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint propagation for a flask request.
|
||||
* Taint propagation for `flask.Request`.
|
||||
*
|
||||
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request
|
||||
*/
|
||||
private class FlaskRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Methods
|
||||
exists(string method_name | method_name in ["get_data", "get_json"] |
|
||||
// Method access
|
||||
nodeFrom = request().getAUse() and
|
||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
||||
nodeTo.(DataFlow::AttrRead).getAttributeName() = method_name
|
||||
or
|
||||
// Method call
|
||||
nodeFrom = request().getMember(method_name).getAUse() and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = nodeFrom
|
||||
)
|
||||
or
|
||||
// Attributes
|
||||
nodeFrom = request().getAUse() and
|
||||
exists(DataFlow::AttrRead read | nodeTo = read and read.getObject() = nodeFrom |
|
||||
read.getAttributeName() in [
|
||||
// str
|
||||
"path", "full_path", "base_url", "url", "access_control_request_method",
|
||||
"content_encoding", "content_md5", "content_type", "data", "method", "mimetype",
|
||||
"origin", "query_string", "referrer", "remote_addr", "remote_user", "user_agent",
|
||||
// dict
|
||||
"environ", "cookies", "mimetype_params", "view_args",
|
||||
// json
|
||||
"json",
|
||||
// List[str]
|
||||
"access_route",
|
||||
// file-like
|
||||
"stream", "input_stream",
|
||||
// MultiDict[str, str]
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict
|
||||
"args", "values", "form",
|
||||
// MultiDict[str, FileStorage]
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage
|
||||
// TODO: FileStorage needs extra taint steps
|
||||
"files",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.HeaderSet
|
||||
"access_control_request_headers", "pragma",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Accept
|
||||
// TODO: Kinda badly modeled for now -- has type List[Tuple[value, quality]], and some extra methods
|
||||
"accept_charsets", "accept_encodings", "accept_languages", "accept_mimetypes",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization
|
||||
// TODO: dict subclass with extra attributes like `username` and `password`
|
||||
"authorization",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.RequestCacheControl
|
||||
// TODO: has attributes like `no_cache`, and `to_header` method (actually, many of these models do)
|
||||
"cache_control",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers
|
||||
// TODO: dict-like with wsgiref.headers.Header compatibility methods
|
||||
"headers"
|
||||
]
|
||||
)
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "flask.Request" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = request().getAUse() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
// str
|
||||
"path", "full_path", "base_url", "url", "access_control_request_method",
|
||||
"content_encoding", "content_md5", "content_type", "data", "method", "mimetype", "origin",
|
||||
"query_string", "referrer", "remote_addr", "remote_user", "user_agent",
|
||||
// dict
|
||||
"environ", "cookies", "mimetype_params", "view_args",
|
||||
// json
|
||||
"json",
|
||||
// List[str]
|
||||
"access_route",
|
||||
// file-like
|
||||
"stream", "input_stream",
|
||||
// MultiDict[str, str]
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict
|
||||
"args", "values", "form",
|
||||
// MultiDict[str, FileStorage]
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage
|
||||
// TODO: FileStorage needs extra taint steps
|
||||
"files",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.HeaderSet
|
||||
"access_control_request_headers", "pragma",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Accept
|
||||
// TODO: Kinda badly modeled for now -- has type List[Tuple[value, quality]], and some extra methods
|
||||
"accept_charsets", "accept_encodings", "accept_languages", "accept_mimetypes",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization
|
||||
// TODO: dict subclass with extra attributes like `username` and `password`
|
||||
"authorization",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.RequestCacheControl
|
||||
// TODO: has attributes like `no_cache`, and `to_header` method (actually, many of these models do)
|
||||
"cache_control",
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers
|
||||
// TODO: dict-like with wsgiref.headers.Header compatibility methods
|
||||
"headers"
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() { result in ["get_data", "get_json"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
private class RequestAttrMultiDict extends Werkzeug::werkzeug::datastructures::MultiDict::InstanceSourceApiNode {
|
||||
private class RequestAttrMultiDict extends Werkzeug::MultiDict::InstanceSource {
|
||||
string attr_name;
|
||||
|
||||
RequestAttrMultiDict() {
|
||||
attr_name in ["args", "values", "form", "files"] and
|
||||
this = request().getMember(attr_name)
|
||||
this.(DataFlow::AttrRead).accesses(request().getAUse(), attr_name)
|
||||
}
|
||||
|
||||
override string toString() { result = this.(API::Node).toString() }
|
||||
}
|
||||
|
||||
private class RequestAttrFiles extends RequestAttrMultiDict {
|
||||
// TODO: Somehow specify that elements of `RequestAttrFiles` are
|
||||
// Werkzeug::werkzeug::datastructures::FileStorage and should have those additional taint steps
|
||||
// AND that the 0-indexed argument to its' save method is a sink for path-injection.
|
||||
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.save
|
||||
RequestAttrFiles() { attr_name = "files" }
|
||||
/** An `FileStorage` instance that originates from a flask request. */
|
||||
private class FlaskRequestFileStorageInstances extends Werkzeug::FileStorage::InstanceSource {
|
||||
FlaskRequestFileStorageInstances() {
|
||||
// TODO: this currently only works in local-scope, since writing type-trackers for
|
||||
// this is a little too much effort. Once API-graphs are available for more
|
||||
// things, we can rewrite this.
|
||||
//
|
||||
// TODO: This approach for identifying member-access is very adhoc, and we should
|
||||
// be able to do something more structured for providing modeling of the members
|
||||
// of a container-object.
|
||||
exists(DataFlow::AttrRead files | files.accesses(request().getAUse(), "files") |
|
||||
this.asCfgNode().(SubscriptNode).getObject() = files.asCfgNode()
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(files, "get")
|
||||
or
|
||||
exists(DataFlow::MethodCallNode getlistCall | getlistCall.calls(files, "getlist") |
|
||||
this.asCfgNode().(SubscriptNode).getObject() = getlistCall.asCfgNode()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An `Headers` instance that originates from a flask request. */
|
||||
private class FlaskRequestHeadersInstances extends Werkzeug::Headers::InstanceSource {
|
||||
FlaskRequestHeadersInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(request().getAUse(), "headers")
|
||||
}
|
||||
}
|
||||
|
||||
/** An `Authorization` instance that originates from a flask request. */
|
||||
private class FlaskRequestAuthorizationInstances extends Werkzeug::Authorization::InstanceSource {
|
||||
FlaskRequestAuthorizationInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(request().getAUse(), "authorization")
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* Provides models for the `MarkupSafe` PyPI package.
|
||||
@@ -82,7 +83,7 @@ private module MarkupSafeModel {
|
||||
}
|
||||
|
||||
/** Taint propagation for `markupsafe.Markup`. */
|
||||
class AddtionalTaintSteps extends TaintTracking::AdditionalTaintStep {
|
||||
private class AddtionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
nodeTo.(ClassInstantiation).getArg(0) = nodeFrom
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -60,28 +61,29 @@ module Multidict {
|
||||
|
||||
/**
|
||||
* Taint propagation for `multidict.MultiDictProxy`.
|
||||
*
|
||||
* See https://multidict.readthedocs.io/en/stable/multidict.html#multidictproxy
|
||||
*/
|
||||
class MultiDictProxyAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "multidict.MultiDictProxy" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { none() }
|
||||
|
||||
override string getMethodName() { result in ["getone", "getall"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra taint propagation for `multidict.MultiDictProxy`, not covered by `InstanceTaintSteps`.
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// class instantiation
|
||||
exists(ClassInstantiation call |
|
||||
nodeFrom = call.getArg(0) and
|
||||
nodeTo = call
|
||||
)
|
||||
or
|
||||
// Methods
|
||||
//
|
||||
// TODO: When we have tools that make it easy, model these properly to handle
|
||||
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
|
||||
// (since it allows us to at least capture the most common cases).
|
||||
nodeFrom = instance() and
|
||||
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
|
||||
// methods (non-async)
|
||||
attr.getAttributeName() in ["getone", "getall"] and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,173 @@ private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.PEP249
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/** Provides models for the Python standard library. */
|
||||
private module Stdlib {
|
||||
module Stdlib {
|
||||
/**
|
||||
* Provides models for file-like objects,
|
||||
* mostly to define standard set of extra taint-steps.
|
||||
*
|
||||
* See
|
||||
* - https://docs.python.org/3.9/glossary.html#term-file-like-object
|
||||
* - https://docs.python.org/3.9/library/io.html#io.IOBase
|
||||
*/
|
||||
module FileLikeObject {
|
||||
/**
|
||||
* A source of a file-like object, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `like::instance()` to get references to instances of `file.like`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to a file-like object. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a file-like object. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for file-like objects.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "<file-like object>" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { none() }
|
||||
|
||||
override string getMethodName() { result in ["read", "readline", "readlines"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra taint propagation for file-like objects, not covered by `InstanceTaintSteps`.",
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// taint-propagation back to instance from `foo.write(tainted_data)`
|
||||
exists(DataFlow::AttrRead write, DataFlow::CallCfgNode call, DataFlow::Node instance_ |
|
||||
instance_ = instance() and
|
||||
write.accesses(instance_, "write")
|
||||
|
|
||||
nodeTo.(DataFlow::PostUpdateNode).getPreUpdateNode() = instance_ and
|
||||
call.getFunction() = write and
|
||||
nodeFrom = call.getArg(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `http.client.HTTPMessage` class
|
||||
*
|
||||
* Has no official docs, but see
|
||||
* https://github.com/python/cpython/blob/64f54b7ccd49764b0304e076bfd79b5482988f53/Lib/http/client.py#L175
|
||||
* and https://docs.python.org/3.9/library/email.compat32-message.html#email.message.Message
|
||||
*/
|
||||
module HTTPMessage {
|
||||
/**
|
||||
* A source of instances of `http.client.HTTPMessage`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `HTTPMessage::instance()` to get references to instances of `http.client.HTTPMessage`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `http.client.HTTPMessage`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `http.client.HTTPMessage`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `http.client.HTTPMessage`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "http.client.HTTPMessage" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { none() }
|
||||
|
||||
override string getMethodName() { result in ["get_all", "as_bytes", "as_string", "keys"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `http.cookies.Morsel` class
|
||||
*
|
||||
* See https://docs.python.org/3.9/library/http.cookies.html#http.cookies.Morsel.
|
||||
*/
|
||||
module Morsel {
|
||||
/**
|
||||
* A source of instances of `http.cookies.Morsel`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `Morsel::instance()` to get references to instances of `http.cookies.Morsel`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `http.cookies.Morsel`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `http.cookies.Morsel`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `http.cookies.Morsel`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "http.cookies.Morsel" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { result in ["key", "value", "coded_value"] }
|
||||
|
||||
override string getMethodName() { result in ["output", "js_output"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the Python standard library.
|
||||
*
|
||||
* This module is marked private as exposing it means committing to 1-year deprecation
|
||||
* policy, and the code is not in a polished enough state that we want to do so -- at
|
||||
* least not without having convincing use-cases for it :)
|
||||
*/
|
||||
private module StdlibPrivate {
|
||||
// ---------------------------------------------------------------------------
|
||||
// os
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -395,7 +559,8 @@ private module Stdlib {
|
||||
* A call to the builtin `open` function.
|
||||
* See https://docs.python.org/3/library/functions.html#open
|
||||
*/
|
||||
private class OpenCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
|
||||
private class OpenCall extends FileSystemAccess::Range, Stdlib::FileLikeObject::InstanceSource,
|
||||
DataFlow::CallCfgNode {
|
||||
OpenCall() { this = getOpenFunctionRef().getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
@@ -911,6 +1076,20 @@ private module Stdlib {
|
||||
}
|
||||
}
|
||||
|
||||
/** An `HTTPMessage` instance that originates from a `BaseHTTPRequestHandler` instance. */
|
||||
private class BaseHTTPRequestHandlerHeadersInstances extends Stdlib::HTTPMessage::InstanceSource {
|
||||
BaseHTTPRequestHandlerHeadersInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(instance(), "headers")
|
||||
}
|
||||
}
|
||||
|
||||
/** A file-like object that originates from a `BaseHTTPRequestHandler` instance. */
|
||||
private class BaseHTTPRequestHandlerFileLikeObjectInstances extends Stdlib::FileLikeObject::InstanceSource {
|
||||
BaseHTTPRequestHandlerFileLikeObjectInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(instance(), "rfile")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry-point for handling a request with a `BaseHTTPRequestHandler` subclass.
|
||||
*
|
||||
@@ -1081,7 +1260,7 @@ private module Stdlib {
|
||||
}
|
||||
|
||||
/** A call to the `open` method on a `pathlib.Path` instance. */
|
||||
private class PathLibOpenCall extends PathlibFileAccess {
|
||||
private class PathLibOpenCall extends PathlibFileAccess, Stdlib::FileLikeObject::InstanceSource {
|
||||
PathLibOpenCall() { attrbuteName = "open" }
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,58 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.regex
|
||||
private import semmle.python.frameworks.Stdlib
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* Provides models for the `tornado` PyPI package.
|
||||
* See https://www.tornadoweb.org/en/stable/.
|
||||
*/
|
||||
private module Tornado {
|
||||
/**
|
||||
* Provides models for the `tornado.httputil.HTTPHeaders` class
|
||||
*
|
||||
* See https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders.
|
||||
*/
|
||||
module HTTPHeaders {
|
||||
/**
|
||||
* A source of instances of `tornado.httputil.HTTPHeaders`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `HTTPHeaders::instance()` to get references to instances of `tornado.httputil.HTTPHeaders`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `tornado.httputil.HTTPHeaders`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `tornado.httputil.HTTPHeaders`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `tornado.httputil.HTTPHeaders`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "tornado.httputil.HTTPHeaders" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { none() }
|
||||
|
||||
override string getMethodName() { result in ["get_list", "get_all"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// tornado
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -97,32 +143,6 @@ private module Tornado {
|
||||
/** Gets a reference to an instance of the `tornado.web.RequestHandler` class or any subclass. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** Gets a reference to one of the methods `get_argument`, `get_body_argument`, `get_query_argument`. */
|
||||
private DataFlow::TypeTrackingNode argumentMethod(DataFlow::TypeTracker t) {
|
||||
t.startInAttr(["get_argument", "get_body_argument", "get_query_argument"]) and
|
||||
result = instance()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = argumentMethod(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to one of the methods `get_argument`, `get_body_argument`, `get_query_argument`. */
|
||||
DataFlow::Node argumentMethod() {
|
||||
argumentMethod(DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
}
|
||||
|
||||
/** Gets a reference to one of the methods `get_arguments`, `get_body_arguments`, `get_query_arguments`. */
|
||||
private DataFlow::TypeTrackingNode argumentsMethod(DataFlow::TypeTracker t) {
|
||||
t.startInAttr(["get_arguments", "get_body_arguments", "get_query_arguments"]) and
|
||||
result = instance()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = argumentsMethod(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to one of the methods `get_arguments`, `get_body_arguments`, `get_query_arguments`. */
|
||||
DataFlow::Node argumentsMethod() {
|
||||
argumentsMethod(DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
}
|
||||
|
||||
/** Gets a reference the `redirect` method. */
|
||||
private DataFlow::TypeTrackingNode redirectMethod(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("redirect") and
|
||||
@@ -147,30 +167,33 @@ private module Tornado {
|
||||
/** Gets a reference to the `write` method. */
|
||||
DataFlow::Node writeMethod() { writeMethod(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Method access
|
||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
||||
nodeFrom = instance() and
|
||||
nodeTo in [argumentMethod(), argumentsMethod()]
|
||||
or
|
||||
// Method call
|
||||
nodeTo.asCfgNode().(CallNode).getFunction() = nodeFrom.asCfgNode() and
|
||||
nodeFrom in [argumentMethod(), argumentsMethod()]
|
||||
or
|
||||
// Attributes
|
||||
nodeFrom = instance() and
|
||||
exists(DataFlow::AttrRead read | nodeTo = read and read.getObject() = nodeFrom |
|
||||
read.getAttributeName() in [
|
||||
// List[str]
|
||||
"path_args",
|
||||
// Dict[str, str]
|
||||
"path_kwargs",
|
||||
// tornado.httputil.HTTPServerRequest
|
||||
"request"
|
||||
]
|
||||
)
|
||||
/**
|
||||
* Taint propagation for `tornado.web.RequestHandler`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "tornado.web.RequestHandler" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
// List[str]
|
||||
"path_args",
|
||||
// Dict[str, str]
|
||||
"path_kwargs",
|
||||
// tornado.httputil.HTTPServerRequest
|
||||
"request"
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() {
|
||||
result in [
|
||||
"get_argument", "get_body_argument", "get_query_argument", "get_arguments",
|
||||
"get_body_arguments", "get_query_arguments"
|
||||
]
|
||||
}
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
private class RequestAttrAccess extends tornado::httputil::HttpServerRequest::InstanceSource {
|
||||
@@ -274,41 +297,53 @@ private module Tornado {
|
||||
/** Gets a reference to an instance of `tornado.httputil.HttpServerRequest`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** Gets a reference to the `full_url` method. */
|
||||
private DataFlow::TypeTrackingNode full_url(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("full_url") and
|
||||
result = instance()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = full_url(t2).track(t2, t))
|
||||
/**
|
||||
* Taint propagation for `tornado.httputil.HttpServerRequest`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "tornado.httputil.HttpServerRequest" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
// str / bytes
|
||||
"uri", "path", "query", "remote_ip", "body",
|
||||
// Dict[str, List[bytes]]
|
||||
"arguments", "query_arguments", "body_arguments",
|
||||
// dict-like, https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders
|
||||
"headers",
|
||||
// Dict[str, http.cookies.Morsel]
|
||||
"cookies"
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() { result in ["full_url"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/** Gets a reference to the `full_url` method. */
|
||||
DataFlow::Node full_url() { full_url(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
/** An `HTTPHeaders` instance that originates from a Tornado request. */
|
||||
private class TornadoRequestHTTPHeadersInstances extends HTTPHeaders::InstanceSource {
|
||||
TornadoRequestHTTPHeadersInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(instance(), "headers")
|
||||
}
|
||||
}
|
||||
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Method access
|
||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
||||
nodeFrom = instance() and
|
||||
nodeTo in [full_url()]
|
||||
or
|
||||
// Method call
|
||||
nodeTo.asCfgNode().(CallNode).getFunction() = nodeFrom.asCfgNode() and
|
||||
nodeFrom in [full_url()]
|
||||
or
|
||||
// Attributes
|
||||
nodeFrom = instance() and
|
||||
exists(DataFlow::AttrRead read | nodeTo = read and read.getObject() = nodeFrom |
|
||||
read.getAttributeName() in [
|
||||
// str / bytes
|
||||
"uri", "path", "query", "remote_ip", "body",
|
||||
// Dict[str, List[bytes]]
|
||||
"arguments", "query_arguments", "body_arguments",
|
||||
// dict-like, https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders
|
||||
"headers",
|
||||
// Dict[str, http.cookies.Morsel]
|
||||
"cookies"
|
||||
]
|
||||
/** An `Morsel` instance that originates from a Tornado request. */
|
||||
private class TornadoRequestMorselInstances extends Stdlib::Morsel::InstanceSource {
|
||||
TornadoRequestMorselInstances() {
|
||||
// TODO: this currently only works in local-scope, since writing type-trackers for
|
||||
// this is a little too much effort. Once API-graphs are available for more
|
||||
// things, we can rewrite this.
|
||||
//
|
||||
// TODO: This approach for identifying member-access is very adhoc, and we should
|
||||
// be able to do something more structured for providing modeling of the members
|
||||
// of a container-object.
|
||||
exists(DataFlow::AttrRead files | files.accesses(instance(), "cookies") |
|
||||
this.asCfgNode().(SubscriptNode).getObject() = files.asCfgNode()
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(files, "get")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* Provides models for the `twisted` PyPI package.
|
||||
@@ -110,6 +111,31 @@ private module Twisted {
|
||||
|
||||
/** Gets a reference to an instance of `twisted.web.server.Request`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `twisted.web.server.Request`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "twisted.web.server.Request" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
"uri", "path", "prepath", "postpath", "content", "args", "received_cookies",
|
||||
"requestHeaders", "user", "password", "host"
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() {
|
||||
result in [
|
||||
"getCookie", "getHeader", "getAllHeaders", "getUser", "getPassword", "getHost",
|
||||
"getRequestHostname"
|
||||
]
|
||||
}
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,36 +151,6 @@ private module Twisted {
|
||||
override string getSourceType() { result = "twisted.web.server.Request" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint propagation for `twisted.web.server.Request`.
|
||||
*/
|
||||
private class TwistedRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Methods
|
||||
//
|
||||
// TODO: When we have tools that make it easy, model these properly to handle
|
||||
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
|
||||
// (since it allows us to at least capture the most common cases).
|
||||
nodeFrom = Request::instance() and
|
||||
exists(DataFlow::AttrRead attr | attr.getObject() = nodeFrom |
|
||||
// normal (non-async) methods
|
||||
attr.getAttributeName() in [
|
||||
"getCookie", "getHeader", "getAllHeaders", "getUser", "getPassword", "getHost",
|
||||
"getRequestHostname"
|
||||
] and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
|
||||
)
|
||||
or
|
||||
// Attributes
|
||||
nodeFrom = Request::instance() and
|
||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
||||
"uri", "path", "prepath", "postpath", "content", "args", "received_cookies",
|
||||
"requestHeaders", "user", "password", "host"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter of a request handler method (on a `twisted.web.resource.Resource` subclass)
|
||||
* that is also given remote user input. (a bit like RoutedParameter).
|
||||
@@ -198,17 +194,8 @@ private module Twisted {
|
||||
*
|
||||
* See https://twistedmatrix.com/documents/21.2.0/api/twisted.web.server.Request.html#write
|
||||
*/
|
||||
class TwistedRequestWriteCall extends HTTP::Server::HttpResponse::Range, DataFlow::CallCfgNode {
|
||||
TwistedRequestWriteCall() {
|
||||
// TODO: When we have tools that make it easy, model these properly to handle
|
||||
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
|
||||
// (since it allows us to at least capture the most common cases).
|
||||
exists(DataFlow::AttrRead read |
|
||||
this.getFunction() = read and
|
||||
read.getObject() = Request::instance() and
|
||||
read.getAttributeName() = "write"
|
||||
)
|
||||
}
|
||||
class TwistedRequestWriteCall extends HTTP::Server::HttpResponse::Range, DataFlow::MethodCallNode {
|
||||
TwistedRequestWriteCall() { this.calls(Request::instance(), "write") }
|
||||
|
||||
override DataFlow::Node getBody() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("data")]
|
||||
@@ -225,17 +212,8 @@ private module Twisted {
|
||||
* See https://twistedmatrix.com/documents/21.2.0/api/twisted.web.http.Request.html#redirect
|
||||
*/
|
||||
class TwistedRequestRedirectCall extends HTTP::Server::HttpRedirectResponse::Range,
|
||||
DataFlow::CallCfgNode {
|
||||
TwistedRequestRedirectCall() {
|
||||
// TODO: When we have tools that make it easy, model these properly to handle
|
||||
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
|
||||
// (since it allows us to at least capture the most common cases).
|
||||
exists(DataFlow::AttrRead read |
|
||||
this.getFunction() = read and
|
||||
read.getObject() = Request::instance() and
|
||||
read.getAttributeName() = "redirect"
|
||||
)
|
||||
}
|
||||
DataFlow::MethodCallNode {
|
||||
TwistedRequestRedirectCall() { this.calls(Request::instance(), "redirect") }
|
||||
|
||||
override DataFlow::Node getBody() { none() }
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.Stdlib
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* Provides models for the `Werkzeug` PyPI package.
|
||||
@@ -17,86 +20,92 @@ private import semmle.python.ApiGraphs
|
||||
* - https://werkzeug.palletsprojects.com/en/1.0.x/#werkzeug
|
||||
*/
|
||||
module Werkzeug {
|
||||
/** Provides models for the `werkzeug` module. */
|
||||
module werkzeug {
|
||||
/** Provides models for the `werkzeug.datastructures` module. */
|
||||
module datastructures {
|
||||
/**
|
||||
* Provides models for the `werkzeug.datastructures.MultiDict` class
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict.
|
||||
*/
|
||||
module MultiDict {
|
||||
/** DEPRECATED. Use `InstanceSourceApiNode` instead. */
|
||||
abstract deprecated class InstanceSource extends DataFlow::Node { }
|
||||
/**
|
||||
* Provides models for the `werkzeug.datastructures.MultiDict` class
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict.
|
||||
*/
|
||||
module MultiDict {
|
||||
/**
|
||||
* A source of instances of `werkzeug.datastructures.MultiDict`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `MultiDict::instance()` to get references to instances of `werkzeug.datastructures.MultiDict`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/**
|
||||
* A source of instances of `werkzeug.datastructures.MultiDict`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `MultiDict::instance()` to get references to instances of `werkzeug.datastructures.MultiDict`.
|
||||
*/
|
||||
abstract class InstanceSourceApiNode extends API::Node { }
|
||||
|
||||
/**
|
||||
* Gets a reference to the `getlist` method on an instance of `werkzeug.datastructures.MultiDict`.
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers.getlist
|
||||
*/
|
||||
DataFlow::Node getlist() {
|
||||
result = any(InstanceSourceApiNode a).getMember("getlist").getAUse()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `werkzeug.datastructures.FileStorage` class
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.
|
||||
*/
|
||||
module FileStorage {
|
||||
/** DEPRECATED. Use `InstanceSourceApiNode` instead. */
|
||||
abstract deprecated class InstanceSource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A source of instances of `werkzeug.datastructures.FileStorage`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `FileStorage::instance()` to get references to instances of `werkzeug.datastructures.FileStorage`.
|
||||
*/
|
||||
abstract class InstanceSourceApiNode extends API::Node { }
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.FileStorage`. */
|
||||
DataFlow::Node instance() { result = any(InstanceSourceApiNode a).getAUse() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MultiDictAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// obj -> obj.getlist
|
||||
exists(DataFlow::AttrRead read |
|
||||
read.getObject() = nodeFrom and
|
||||
nodeTo = read and
|
||||
nodeTo = werkzeug::datastructures::MultiDict::getlist()
|
||||
)
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.MultiDict`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
// getlist -> getlist()
|
||||
nodeFrom = werkzeug::datastructures::MultiDict::getlist() and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = nodeFrom
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.MultiDict`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `werkzeug.datastructures.MultiDict`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "werkzeug.datastructures.MultiDict" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { none() }
|
||||
|
||||
override string getMethodName() { result in ["getlist"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
private class FileStorageAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
nodeFrom = werkzeug::datastructures::FileStorage::instance() and
|
||||
exists(DataFlow::AttrRead read | nodeTo = read |
|
||||
read.getAttributeName() in [
|
||||
/**
|
||||
* Provides models for the `werkzeug.datastructures.FileStorage` class
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.
|
||||
*/
|
||||
module FileStorage {
|
||||
/**
|
||||
* A source of instances of `werkzeug.datastructures.FileStorage`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `FileStorage::instance()` to get references to instances of `werkzeug.datastructures.FileStorage`.
|
||||
*/
|
||||
// All the attributes of the wrapper stream are proxied by the file storage so it’s
|
||||
// possible to do storage.read() instead of the long form storage.stream.read(). So
|
||||
// that's why InstanceSource also extends `Stdlib::FileLikeObject::InstanceSource`
|
||||
abstract class InstanceSource extends Stdlib::FileLikeObject::InstanceSource,
|
||||
DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.FileStorage`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.FileStorage`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `werkzeug.datastructures.FileStorage`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "werkzeug.datastructures.FileStorage" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
// str
|
||||
"filename", "name", "content_type", "mimetype",
|
||||
// file-like
|
||||
@@ -105,9 +114,244 @@ module Werkzeug {
|
||||
"headers",
|
||||
// dict[str, str]
|
||||
"mimetype_params"
|
||||
] and
|
||||
read.getObject() = nodeFrom
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() { none() }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/** A file-like object instance that originates from a `FileStorage`. */
|
||||
private class FileStorageFileLikeInstances extends Stdlib::FileLikeObject::InstanceSource {
|
||||
FileStorageFileLikeInstances() { this.(DataFlow::AttrRead).accesses(instance(), "stream") }
|
||||
}
|
||||
|
||||
/** A call to the `save` method of a `FileStorage`. */
|
||||
private class FileStorageSaveCall extends FileSystemAccess::Range, DataFlow::MethodCallNode {
|
||||
FileStorageSaveCall() { this.calls(instance(), "save") }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result in [this.getArg(0), this.getArgByName("dst")]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `werkzeug.datastructures.Headers` class
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers.
|
||||
*/
|
||||
module Headers {
|
||||
/**
|
||||
* A source of instances of `werkzeug.datastructures.Headers`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `Headers::instance()` to get references to instances of `werkzeug.datastructures.Headers`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.Headers`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.Headers`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `werkzeug.datastructures.Headers`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "werkzeug.datastructures.Headers" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() { none() }
|
||||
|
||||
override string getMethodName() {
|
||||
result in ["getlist", "get_all", "popitem", "to_wsgi_list"]
|
||||
}
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `werkzeug.datastructures.Authorization` class
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization.
|
||||
*/
|
||||
module Authorization {
|
||||
/**
|
||||
* A source of instances of `werkzeug.datastructures.Authorization`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `Authorization::instance()` to get references to instances of `werkzeug.datastructures.Authorization`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.Authorization`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.Authorization`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* Taint propagation for `werkzeug.datastructures.Authorization`.
|
||||
*/
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "werkzeug.datastructures.Authorization" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
"username", "password", "realm", "nonce", "uri", "nc", "cnonce", "response", "opaque",
|
||||
"qop"
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() { none() }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
import WerkzeugOld
|
||||
}
|
||||
|
||||
/**
|
||||
* Old version that contains the deprecated modules.
|
||||
*/
|
||||
private module WerkzeugOld {
|
||||
/**
|
||||
* DEPRECATED: Use the modeling available directly in the `Werkzeug` module instead.
|
||||
*
|
||||
* Provides models for the `werkzeug` module.
|
||||
*/
|
||||
deprecated module werkzeug {
|
||||
/**
|
||||
* DEPRECATED: Use the modeling available directly in the `Werkzeug` module instead.
|
||||
*
|
||||
* Provides models for the `werkzeug.datastructures` module.
|
||||
*/
|
||||
deprecated module datastructures {
|
||||
/**
|
||||
* DEPRECATED: Use `Werkzeug::MultiDict` instead.
|
||||
*
|
||||
* Provides models for the `werkzeug.datastructures.MultiDict` class
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict.
|
||||
*/
|
||||
deprecated module MultiDict {
|
||||
/**
|
||||
* DEPRECATED. Use `Werkzeug::MultiDict::InstanceSource` instead.
|
||||
*/
|
||||
abstract deprecated class InstanceSource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `Werkzeug::MultiDict::InstanceSource` instead.
|
||||
*
|
||||
* A source of instances of `werkzeug.datastructures.MultiDict`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `MultiDict::instance()` to get references to instances of `werkzeug.datastructures.MultiDict`.
|
||||
*/
|
||||
abstract deprecated class InstanceSourceApiNode extends API::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
*
|
||||
* Gets a reference to the `getlist` method on an instance of `werkzeug.datastructures.MultiDict`.
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers.getlist
|
||||
*/
|
||||
deprecated DataFlow::Node getlist() {
|
||||
result = any(InstanceSourceApiNode a).getMember("getlist").getAUse()
|
||||
}
|
||||
|
||||
private class MultiDictAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// obj -> obj.getlist
|
||||
exists(DataFlow::AttrRead read |
|
||||
read.getObject() = nodeFrom and
|
||||
nodeTo = read and
|
||||
nodeTo = getlist()
|
||||
)
|
||||
or
|
||||
// getlist -> getlist()
|
||||
nodeFrom = getlist() and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = nodeFrom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Werkzeug::FileStorage` instead.
|
||||
*
|
||||
* Provides models for the `werkzeug.datastructures.FileStorage` class
|
||||
*
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.
|
||||
*/
|
||||
deprecated module FileStorage {
|
||||
/**
|
||||
* DEPRECATED. Use `Werkzeug::FileStorage::InstanceSource` instead.
|
||||
*/
|
||||
abstract deprecated class InstanceSource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `Werkzeug::FileStorage::InstanceSource` instead.
|
||||
*
|
||||
* A source of instances of `werkzeug.datastructures.FileStorage`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `FileStorage::instance()` to get references to instances of `werkzeug.datastructures.FileStorage`.
|
||||
*/
|
||||
abstract deprecated class InstanceSourceApiNode extends API::Node { }
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.FileStorage`. */
|
||||
deprecated DataFlow::Node instance() { result = any(InstanceSourceApiNode a).getAUse() }
|
||||
|
||||
private class FileStorageAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
nodeFrom = instance() and
|
||||
exists(DataFlow::AttrRead read | nodeTo = read |
|
||||
read.getAttributeName() in [
|
||||
// str
|
||||
"filename", "name", "content_type", "mimetype",
|
||||
// file-like
|
||||
"stream",
|
||||
// TODO: werkzeug.datastructures.Headers
|
||||
"headers",
|
||||
// dict[str, str]
|
||||
"mimetype_params"
|
||||
] and
|
||||
read.getObject() = nodeFrom
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.Multidict
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -52,10 +53,30 @@ module Yarl {
|
||||
|
||||
/**
|
||||
* Taint propagation for `yarl.URL`.
|
||||
*
|
||||
* See https://yarl.readthedocs.io/en/stable/api.html#yarl.URL
|
||||
*/
|
||||
class YarlUrlAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "yarl.URL" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = instance() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
"user", "raw_user", "password", "raw_password", "host", "raw_host", "port",
|
||||
"explicit_port", "authority", "raw_authority", "path", "raw_path", "path_qs",
|
||||
"raw_path_qs", "query_string", "raw_query_string", "fragment", "raw_fragment", "parts",
|
||||
"raw_parts", "name", "raw_name", "query"
|
||||
]
|
||||
}
|
||||
|
||||
override string getMethodName() { result in ["human_repr"] }
|
||||
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra taint propagation for `yarl.URL`, not covered by `InstanceTaintSteps`.
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// class instantiation
|
||||
exists(ClassInstantiation call |
|
||||
@@ -63,51 +84,20 @@ module Yarl {
|
||||
nodeTo = call
|
||||
)
|
||||
or
|
||||
// Methods
|
||||
//
|
||||
// TODO: When we have tools that make it easy, model these properly to handle
|
||||
// `meth = obj.meth; meth()`. Until then, we'll use this more syntactic approach
|
||||
// (since it allows us to at least capture the most common cases).
|
||||
exists(DataFlow::AttrRead attr |
|
||||
// methods (that replaces part of URL, taken as only arguments)
|
||||
attr.getAttributeName() in [
|
||||
// methods that give an altered URL. taint both from object, and form argument
|
||||
// (to result of call)
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.calls(instance(),
|
||||
[
|
||||
"with_scheme", "with_user", "with_password", "with_host", "with_port", "with_path",
|
||||
"with_query", "with_query", "update_query", "update_query", "with_fragment",
|
||||
"with_name",
|
||||
// join is a bit different, but is still correct to add here :+1:
|
||||
"join"
|
||||
] and
|
||||
(
|
||||
// obj -> obj.meth()
|
||||
nodeFrom = instance() and
|
||||
attr.getObject() = nodeFrom and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
|
||||
or
|
||||
// argument of obj.meth() -> obj.meth()
|
||||
attr.getObject() = instance() and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr and
|
||||
nodeFrom in [
|
||||
nodeTo.(DataFlow::CallCfgNode).getArg(_),
|
||||
nodeTo.(DataFlow::CallCfgNode).getArgByName(_)
|
||||
]
|
||||
)
|
||||
or
|
||||
// other methods
|
||||
nodeFrom = instance() and
|
||||
attr.getObject() = nodeFrom and
|
||||
attr.getAttributeName() in ["human_repr"] and
|
||||
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr
|
||||
]) and
|
||||
nodeTo = call and
|
||||
nodeFrom in [call.getObject(), call.getArg(_), call.getArgByName(_)]
|
||||
)
|
||||
or
|
||||
// Attributes
|
||||
nodeFrom = instance() and
|
||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
|
||||
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
|
||||
"user", "raw_user", "password", "raw_password", "host", "raw_host", "port",
|
||||
"explicit_port", "authority", "raw_authority", "path", "raw_path", "path_qs",
|
||||
"raw_path_qs", "query_string", "raw_query_string", "fragment", "raw_fragment", "parts",
|
||||
"raw_parts", "name", "raw_name", "query"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* INTERNAL: Do no use.
|
||||
*
|
||||
* Provides helper class for defining additional taint step.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/**
|
||||
* A helper class for defining additional taint steps.
|
||||
*/
|
||||
bindingset[this]
|
||||
abstract class InstanceTaintStepsHelper extends string {
|
||||
/** Gets an instance that the additional taint steps should be applied to. */
|
||||
abstract DataFlow::Node getInstance();
|
||||
|
||||
/** Gets the name of an attribute that should be tainted. */
|
||||
abstract string getAttributeName();
|
||||
|
||||
/** Gets the name of a method, whose results should be tainted. */
|
||||
abstract string getMethodName();
|
||||
|
||||
/** Gets the name of an async method, whose results should be tainted. */
|
||||
abstract string getAsyncMethodName();
|
||||
}
|
||||
|
||||
private class InstanceAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(InstanceTaintStepsHelper helper |
|
||||
// normal (non-async) methods
|
||||
nodeFrom = helper.getInstance() and
|
||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, helper.getMethodName())
|
||||
or
|
||||
// async methods.
|
||||
//
|
||||
// since we have general taint-step from `foo` in `await foo` to the whole
|
||||
// expression, we simply taint the awaitable that is the result of "calling" the
|
||||
// async method. That also allows such an awaitable to be placed in a list (for
|
||||
// use with `asyncio.gather` for example), and thereby propagate taint to the
|
||||
// list.
|
||||
nodeFrom = helper.getInstance() and
|
||||
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, helper.getAsyncMethodName())
|
||||
or
|
||||
// Attributes
|
||||
nodeFrom = helper.getInstance() and
|
||||
nodeTo.(DataFlow::AttrRead).accesses(nodeFrom, helper.getAttributeName())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -369,12 +369,12 @@ abstract class RegexString extends Expr {
|
||||
// hex value \xhh
|
||||
this.getChar(start + 1) = "x" and end = start + 4
|
||||
or
|
||||
// octal value \ooo
|
||||
// octal value \o, \oo, or \ooo
|
||||
end in [start + 2 .. start + 4] and
|
||||
this.getText().substring(start + 1, end).toInt() >= 0 and
|
||||
forall(int i | i in [start + 1 .. end - 1] | this.isOctal(i)) and
|
||||
not (
|
||||
end < start + 4 and
|
||||
exists(this.getText().substring(start + 1, end + 1).toInt())
|
||||
this.isOctal(end)
|
||||
)
|
||||
or
|
||||
// 16-bit hex value \uhhhh
|
||||
@@ -392,6 +392,9 @@ abstract class RegexString extends Expr {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate isOctal(int index) { this.getChar(index) = [0 .. 7].toString() }
|
||||
|
||||
/** Holds if `index` is inside a character set. */
|
||||
predicate inCharSet(int index) {
|
||||
exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2])
|
||||
@@ -690,6 +693,7 @@ abstract class RegexString extends Expr {
|
||||
|
||||
private predicate numbered_backreference(int start, int end, int value) {
|
||||
this.escapingChar(start) and
|
||||
// starting with 0 makes it an octal escape
|
||||
not this.getChar(start + 1) = "0" and
|
||||
exists(string text, string svalue, int len |
|
||||
end = start + len and
|
||||
@@ -698,8 +702,16 @@ abstract class RegexString extends Expr {
|
||||
|
|
||||
svalue = text.substring(start + 1, start + len) and
|
||||
value = svalue.toInt() and
|
||||
not exists(text.substring(start + 1, start + len + 1).toInt()) and
|
||||
value > 0
|
||||
// value is composed of digits
|
||||
forall(int i | i in [start + 1 .. start + len - 1] | this.getChar(i) = [0 .. 9].toString()) and
|
||||
// a longer reference is not possible
|
||||
not (
|
||||
len = 2 and
|
||||
exists(text.substring(start + 1, start + len + 1).toInt())
|
||||
) and
|
||||
// 3 octal digits makes it an octal escape
|
||||
not forall(int i | i in [start + 1 .. start + 4] | this.isOctal(i))
|
||||
// TODO: Inside a character set, all numeric escapes are treated as characters.
|
||||
)
|
||||
}
|
||||
|
||||
@@ -761,15 +773,18 @@ abstract class RegexString extends Expr {
|
||||
* string is empty.
|
||||
*/
|
||||
predicate multiples(int start, int end, string lower, string upper) {
|
||||
this.getChar(start) = "{" and
|
||||
this.getChar(end - 1) = "}" and
|
||||
exists(string inner | inner = this.getText().substring(start + 1, end - 1) |
|
||||
inner.regexpMatch("[0-9]+") and
|
||||
exists(string text, string match, string inner |
|
||||
text = this.getText() and
|
||||
end = start + match.length() and
|
||||
inner = match.substring(1, match.length() - 1)
|
||||
|
|
||||
match = text.regexpFind("\\{[0-9]+\\}", _, start) and
|
||||
lower = inner and
|
||||
upper = lower
|
||||
or
|
||||
inner.regexpMatch("[0-9]*,[0-9]*") and
|
||||
exists(int commaIndex | commaIndex = inner.indexOf(",") |
|
||||
match = text.regexpFind("\\{[0-9]*,[0-9]*\\}", _, start) and
|
||||
exists(int commaIndex |
|
||||
commaIndex = inner.indexOf(",") and
|
||||
lower = inner.prefix(commaIndex) and
|
||||
upper = inner.suffix(commaIndex + 1)
|
||||
)
|
||||
|
||||
@@ -164,6 +164,7 @@ class RelevantRegExpTerm extends RegExpTerm {
|
||||
|
||||
/**
|
||||
* Holds if `term` is the chosen canonical representative for all terms with string representation `str`.
|
||||
* The string representation includes which flags are used with the regular expression.
|
||||
*
|
||||
* Using canonical representatives gives a huge performance boost when working with tuples containing multiple `InputSymbol`s.
|
||||
* The number of `InputSymbol`s is decreased by 3 orders of magnitude or more in some larger benchmarks.
|
||||
@@ -173,26 +174,48 @@ private predicate isCanonicalTerm(RelevantRegExpTerm term, string str) {
|
||||
min(RelevantRegExpTerm t, Location loc, File file |
|
||||
loc = t.getLocation() and
|
||||
file = t.getFile() and
|
||||
str = t.getRawValue()
|
||||
str = t.getRawValue() + "|" + getCanonicalizationFlags(t.getRootTerm())
|
||||
|
|
||||
t order by t.getFile().getRelativePath(), loc.getStartLine(), loc.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string reperesentation of the flags used with the regular expression.
|
||||
* Only the flags that are relevant for the canonicalization are included.
|
||||
*/
|
||||
string getCanonicalizationFlags(RegExpTerm root) {
|
||||
root.isRootTerm() and
|
||||
(if RegExpFlags::isIgnoreCase(root) then result = "i" else result = "")
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract input symbol, representing a set of concrete characters.
|
||||
*/
|
||||
private newtype TInputSymbol =
|
||||
/** An input symbol corresponding to character `c`. */
|
||||
Char(string c) {
|
||||
c = any(RegexpCharacterConstant cc | cc instanceof RelevantRegExpTerm).getValue().charAt(_)
|
||||
c =
|
||||
any(RegexpCharacterConstant cc |
|
||||
cc instanceof RelevantRegExpTerm and
|
||||
not RegExpFlags::isIgnoreCase(cc.getRootTerm())
|
||||
).getValue().charAt(_)
|
||||
or
|
||||
// normalize everything to lower case if the regexp is case insensitive
|
||||
c =
|
||||
any(RegexpCharacterConstant cc, string char |
|
||||
cc instanceof RelevantRegExpTerm and
|
||||
RegExpFlags::isIgnoreCase(cc.getRootTerm()) and
|
||||
char = cc.getValue().charAt(_)
|
||||
|
|
||||
char.toLowerCase()
|
||||
)
|
||||
} or
|
||||
/**
|
||||
* An input symbol representing all characters matched by
|
||||
* a (non-universal) character class that has string representation `charClassString`.
|
||||
*/
|
||||
CharClass(string charClassString) {
|
||||
exists(RelevantRegExpTerm term | term.getRawValue() = charClassString) and
|
||||
exists(RelevantRegExpTerm recc | isCanonicalTerm(recc, charClassString) |
|
||||
recc instanceof RegExpCharacterClass and
|
||||
not recc.(RegExpCharacterClass).isUniversalClass()
|
||||
@@ -293,6 +316,19 @@ private module CharacterClasses {
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate hasChildThatMatches(RegExpCharacterClass cc, string char) {
|
||||
if RegExpFlags::isIgnoreCase(cc.getRootTerm())
|
||||
then
|
||||
// normalize everything to lower case if the regexp is case insensitive
|
||||
exists(string c | hasChildThatMatchesIgnoringCasingFlags(cc, c) | char = c.toLowerCase())
|
||||
else hasChildThatMatchesIgnoringCasingFlags(cc, char)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the character class `cc` has a child (constant or range) that matches `char`.
|
||||
* Ignores whether the character class is inside a regular expression that has the ignore case flag.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate hasChildThatMatchesIgnoringCasingFlags(RegExpCharacterClass cc, string char) {
|
||||
exists(getCanonicalCharClass(cc)) and
|
||||
exists(RegExpTerm child | child = cc.getAChild() |
|
||||
char = child.(RegexpCharacterConstant).getValue()
|
||||
@@ -537,7 +573,14 @@ private State after(RegExpTerm t) {
|
||||
predicate delta(State q1, EdgeLabel lbl, State q2) {
|
||||
exists(RegexpCharacterConstant s, int i |
|
||||
q1 = Match(s, i) and
|
||||
lbl = Char(s.getValue().charAt(i)) and
|
||||
(
|
||||
not RegExpFlags::isIgnoreCase(s.getRootTerm()) and
|
||||
lbl = Char(s.getValue().charAt(i))
|
||||
or
|
||||
// normalize everything to lower case if the regexp is case insensitive
|
||||
RegExpFlags::isIgnoreCase(s.getRootTerm()) and
|
||||
exists(string c | c = s.getValue().charAt(i) | lbl = Char(c.toLowerCase()))
|
||||
) and
|
||||
(
|
||||
q2 = Match(s, i + 1)
|
||||
or
|
||||
@@ -547,20 +590,20 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
|
||||
)
|
||||
or
|
||||
exists(RegExpDot dot | q1 = before(dot) and q2 = after(dot) |
|
||||
if dot.getLiteral().isDotAll() then lbl = Any() else lbl = Dot()
|
||||
if RegExpFlags::isDotAll(dot.getRootTerm()) then lbl = Any() else lbl = Dot()
|
||||
)
|
||||
or
|
||||
exists(RegExpCharacterClass cc |
|
||||
cc.isUniversalClass() and q1 = before(cc) and lbl = Any() and q2 = after(cc)
|
||||
or
|
||||
q1 = before(cc) and
|
||||
lbl = CharClass(cc.getRawValue()) and
|
||||
lbl = CharClass(cc.getRawValue() + "|" + getCanonicalizationFlags(cc.getRootTerm())) and
|
||||
q2 = after(cc)
|
||||
)
|
||||
or
|
||||
exists(RegExpCharacterClassEscape cc |
|
||||
q1 = before(cc) and
|
||||
lbl = CharClass(cc.getRawValue()) and
|
||||
lbl = CharClass(cc.getRawValue() + "|" + getCanonicalizationFlags(cc.getRootTerm())) and
|
||||
q2 = after(cc)
|
||||
)
|
||||
or
|
||||
@@ -627,13 +670,27 @@ RegExpRoot getRoot(RegExpTerm term) {
|
||||
result = getRoot(term.getParent())
|
||||
}
|
||||
|
||||
/**
|
||||
* A state in the NFA.
|
||||
*/
|
||||
private newtype TState =
|
||||
/**
|
||||
* A state representing that the NFA is about to match a term.
|
||||
* `i` is used to index into multi-char literals.
|
||||
*/
|
||||
Match(RelevantRegExpTerm t, int i) {
|
||||
i = 0
|
||||
or
|
||||
exists(t.(RegexpCharacterConstant).getValue().charAt(i))
|
||||
} or
|
||||
/**
|
||||
* An accept state, where exactly the given input string is accepted.
|
||||
*/
|
||||
Accept(RegExpRoot l) { l.isRelevant() } or
|
||||
/**
|
||||
* An accept state, where the given input string, or any string that has this
|
||||
* string as a prefix, is accepted.
|
||||
*/
|
||||
AcceptAnySuffix(RegExpRoot l) { l.isRelevant() }
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,3 +18,32 @@ predicate isExcluded(RegExpParent parent) {
|
||||
// we explicitly exclude these.
|
||||
count(int i | exists(parent.getRegex().getText().regexpFind("\\.\\*", i, _)) | i) > 10
|
||||
}
|
||||
|
||||
/**
|
||||
* A module containing predicates for determining which flags a regular expression have.
|
||||
*/
|
||||
module RegExpFlags {
|
||||
/**
|
||||
* Holds if `root` has the `i` flag for case-insensitive matching.
|
||||
*/
|
||||
predicate isIgnoreCase(RegExpTerm root) {
|
||||
root.isRootTerm() and
|
||||
root.getLiteral().isIgnoreCase()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the flags for `root`, or the empty string if `root` has no flags.
|
||||
*/
|
||||
string getFlags(RegExpTerm root) {
|
||||
root.isRootTerm() and
|
||||
result = root.getLiteral().getFlags()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `root` has the `s` flag for multi-line matching.
|
||||
*/
|
||||
predicate isDotAll(RegExpTerm root) {
|
||||
root.isRootTerm() and
|
||||
root.getLiteral().isDotAll()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,16 +120,11 @@ svnchurn(
|
||||
Python dbscheme
|
||||
****************************/
|
||||
|
||||
/* fromSource is ignored */
|
||||
files(unique int id: @file,
|
||||
varchar(900) name: string ref,
|
||||
varchar(900) simple: string ref,
|
||||
varchar(900) ext: string ref,
|
||||
int fromSource: int ref);
|
||||
varchar(900) name: string ref);
|
||||
|
||||
folders(unique int id: @folder,
|
||||
varchar(900) name: string ref,
|
||||
varchar(900) simple: string ref);
|
||||
varchar(900) name: string ref);
|
||||
|
||||
@container = @folder | @file;
|
||||
|
||||
|
||||
@@ -4331,18 +4331,6 @@
|
||||
<k>name</k>
|
||||
<v>3066</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>simple</k>
|
||||
<v>1294</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>ext</k>
|
||||
<v>1</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>fromSource</k>
|
||||
<v>1</v>
|
||||
</e>
|
||||
</columnsizes>
|
||||
<dependencies>
|
||||
<dep>
|
||||
@@ -4362,54 +4350,6 @@
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>id</src>
|
||||
<trg>simple</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>3066</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>id</src>
|
||||
<trg>ext</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>3066</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>id</src>
|
||||
<trg>fromSource</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>3066</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>name</src>
|
||||
<trg>id</trg>
|
||||
<val>
|
||||
@@ -4425,276 +4365,6 @@
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>name</src>
|
||||
<trg>simple</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>3066</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>name</src>
|
||||
<trg>ext</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>3066</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>name</src>
|
||||
<trg>fromSource</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>3066</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>simple</src>
|
||||
<trg>id</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>1058</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>2</a>
|
||||
<b>3</b>
|
||||
<v>132</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>3</a>
|
||||
<b>38</b>
|
||||
<v>98</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>47</a>
|
||||
<b>646</b>
|
||||
<v>6</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>simple</src>
|
||||
<trg>name</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>1058</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>2</a>
|
||||
<b>3</b>
|
||||
<v>132</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>3</a>
|
||||
<b>38</b>
|
||||
<v>98</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>47</a>
|
||||
<b>646</b>
|
||||
<v>6</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>simple</src>
|
||||
<trg>ext</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>1294</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>simple</src>
|
||||
<trg>fromSource</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>1294</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>ext</src>
|
||||
<trg>id</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>3066</a>
|
||||
<b>3067</b>
|
||||
<v>1</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>ext</src>
|
||||
<trg>name</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>3066</a>
|
||||
<b>3067</b>
|
||||
<v>1</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>ext</src>
|
||||
<trg>simple</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1294</a>
|
||||
<b>1295</b>
|
||||
<v>1</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>ext</src>
|
||||
<trg>fromSource</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>1</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>fromSource</src>
|
||||
<trg>id</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>3066</a>
|
||||
<b>3067</b>
|
||||
<v>1</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>fromSource</src>
|
||||
<trg>name</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>3066</a>
|
||||
<b>3067</b>
|
||||
<v>1</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>fromSource</src>
|
||||
<trg>simple</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1294</a>
|
||||
<b>1295</b>
|
||||
<v>1</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>fromSource</src>
|
||||
<trg>ext</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>1</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
</dependencies>
|
||||
</relation>
|
||||
<relation>
|
||||
@@ -4709,10 +4379,6 @@
|
||||
<k>name</k>
|
||||
<v>686</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>simple</k>
|
||||
<v>538</v>
|
||||
</e>
|
||||
</columnsizes>
|
||||
<dependencies>
|
||||
<dep>
|
||||
@@ -4732,22 +4398,6 @@
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>id</src>
|
||||
<trg>simple</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>686</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>name</src>
|
||||
<trg>id</trg>
|
||||
<val>
|
||||
@@ -4763,74 +4413,6 @@
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>name</src>
|
||||
<trg>simple</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>686</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>simple</src>
|
||||
<trg>id</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>481</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>2</a>
|
||||
<b>4</b>
|
||||
<v>45</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>4</a>
|
||||
<b>27</b>
|
||||
<v>12</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
<dep>
|
||||
<src>simple</src>
|
||||
<trg>name</trg>
|
||||
<val>
|
||||
<hist>
|
||||
<budget>12</budget>
|
||||
<bs>
|
||||
<b>
|
||||
<a>1</a>
|
||||
<b>2</b>
|
||||
<v>481</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>2</a>
|
||||
<b>4</b>
|
||||
<v>45</v>
|
||||
</b>
|
||||
<b>
|
||||
<a>4</a>
|
||||
<b>27</b>
|
||||
<v>12</v>
|
||||
</b>
|
||||
</bs>
|
||||
</hist>
|
||||
</val>
|
||||
</dep>
|
||||
</dependencies>
|
||||
</relation>
|
||||
<relation>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @id py/weak-sensitive-data-hashing
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
* external/cwe/cwe-328
|
||||
* external/cwe/cwe-916
|
||||
*/
|
||||
|
||||
|
||||
@@ -30,5 +30,11 @@ predicate modification_of_locals(ControlFlowNode f) {
|
||||
}
|
||||
|
||||
from AstNode a, ControlFlowNode f
|
||||
where modification_of_locals(f) and a = f.getNode()
|
||||
where
|
||||
modification_of_locals(f) and
|
||||
a = f.getNode() and
|
||||
// in module level scope `locals() == globals()`
|
||||
// see https://docs.python.org/3/library/functions.html#locals
|
||||
// FP report in https://github.com/github/codeql/issues/6674
|
||||
not a.getScope() instanceof ModuleScope
|
||||
select a, "Modification of the locals() dictionary will have no effect on the local variables."
|
||||
|
||||
@@ -19,6 +19,7 @@ predicate unused_local(Name unused, LocalVariable v) {
|
||||
def.getVariable() = v and
|
||||
def.isUnused() and
|
||||
not exists(def.getARedef()) and
|
||||
not exists(annotation_without_assignment(v)) and
|
||||
def.isRelevant() and
|
||||
not v = any(Nonlocal n).getAVariable() and
|
||||
not exists(def.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and
|
||||
@@ -26,6 +27,17 @@ predicate unused_local(Name unused, LocalVariable v) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any annotation of the local variable `v` that does not also reassign its value.
|
||||
*
|
||||
* TODO: This predicate should not be needed. Rather, annotated "assignments" that do not actually
|
||||
* assign a value should not result in the creation of an SSA variable (which then goes unused).
|
||||
*/
|
||||
private AnnAssign annotation_without_assignment(LocalVariable v) {
|
||||
result.getTarget() = v.getAStore() and
|
||||
not exists(result.getValue())
|
||||
}
|
||||
|
||||
from Name unused, LocalVariable v
|
||||
where
|
||||
unused_local(unused, v) and
|
||||
|
||||
18
python/ql/src/meta/alerts/RemoteFlowSources.ql
Normal file
18
python/ql/src/meta/alerts/RemoteFlowSources.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Remote flow sources
|
||||
* @description Sources of remote user input.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id py/meta/alerts/remote-flow-sources
|
||||
* @tags meta
|
||||
* @precision very-low
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import meta.MetaMetrics
|
||||
|
||||
from RemoteFlowSource source
|
||||
where not source.getLocation().getFile() instanceof IgnoredFile
|
||||
select source, "RemoteFlowSource: " + source.getSourceType()
|
||||
55
python/ql/src/meta/alerts/RemoteFlowSourcesReach.ql
Normal file
55
python/ql/src/meta/alerts/RemoteFlowSourcesReach.ql
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @name Remote flow sources reach
|
||||
* @description Nodes that can be reached with taint tracking from sources of
|
||||
* remote user input.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id py/meta/alerts/remote-flow-sources-reach
|
||||
* @tags meta
|
||||
* @precision very-low
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import meta.MetaMetrics
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
class RemoteFlowSourceReach extends TaintTracking::Configuration {
|
||||
RemoteFlowSourceReach() { this = "RemoteFlowSourceReach" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
node instanceof RemoteFlowSource and
|
||||
not node.getLocation().getFile() instanceof IgnoredFile
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
not node.getLocation().getFile() instanceof IgnoredFile and
|
||||
(
|
||||
node instanceof RemoteFlowSource
|
||||
or
|
||||
this.isAdditionalFlowStep(_, node)
|
||||
) and
|
||||
// In september 2021 we changed how we do taint-propagation for method calls (mostly
|
||||
// relating to modeled frameworks/libraries). We used to do `obj -> obj.meth` and
|
||||
// `obj.meth -> obj.meth()` in two separate steps, and now do them in one
|
||||
// `obj -> obj.meth()`. To be able to compare the overall reach between these two
|
||||
// version, we don't want this query to alert us to the fact that we no longer taint
|
||||
// the node in the middle (since that is just noise).
|
||||
// see https://github.com/github/codeql/pull/6349
|
||||
//
|
||||
// We should be able to remove the following few lines of code once we don't care to
|
||||
// compare with the old (before September 2021) way of doing taint-propagation for
|
||||
// method calls.
|
||||
not exists(DataFlow::MethodCallNode c |
|
||||
node = c.getFunction() and
|
||||
this.isAdditionalFlowStep(c.getObject(), node) and
|
||||
this.isAdditionalFlowStep(node, c)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from RemoteFlowSourceReach cfg, DataFlow::Node reachable
|
||||
where cfg.hasFlow(_, reachable)
|
||||
select reachable, prettyNode(reachable)
|
||||
@@ -1,8 +1,8 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.0.2
|
||||
dependencies:
|
||||
codeql/python-all: ^0.0.2
|
||||
codeql/suite-helpers: ^0.0.2
|
||||
codeql/python-all: "*"
|
||||
codeql/suite-helpers: "*"
|
||||
suites: codeql-suites
|
||||
extractor: python
|
||||
defaultSuiteFile: codeql-suites/python-code-scanning.qls
|
||||
|
||||
@@ -74,12 +74,6 @@ def f():
|
||||
change_foo()
|
||||
sink(foo) #$ use=moduleImport("danger").getMember("SOURCE")
|
||||
|
||||
# Star imports
|
||||
|
||||
from unknown import * #$ use=moduleImport("unknown")
|
||||
|
||||
hello() #$ MISSING: use=moduleImport("unknown").getMember("hello").getReturn()
|
||||
|
||||
|
||||
# Subclasses
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Star imports
|
||||
|
||||
from unknown import * #$ use=moduleImport("unknown")
|
||||
|
||||
# Currently missing, as we do not consider `hello` to be a `LocalSourceNode`, since it has flow
|
||||
# going into it from its corresponding `GlobalSsaVariable`.
|
||||
hello() #$ MISSING: use=moduleImport("unknown").getMember("hello").getReturn()
|
||||
|
||||
# We don't want our analysis to think that either `non_module_member` or `outer_bar` can
|
||||
# come from `from unknown import *`
|
||||
non_module_member
|
||||
|
||||
outer_bar = 5
|
||||
outer_bar
|
||||
|
||||
def foo():
|
||||
world() #$ use=moduleImport("unknown").getMember("world").getReturn()
|
||||
bar = 5
|
||||
bar
|
||||
non_module_member
|
||||
print(bar) #$ use=moduleImport("builtins").getMember("print").getReturn()
|
||||
|
||||
def quux():
|
||||
global non_module_member
|
||||
non_module_member = 5
|
||||
|
||||
def func1():
|
||||
var() #$ use=moduleImport("unknown").getMember("var").getReturn()
|
||||
def func2():
|
||||
var = "FOO"
|
||||
|
||||
def func3():
|
||||
var2 = print #$ use=moduleImport("builtins").getMember("print")
|
||||
def func4():
|
||||
var2() #$ MISSING: use=moduleImport("builtins").getMember("print").getReturn()
|
||||
func4()
|
||||
@@ -0,0 +1,15 @@
|
||||
# Star imports in local scope
|
||||
|
||||
hello2()
|
||||
|
||||
def foo():
|
||||
from unknown2 import * #$ use=moduleImport("unknown2")
|
||||
world2() #$ use=moduleImport("unknown2").getMember("world2").getReturn()
|
||||
bar2 = 5
|
||||
bar2
|
||||
non_module_member2
|
||||
print(bar2) #$ use=moduleImport("builtins").getMember("print").getReturn()
|
||||
|
||||
def quux2():
|
||||
global non_module_member2
|
||||
non_module_member2 = 5
|
||||
@@ -1,7 +1,7 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import experimental.dataflow.TestUtil.PrintNode
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
abstract class FlowTest extends InlineExpectationsTest {
|
||||
bindingset[this]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import experimental.dataflow.TestUtil.PrintNode
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
/**
|
||||
* A routing test is designed to test that values are routed to the
|
||||
|
||||
@@ -66,9 +66,9 @@ def argument_passing(
|
||||
b,
|
||||
/,
|
||||
c,
|
||||
d=arg4,
|
||||
d=arg4, #$ arg4 func=argument_passing
|
||||
*,
|
||||
e=arg5,
|
||||
e=arg5, #$ arg5 func=argument_passing
|
||||
f,
|
||||
**g,
|
||||
):
|
||||
@@ -120,7 +120,7 @@ def test_multiple_kw_args():
|
||||
with_multiple_kw_args(**{"b": arg2}, **{"c": arg3}, **{"a": arg1}) #$ arg1 arg2 arg3 func=with_multiple_kw_args
|
||||
|
||||
|
||||
def with_default_arguments(a=arg1, b=arg2, c=arg3): # Need a mechanism to test default arguments
|
||||
def with_default_arguments(a=arg1, b=arg2, c=arg3): #$ arg1 arg2 arg3 func=with_default_arguments
|
||||
SINK1(a)
|
||||
SINK2(b)
|
||||
SINK3(c)
|
||||
|
||||
@@ -14,6 +14,8 @@ edges
|
||||
| argumentPassing.py:120:59:120:69 | ControlFlowNode for Dict [Dictionary element at key a] | argumentPassing.py:120:5:120:70 | KwUnpacked a |
|
||||
| argumentPassing.py:120:65:120:68 | ControlFlowNode for arg1 | argumentPassing.py:120:59:120:69 | ControlFlowNode for Dict [Dictionary element at key a] |
|
||||
| argumentPassing.py:123:28:123:28 | ControlFlowNode for a | argumentPassing.py:124:11:124:11 | ControlFlowNode for a |
|
||||
| argumentPassing.py:123:28:123:28 | ControlFlowNode for a | argumentPassing.py:124:11:124:11 | ControlFlowNode for a |
|
||||
| argumentPassing.py:123:30:123:33 | ControlFlowNode for arg1 | argumentPassing.py:123:28:123:28 | ControlFlowNode for a |
|
||||
| argumentPassing.py:132:28:132:31 | ControlFlowNode for arg1 | argumentPassing.py:123:28:123:28 | ControlFlowNode for a |
|
||||
| argumentPassing.py:138:22:138:24 | ControlFlowNode for foo | argumentPassing.py:139:11:139:13 | ControlFlowNode for foo |
|
||||
| argumentPassing.py:160:46:160:49 | ControlFlowNode for arg1 | argumentPassing.py:138:22:138:24 | ControlFlowNode for foo |
|
||||
@@ -102,6 +104,8 @@ nodes
|
||||
| argumentPassing.py:120:59:120:69 | ControlFlowNode for Dict [Dictionary element at key a] | semmle.label | ControlFlowNode for Dict [Dictionary element at key a] |
|
||||
| argumentPassing.py:120:65:120:68 | ControlFlowNode for arg1 | semmle.label | ControlFlowNode for arg1 |
|
||||
| argumentPassing.py:123:28:123:28 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| argumentPassing.py:123:28:123:28 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| argumentPassing.py:123:30:123:33 | ControlFlowNode for arg1 | semmle.label | ControlFlowNode for arg1 |
|
||||
| argumentPassing.py:124:11:124:11 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| argumentPassing.py:132:28:132:31 | ControlFlowNode for arg1 | semmle.label | ControlFlowNode for arg1 |
|
||||
| argumentPassing.py:138:22:138:24 | ControlFlowNode for foo | semmle.label | ControlFlowNode for foo |
|
||||
@@ -196,6 +200,7 @@ nodes
|
||||
| classes.py:860:15:860:18 | ControlFlowNode for self | semmle.label | ControlFlowNode for self |
|
||||
| classes.py:866:5:866:11 | SSA variable with_or | semmle.label | SSA variable with_or |
|
||||
| classes.py:868:5:868:11 | ControlFlowNode for with_or | semmle.label | ControlFlowNode for with_or |
|
||||
subpaths
|
||||
#select
|
||||
| argumentPassing.py:89:22:89:25 | ControlFlowNode for arg1 | argumentPassing.py:89:22:89:25 | ControlFlowNode for arg1 | argumentPassing.py:75:11:75:11 | ControlFlowNode for a | Flow found |
|
||||
| argumentPassing.py:94:22:94:25 | ControlFlowNode for arg1 | argumentPassing.py:94:22:94:25 | ControlFlowNode for arg1 | argumentPassing.py:75:11:75:11 | ControlFlowNode for a | Flow found |
|
||||
@@ -206,6 +211,7 @@ nodes
|
||||
| argumentPassing.py:118:27:118:30 | ControlFlowNode for arg1 | argumentPassing.py:118:27:118:30 | ControlFlowNode for arg1 | argumentPassing.py:110:11:110:11 | ControlFlowNode for a | Flow found |
|
||||
| argumentPassing.py:119:27:119:30 | ControlFlowNode for arg1 | argumentPassing.py:119:27:119:30 | ControlFlowNode for arg1 | argumentPassing.py:110:11:110:11 | ControlFlowNode for a | Flow found |
|
||||
| argumentPassing.py:120:65:120:68 | ControlFlowNode for arg1 | argumentPassing.py:120:65:120:68 | ControlFlowNode for arg1 | argumentPassing.py:110:11:110:11 | ControlFlowNode for a | Flow found |
|
||||
| argumentPassing.py:123:30:123:33 | ControlFlowNode for arg1 | argumentPassing.py:123:30:123:33 | ControlFlowNode for arg1 | argumentPassing.py:124:11:124:11 | ControlFlowNode for a | Flow found |
|
||||
| argumentPassing.py:132:28:132:31 | ControlFlowNode for arg1 | argumentPassing.py:132:28:132:31 | ControlFlowNode for arg1 | argumentPassing.py:124:11:124:11 | ControlFlowNode for a | Flow found |
|
||||
| argumentPassing.py:160:46:160:49 | ControlFlowNode for arg1 | argumentPassing.py:160:46:160:49 | ControlFlowNode for arg1 | argumentPassing.py:139:11:139:13 | ControlFlowNode for foo | Flow found |
|
||||
| argumentPassing.py:168:14:168:17 | ControlFlowNode for arg1 | argumentPassing.py:168:14:168:17 | ControlFlowNode for arg1 | argumentPassing.py:166:15:166:15 | ControlFlowNode for a | Flow found |
|
||||
|
||||
@@ -10,6 +10,8 @@ edges
|
||||
| argumentPassing.py:120:29:120:39 | ControlFlowNode for Dict [Dictionary element at key b] | argumentPassing.py:120:5:120:70 | KwUnpacked b |
|
||||
| argumentPassing.py:120:35:120:38 | ControlFlowNode for arg2 | argumentPassing.py:120:29:120:39 | ControlFlowNode for Dict [Dictionary element at key b] |
|
||||
| argumentPassing.py:123:36:123:36 | ControlFlowNode for b | argumentPassing.py:125:11:125:11 | ControlFlowNode for b |
|
||||
| argumentPassing.py:123:36:123:36 | ControlFlowNode for b | argumentPassing.py:125:11:125:11 | ControlFlowNode for b |
|
||||
| argumentPassing.py:123:38:123:41 | ControlFlowNode for arg2 | argumentPassing.py:123:36:123:36 | ControlFlowNode for b |
|
||||
| argumentPassing.py:133:30:133:33 | ControlFlowNode for arg2 | argumentPassing.py:123:36:123:36 | ControlFlowNode for b |
|
||||
| argumentPassing.py:138:29:138:34 | ControlFlowNode for kwargs [Dictionary element at key bar] | argumentPassing.py:140:20:140:25 | ControlFlowNode for kwargs [Dictionary element at key bar] |
|
||||
| argumentPassing.py:140:5:140:26 | KwUnpacked bar | argumentPassing.py:145:18:145:20 | ControlFlowNode for bar |
|
||||
@@ -64,6 +66,8 @@ nodes
|
||||
| argumentPassing.py:120:29:120:39 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
|
||||
| argumentPassing.py:120:35:120:38 | ControlFlowNode for arg2 | semmle.label | ControlFlowNode for arg2 |
|
||||
| argumentPassing.py:123:36:123:36 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| argumentPassing.py:123:36:123:36 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| argumentPassing.py:123:38:123:41 | ControlFlowNode for arg2 | semmle.label | ControlFlowNode for arg2 |
|
||||
| argumentPassing.py:125:11:125:11 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| argumentPassing.py:133:30:133:33 | ControlFlowNode for arg2 | semmle.label | ControlFlowNode for arg2 |
|
||||
| argumentPassing.py:138:29:138:34 | ControlFlowNode for kwargs [Dictionary element at key bar] | semmle.label | ControlFlowNode for kwargs [Dictionary element at key bar] |
|
||||
@@ -121,12 +125,14 @@ nodes
|
||||
| classes.py:858:22:858:26 | ControlFlowNode for other | semmle.label | ControlFlowNode for other |
|
||||
| classes.py:859:15:859:19 | ControlFlowNode for other | semmle.label | ControlFlowNode for other |
|
||||
| classes.py:868:15:868:18 | ControlFlowNode for arg2 | semmle.label | ControlFlowNode for arg2 |
|
||||
subpaths
|
||||
#select
|
||||
| argumentPassing.py:94:28:94:31 | ControlFlowNode for arg2 | argumentPassing.py:94:28:94:31 | ControlFlowNode for arg2 | argumentPassing.py:76:11:76:11 | ControlFlowNode for b | Flow found |
|
||||
| argumentPassing.py:104:25:104:28 | ControlFlowNode for arg2 | argumentPassing.py:104:25:104:28 | ControlFlowNode for arg2 | argumentPassing.py:99:11:99:11 | ControlFlowNode for b | Flow found |
|
||||
| argumentPassing.py:105:27:105:30 | ControlFlowNode for arg2 | argumentPassing.py:105:27:105:30 | ControlFlowNode for arg2 | argumentPassing.py:99:11:99:11 | ControlFlowNode for b | Flow found |
|
||||
| argumentPassing.py:117:29:117:32 | ControlFlowNode for arg2 | argumentPassing.py:117:29:117:32 | ControlFlowNode for arg2 | argumentPassing.py:111:11:111:11 | ControlFlowNode for b | Flow found |
|
||||
| argumentPassing.py:120:35:120:38 | ControlFlowNode for arg2 | argumentPassing.py:120:35:120:38 | ControlFlowNode for arg2 | argumentPassing.py:111:11:111:11 | ControlFlowNode for b | Flow found |
|
||||
| argumentPassing.py:123:38:123:41 | ControlFlowNode for arg2 | argumentPassing.py:123:38:123:41 | ControlFlowNode for arg2 | argumentPassing.py:125:11:125:11 | ControlFlowNode for b | Flow found |
|
||||
| argumentPassing.py:133:30:133:33 | ControlFlowNode for arg2 | argumentPassing.py:133:30:133:33 | ControlFlowNode for arg2 | argumentPassing.py:125:11:125:11 | ControlFlowNode for b | Flow found |
|
||||
| argumentPassing.py:160:36:160:39 | ControlFlowNode for arg2 | argumentPassing.py:160:36:160:39 | ControlFlowNode for arg2 | argumentPassing.py:146:11:146:13 | ControlFlowNode for bar | Flow found |
|
||||
| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key | Flow found |
|
||||
|
||||
@@ -10,6 +10,8 @@ edges
|
||||
| argumentPassing.py:120:44:120:54 | ControlFlowNode for Dict [Dictionary element at key c] | argumentPassing.py:120:5:120:70 | KwUnpacked c |
|
||||
| argumentPassing.py:120:50:120:53 | ControlFlowNode for arg3 | argumentPassing.py:120:44:120:54 | ControlFlowNode for Dict [Dictionary element at key c] |
|
||||
| argumentPassing.py:123:44:123:44 | ControlFlowNode for c | argumentPassing.py:126:11:126:11 | ControlFlowNode for c |
|
||||
| argumentPassing.py:123:44:123:44 | ControlFlowNode for c | argumentPassing.py:126:11:126:11 | ControlFlowNode for c |
|
||||
| argumentPassing.py:123:46:123:49 | ControlFlowNode for arg3 | argumentPassing.py:123:44:123:44 | ControlFlowNode for c |
|
||||
| argumentPassing.py:134:5:134:41 | KwUnpacked c | argumentPassing.py:123:44:123:44 | ControlFlowNode for c |
|
||||
| argumentPassing.py:134:30:134:40 | ControlFlowNode for Dict [Dictionary element at key c] | argumentPassing.py:134:5:134:41 | KwUnpacked c |
|
||||
| argumentPassing.py:134:36:134:39 | ControlFlowNode for arg3 | argumentPassing.py:134:30:134:40 | ControlFlowNode for Dict [Dictionary element at key c] |
|
||||
@@ -37,6 +39,8 @@ nodes
|
||||
| argumentPassing.py:120:44:120:54 | ControlFlowNode for Dict [Dictionary element at key c] | semmle.label | ControlFlowNode for Dict [Dictionary element at key c] |
|
||||
| argumentPassing.py:120:50:120:53 | ControlFlowNode for arg3 | semmle.label | ControlFlowNode for arg3 |
|
||||
| argumentPassing.py:123:44:123:44 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
|
||||
| argumentPassing.py:123:44:123:44 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
|
||||
| argumentPassing.py:123:46:123:49 | ControlFlowNode for arg3 | semmle.label | ControlFlowNode for arg3 |
|
||||
| argumentPassing.py:126:11:126:11 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
|
||||
| argumentPassing.py:134:5:134:41 | KwUnpacked c | semmle.label | KwUnpacked c |
|
||||
| argumentPassing.py:134:30:134:40 | ControlFlowNode for Dict [Dictionary element at key c] | semmle.label | ControlFlowNode for Dict [Dictionary element at key c] |
|
||||
@@ -53,11 +57,13 @@ nodes
|
||||
| classes.py:570:32:570:36 | ControlFlowNode for value | semmle.label | ControlFlowNode for value |
|
||||
| classes.py:571:15:571:19 | ControlFlowNode for value | semmle.label | ControlFlowNode for value |
|
||||
| classes.py:581:26:581:29 | ControlFlowNode for arg3 | semmle.label | ControlFlowNode for arg3 |
|
||||
subpaths
|
||||
#select
|
||||
| argumentPassing.py:94:34:94:37 | ControlFlowNode for arg3 | argumentPassing.py:94:34:94:37 | ControlFlowNode for arg3 | argumentPassing.py:77:11:77:11 | ControlFlowNode for c | Flow found |
|
||||
| argumentPassing.py:117:37:117:40 | ControlFlowNode for arg3 | argumentPassing.py:117:37:117:40 | ControlFlowNode for arg3 | argumentPassing.py:112:11:112:11 | ControlFlowNode for c | Flow found |
|
||||
| argumentPassing.py:119:41:119:44 | ControlFlowNode for arg3 | argumentPassing.py:119:41:119:44 | ControlFlowNode for arg3 | argumentPassing.py:112:11:112:11 | ControlFlowNode for c | Flow found |
|
||||
| argumentPassing.py:120:50:120:53 | ControlFlowNode for arg3 | argumentPassing.py:120:50:120:53 | ControlFlowNode for arg3 | argumentPassing.py:112:11:112:11 | ControlFlowNode for c | Flow found |
|
||||
| argumentPassing.py:123:46:123:49 | ControlFlowNode for arg3 | argumentPassing.py:123:46:123:49 | ControlFlowNode for arg3 | argumentPassing.py:126:11:126:11 | ControlFlowNode for c | Flow found |
|
||||
| argumentPassing.py:134:36:134:39 | ControlFlowNode for arg3 | argumentPassing.py:134:36:134:39 | ControlFlowNode for arg3 | argumentPassing.py:126:11:126:11 | ControlFlowNode for c | Flow found |
|
||||
| argumentPassing.py:160:26:160:29 | ControlFlowNode for arg3 | argumentPassing.py:160:26:160:29 | ControlFlowNode for arg3 | argumentPassing.py:155:11:155:13 | ControlFlowNode for baz | Flow found |
|
||||
| classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:571:15:571:19 | ControlFlowNode for value | Flow found |
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
edges
|
||||
| argumentPassing.py:69:5:69:5 | ControlFlowNode for d | argumentPassing.py:78:11:78:11 | ControlFlowNode for d |
|
||||
| argumentPassing.py:69:7:69:10 | ControlFlowNode for arg4 | argumentPassing.py:69:5:69:5 | ControlFlowNode for d |
|
||||
nodes
|
||||
| argumentPassing.py:69:5:69:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| argumentPassing.py:69:7:69:10 | ControlFlowNode for arg4 | semmle.label | ControlFlowNode for arg4 |
|
||||
| argumentPassing.py:78:11:78:11 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
subpaths
|
||||
#select
|
||||
| argumentPassing.py:69:7:69:10 | ControlFlowNode for arg4 | argumentPassing.py:69:7:69:10 | ControlFlowNode for arg4 | argumentPassing.py:78:11:78:11 | ControlFlowNode for d | Flow found |
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
edges
|
||||
| argumentPassing.py:71:5:71:5 | ControlFlowNode for e | argumentPassing.py:79:11:79:11 | ControlFlowNode for e |
|
||||
| argumentPassing.py:71:7:71:10 | ControlFlowNode for arg5 | argumentPassing.py:71:5:71:5 | ControlFlowNode for e |
|
||||
nodes
|
||||
| argumentPassing.py:71:5:71:5 | ControlFlowNode for e | semmle.label | ControlFlowNode for e |
|
||||
| argumentPassing.py:71:7:71:10 | ControlFlowNode for arg5 | semmle.label | ControlFlowNode for arg5 |
|
||||
| argumentPassing.py:79:11:79:11 | ControlFlowNode for e | semmle.label | ControlFlowNode for e |
|
||||
subpaths
|
||||
#select
|
||||
| argumentPassing.py:71:7:71:10 | ControlFlowNode for arg5 | argumentPassing.py:71:7:71:10 | ControlFlowNode for arg5 | argumentPassing.py:79:11:79:11 | ControlFlowNode for e | Flow found |
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
edges
|
||||
nodes
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -11,5 +11,6 @@ nodes
|
||||
| argumentPassing.py:89:5:89:81 | KwOverflowNode for argument_passing() [Dictionary element at key g] | semmle.label | KwOverflowNode for argument_passing() [Dictionary element at key g] |
|
||||
| argumentPassing.py:89:59:89:80 | ControlFlowNode for Dict [Dictionary element at key g] | semmle.label | ControlFlowNode for Dict [Dictionary element at key g] |
|
||||
| argumentPassing.py:89:76:89:79 | ControlFlowNode for arg7 | semmle.label | ControlFlowNode for arg7 |
|
||||
subpaths
|
||||
#select
|
||||
| argumentPassing.py:89:76:89:79 | ControlFlowNode for arg7 | argumentPassing.py:89:76:89:79 | ControlFlowNode for arg7 | argumentPassing.py:82:15:82:20 | ControlFlowNode for Subscript | Flow found |
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
edges
|
||||
| datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a |
|
||||
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a |
|
||||
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() |
|
||||
| datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x |
|
||||
| datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x |
|
||||
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x |
|
||||
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x |
|
||||
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x |
|
||||
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x |
|
||||
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] |
|
||||
| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] |
|
||||
@@ -121,25 +129,49 @@ edges
|
||||
| test.py:353:11:353:16 | ControlFlowNode for SOURCE | test.py:353:10:353:17 | ControlFlowNode for List [List element] |
|
||||
| test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:357:10:357:27 | ControlFlowNode for Subscript |
|
||||
| test.py:357:16:357:21 | ControlFlowNode for SOURCE | test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] |
|
||||
| test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b |
|
||||
| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b |
|
||||
| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:380:10:380:34 | ControlFlowNode for second() |
|
||||
| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b |
|
||||
| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:388:10:388:36 | ControlFlowNode for second() |
|
||||
| test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b |
|
||||
| test.py:396:10:396:43 | KwUnpacked b | test.py:396:10:396:43 | ControlFlowNode for second() |
|
||||
| test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:396:10:396:43 | KwUnpacked b |
|
||||
| test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] |
|
||||
| test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript |
|
||||
| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:404:33:404:38 | ControlFlowNode for SOURCE | test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
|
||||
| test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript |
|
||||
| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() |
|
||||
| test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
|
||||
| test.py:433:10:433:15 | ControlFlowNode for SOURCE | test.py:433:10:433:38 | ControlFlowNode for IfExp |
|
||||
| test.py:441:34:441:39 | ControlFlowNode for SOURCE | test.py:441:10:441:39 | ControlFlowNode for IfExp |
|
||||
| test.py:462:11:462:11 | ControlFlowNode for x | test.py:463:16:463:16 | ControlFlowNode for x |
|
||||
| test.py:465:12:465:17 | ControlFlowNode for SOURCE | test.py:462:11:462:11 | ControlFlowNode for x |
|
||||
| test.py:465:12:465:17 | ControlFlowNode for SOURCE | test.py:465:10:465:18 | ControlFlowNode for f() |
|
||||
| test.py:469:19:469:19 | ControlFlowNode for b | test.py:470:16:470:16 | ControlFlowNode for b |
|
||||
| test.py:472:28:472:33 | ControlFlowNode for SOURCE | test.py:469:19:469:19 | ControlFlowNode for b |
|
||||
| test.py:472:28:472:33 | ControlFlowNode for SOURCE | test.py:472:10:472:34 | ControlFlowNode for second() |
|
||||
| test.py:483:19:483:19 | ControlFlowNode for b | test.py:484:16:484:16 | ControlFlowNode for b |
|
||||
| test.py:486:30:486:35 | ControlFlowNode for SOURCE | test.py:483:19:483:19 | ControlFlowNode for b |
|
||||
| test.py:486:30:486:35 | ControlFlowNode for SOURCE | test.py:486:10:486:36 | ControlFlowNode for second() |
|
||||
| test.py:497:19:497:19 | ControlFlowNode for b | test.py:498:16:498:16 | ControlFlowNode for b |
|
||||
| test.py:500:10:500:43 | KwUnpacked b | test.py:497:19:497:19 | ControlFlowNode for b |
|
||||
| test.py:500:10:500:43 | KwUnpacked b | test.py:500:10:500:43 | ControlFlowNode for second() |
|
||||
| test.py:500:30:500:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:500:10:500:43 | KwUnpacked b |
|
||||
| test.py:500:36:500:41 | ControlFlowNode for SOURCE | test.py:500:30:500:42 | ControlFlowNode for Dict [Dictionary element at key b] |
|
||||
| test.py:504:30:504:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:504:33:504:33 | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:504:33:504:33 | ControlFlowNode for b [Tuple element at index 0] | test.py:504:33:504:36 | ControlFlowNode for Subscript |
|
||||
| test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:504:30:504:30 | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:505:10:505:39 | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:505:33:505:38 | ControlFlowNode for SOURCE | test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
|
||||
| test.py:509:35:509:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:509:38:509:38 | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:509:38:509:38 | ControlFlowNode for b [Dictionary element at key b] | test.py:509:38:509:43 | ControlFlowNode for Subscript |
|
||||
| test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:509:35:509:35 | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:510:10:510:45 | ControlFlowNode for f_extra_keyword() |
|
||||
| test.py:510:39:510:44 | ControlFlowNode for SOURCE | test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
|
||||
| test.py:522:9:522:14 | ControlFlowNode for SOURCE | test.py:524:10:524:10 | ControlFlowNode for a |
|
||||
@@ -347,9 +379,21 @@ edges
|
||||
| test.py:686:43:686:48 | ControlFlowNode for SOURCE | test.py:686:3:686:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] |
|
||||
| test.py:686:51:686:51 | ControlFlowNode for s | test.py:686:3:686:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] |
|
||||
| test.py:757:16:757:21 | ControlFlowNode for SOURCE | test.py:760:10:760:36 | ControlFlowNode for return_from_inner_scope() |
|
||||
| test.py:795:35:795:35 | ControlFlowNode for x | test.py:796:10:796:10 | ControlFlowNode for x |
|
||||
| test.py:795:37:795:42 | ControlFlowNode for SOURCE | test.py:795:35:795:35 | ControlFlowNode for x |
|
||||
| test.py:795:48:795:48 | ControlFlowNode for y | test.py:797:10:797:10 | ControlFlowNode for y |
|
||||
| test.py:795:50:795:55 | ControlFlowNode for SOURCE | test.py:795:48:795:48 | ControlFlowNode for y |
|
||||
| test.py:795:61:795:61 | ControlFlowNode for z | test.py:798:10:798:10 | ControlFlowNode for z |
|
||||
| test.py:795:63:795:68 | ControlFlowNode for SOURCE | test.py:795:61:795:61 | ControlFlowNode for z |
|
||||
nodes
|
||||
| datamodel.py:35:7:35:7 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| datamodel.py:36:10:36:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| datamodel.py:38:6:38:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
|
||||
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| datamodel.py:44:22:44:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| datamodel.py:46:16:46:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| datamodel.py:49:26:49:26 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| datamodel.py:50:16:50:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| 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() |
|
||||
@@ -501,6 +545,8 @@ nodes
|
||||
| test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
|
||||
| test.py:357:10:357:27 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:357:16:357:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:375:15:375:15 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:376:12:376:12 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:380:10:380:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
| test.py:380:28:380:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:388:10:388:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
@@ -509,9 +555,15 @@ nodes
|
||||
| test.py:396:10:396:43 | KwUnpacked b | semmle.label | KwUnpacked b |
|
||||
| test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
|
||||
| test.py:396:36:396:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:400:12:400:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
|
||||
| test.py:404:33:404:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:408:12:408:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
|
||||
| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
|
||||
| test.py:412:39:412:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
@@ -519,19 +571,33 @@ nodes
|
||||
| test.py:433:10:433:38 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp |
|
||||
| test.py:441:10:441:39 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp |
|
||||
| test.py:441:34:441:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:462:11:462:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:463:16:463:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:465:10:465:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
|
||||
| test.py:465:12:465:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:469:19:469:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:470:16:470:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:472:10:472:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
| test.py:472:28:472:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:483:19:483:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:484:16:484:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:486:10:486:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
| test.py:486:30:486:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:497:19:497:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:498:16:498:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:500:10:500:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
| test.py:500:10:500:43 | KwUnpacked b | semmle.label | KwUnpacked b |
|
||||
| test.py:500:30:500:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
|
||||
| test.py:500:36:500:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:504:30:504:30 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:504:33:504:33 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:504:33:504:36 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:505:10:505:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
|
||||
| test.py:505:33:505:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:509:35:509:35 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:509:38:509:38 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:509:38:509:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:510:10:510:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
|
||||
| test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
|
||||
| test.py:510:39:510:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
@@ -758,6 +824,32 @@ nodes
|
||||
| test.py:686:51:686:51 | ControlFlowNode for s | semmle.label | ControlFlowNode for s |
|
||||
| test.py:757:16:757:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:760:10:760:36 | ControlFlowNode for return_from_inner_scope() | semmle.label | ControlFlowNode for return_from_inner_scope() |
|
||||
| test.py:795:35:795:35 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:795:37:795:42 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:795:48:795:48 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:795:50:795:55 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:795:61:795:61 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
|
||||
| test.py:795:63:795:68 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:796:10:796:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:797:10:797:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:798:10:798:10 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
|
||||
subpaths
|
||||
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a | datamodel.py:38:6:38:17 | ControlFlowNode for f() |
|
||||
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() |
|
||||
| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:380:10:380:34 | ControlFlowNode for second() |
|
||||
| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:388:10:388:36 | ControlFlowNode for second() |
|
||||
| test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:396:10:396:43 | ControlFlowNode for second() |
|
||||
| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() |
|
||||
| test.py:465:12:465:17 | ControlFlowNode for SOURCE | test.py:462:11:462:11 | ControlFlowNode for x | test.py:463:16:463:16 | ControlFlowNode for x | test.py:465:10:465:18 | ControlFlowNode for f() |
|
||||
| test.py:472:28:472:33 | ControlFlowNode for SOURCE | test.py:469:19:469:19 | ControlFlowNode for b | test.py:470:16:470:16 | ControlFlowNode for b | test.py:472:10:472:34 | ControlFlowNode for second() |
|
||||
| test.py:486:30:486:35 | ControlFlowNode for SOURCE | test.py:483:19:483:19 | ControlFlowNode for b | test.py:484:16:484:16 | ControlFlowNode for b | test.py:486:10:486:36 | ControlFlowNode for second() |
|
||||
| test.py:500:10:500:43 | KwUnpacked b | test.py:497:19:497:19 | ControlFlowNode for b | test.py:498:16:498:16 | ControlFlowNode for b | test.py:500:10:500:43 | ControlFlowNode for second() |
|
||||
| test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:504:30:504:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:504:33:504:36 | ControlFlowNode for Subscript | test.py:505:10:505:39 | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:509:35:509:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:509:38:509:43 | ControlFlowNode for Subscript | test.py:510:10:510:45 | ControlFlowNode for f_extra_keyword() |
|
||||
#select
|
||||
| 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() | Flow found |
|
||||
| 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() | Flow found |
|
||||
@@ -865,3 +957,6 @@ nodes
|
||||
| test.py:680:10:680:12 | ControlFlowNode for arg | test.py:685:7:685:12 | ControlFlowNode for SOURCE | test.py:680:10:680:12 | ControlFlowNode for arg | Flow found |
|
||||
| test.py:680:10:680:12 | ControlFlowNode for arg | test.py:686:43:686:48 | ControlFlowNode for SOURCE | test.py:680:10:680:12 | ControlFlowNode for arg | Flow found |
|
||||
| test.py:760:10:760:36 | ControlFlowNode for return_from_inner_scope() | test.py:757:16:757:21 | ControlFlowNode for SOURCE | test.py:760:10:760:36 | ControlFlowNode for return_from_inner_scope() | Flow found |
|
||||
| test.py:796:10:796:10 | ControlFlowNode for x | test.py:795:37:795:42 | ControlFlowNode for SOURCE | test.py:796:10:796:10 | ControlFlowNode for x | Flow found |
|
||||
| test.py:797:10:797:10 | ControlFlowNode for y | test.py:795:50:795:55 | ControlFlowNode for SOURCE | test.py:797:10:797:10 | ControlFlowNode for y | Flow found |
|
||||
| test.py:798:10:798:10 | ControlFlowNode for z | test.py:795:63:795:68 | ControlFlowNode for SOURCE | test.py:798:10:798:10 | ControlFlowNode for z | Flow found |
|
||||
|
||||
@@ -793,6 +793,6 @@ def test_reverse_read_subscript_cls():
|
||||
|
||||
@expects(3)
|
||||
def test_with_default_param_value(x=SOURCE, /, y=SOURCE, *, z=SOURCE):
|
||||
SINK(x) #$ MISSING:flow="SOURCE, l:-1 -> x"
|
||||
SINK(y) #$ MISSING:flow="SOURCE, l:-2 -> y"
|
||||
SINK(z) #$ MISSING:flow="SOURCE, l:-3 -> z"
|
||||
SINK(x) #$ flow="SOURCE, l:-1 -> x"
|
||||
SINK(y) #$ flow="SOURCE, l:-2 -> y"
|
||||
SINK(z) #$ flow="SOURCE, l:-3 -> z"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
edges
|
||||
| examples.py:7:24:7:26 | ControlFlowNode for foo | examples.py:8:20:8:22 | ControlFlowNode for foo |
|
||||
| examples.py:8:20:8:22 | ControlFlowNode for foo | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] |
|
||||
| examples.py:20:17:20:17 | ControlFlowNode for x | examples.py:22:15:22:15 | ControlFlowNode for x |
|
||||
| examples.py:22:15:22:15 | ControlFlowNode for x | examples.py:22:5:22:7 | [post store] ControlFlowNode for obj [Attribute foo] |
|
||||
| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] |
|
||||
| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:20:17:20:17 | ControlFlowNode for x |
|
||||
| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] |
|
||||
| examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:14 | ControlFlowNode for Attribute |
|
||||
| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x |
|
||||
@@ -9,13 +14,29 @@ edges
|
||||
| examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:37:6:37:10 | ControlFlowNode for Attribute [Attribute foo] |
|
||||
| examples.py:37:6:37:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:37:6:37:14 | ControlFlowNode for Attribute |
|
||||
| examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] |
|
||||
| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | ControlFlowNode for foo |
|
||||
| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | examples.py:50:6:50:12 | ControlFlowNode for Attribute |
|
||||
| examples.py:53:28:53:28 | ControlFlowNode for x | examples.py:54:17:54:17 | ControlFlowNode for x |
|
||||
| examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] |
|
||||
| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:7:24:7:26 | ControlFlowNode for foo |
|
||||
| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | examples.py:55:9:55:15 | ControlFlowNode for Attribute |
|
||||
| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:56:12:56:12 | ControlFlowNode for a |
|
||||
| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:53:28:53:28 | ControlFlowNode for x |
|
||||
| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() |
|
||||
| test.py:26:24:26:26 | ControlFlowNode for foo | test.py:27:20:27:22 | ControlFlowNode for foo |
|
||||
| test.py:27:20:27:22 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] |
|
||||
| test.py:29:22:29:24 | ControlFlowNode for foo | test.py:30:20:30:22 | ControlFlowNode for foo |
|
||||
| test.py:30:20:30:22 | ControlFlowNode for foo | test.py:30:9:30:12 | [post store] ControlFlowNode for self [Attribute foo] |
|
||||
| test.py:41:17:41:17 | ControlFlowNode for x | test.py:43:15:43:15 | ControlFlowNode for x |
|
||||
| test.py:43:15:43:15 | ControlFlowNode for x | test.py:43:5:43:7 | [post store] ControlFlowNode for obj [Attribute foo] |
|
||||
| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] |
|
||||
| test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:41:17:41:17 | ControlFlowNode for x |
|
||||
| test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
|
||||
| test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:18 | ControlFlowNode for Attribute |
|
||||
| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] |
|
||||
| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:22:29:24 | ControlFlowNode for foo |
|
||||
| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] |
|
||||
| test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute |
|
||||
| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:17:65:17 | ControlFlowNode for x |
|
||||
@@ -31,13 +52,28 @@ edges
|
||||
| test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] |
|
||||
| test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:77:10:77:18 | ControlFlowNode for Attribute |
|
||||
| test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] |
|
||||
| test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | ControlFlowNode for foo |
|
||||
| test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | test.py:82:10:82:16 | ControlFlowNode for Attribute |
|
||||
| test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] |
|
||||
| test.py:86:21:86:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | ControlFlowNode for foo |
|
||||
| test.py:86:21:86:26 | ControlFlowNode for SOURCE | test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | test.py:87:10:87:16 | ControlFlowNode for Attribute |
|
||||
| test.py:90:28:90:28 | ControlFlowNode for x | test.py:91:17:91:17 | ControlFlowNode for x |
|
||||
| test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] |
|
||||
| test.py:91:17:91:17 | ControlFlowNode for x | test.py:26:24:26:26 | ControlFlowNode for foo |
|
||||
| test.py:91:17:91:17 | ControlFlowNode for x | test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | test.py:92:9:92:15 | ControlFlowNode for Attribute |
|
||||
| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:93:12:93:12 | ControlFlowNode for a |
|
||||
| test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:90:28:90:28 | ControlFlowNode for x |
|
||||
| test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() |
|
||||
nodes
|
||||
| examples.py:7:24:7:26 | ControlFlowNode for foo | semmle.label | ControlFlowNode for foo |
|
||||
| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | semmle.label | [post store] ControlFlowNode for self [Attribute foo] |
|
||||
| examples.py:8:20:8:22 | ControlFlowNode for foo | semmle.label | ControlFlowNode for foo |
|
||||
| examples.py:20:17:20:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| examples.py:22:5:22:7 | [post store] ControlFlowNode for obj [Attribute foo] | semmle.label | [post store] ControlFlowNode for obj [Attribute foo] |
|
||||
| examples.py:22:15:22:15 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] |
|
||||
| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] |
|
||||
@@ -53,8 +89,23 @@ nodes
|
||||
| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] |
|
||||
| examples.py:50:6:50:12 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| examples.py:53:28:53:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| examples.py:54:17:54:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] |
|
||||
| examples.py:55:9:55:15 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| examples.py:56:12:56:12 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() |
|
||||
| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:26:24:26:26 | ControlFlowNode for foo | semmle.label | ControlFlowNode for foo |
|
||||
| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | semmle.label | [post store] ControlFlowNode for self [Attribute foo] |
|
||||
| test.py:27:20:27:22 | ControlFlowNode for foo | semmle.label | ControlFlowNode for foo |
|
||||
| test.py:29:22:29:24 | ControlFlowNode for foo | semmle.label | ControlFlowNode for foo |
|
||||
| test.py:30:9:30:12 | [post store] ControlFlowNode for self [Attribute foo] | semmle.label | [post store] ControlFlowNode for self [Attribute foo] |
|
||||
| test.py:30:20:30:22 | ControlFlowNode for foo | semmle.label | ControlFlowNode for foo |
|
||||
| test.py:41:17:41:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:43:5:43:7 | [post store] ControlFlowNode for obj [Attribute foo] | semmle.label | [post store] ControlFlowNode for obj [Attribute foo] |
|
||||
| test.py:43:15:43:15 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] |
|
||||
| test.py:49:19:49:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] |
|
||||
@@ -85,8 +136,25 @@ nodes
|
||||
| test.py:86:21:86:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] |
|
||||
| test.py:87:10:87:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| test.py:90:28:90:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| test.py:91:17:91:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] |
|
||||
| test.py:92:9:92:15 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| test.py:93:12:93:12 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() |
|
||||
| test.py:97:33:97:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
subpaths
|
||||
| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:20:17:20:17 | ControlFlowNode for x | examples.py:22:5:22:7 | [post store] ControlFlowNode for obj [Attribute foo] | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] |
|
||||
| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | ControlFlowNode for foo | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:7:24:7:26 | ControlFlowNode for foo | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:53:28:53:28 | ControlFlowNode for x | examples.py:56:12:56:12 | ControlFlowNode for a | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() |
|
||||
| test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:41:17:41:17 | ControlFlowNode for x | test.py:43:5:43:7 | [post store] ControlFlowNode for obj [Attribute foo] | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
|
||||
| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:22:29:24 | ControlFlowNode for foo | test.py:30:9:30:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] |
|
||||
| test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| test.py:86:21:86:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| test.py:91:17:91:17 | ControlFlowNode for x | test.py:26:24:26:26 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] |
|
||||
| test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:90:28:90:28 | ControlFlowNode for x | test.py:93:12:93:12 | ControlFlowNode for a | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() |
|
||||
#select
|
||||
| examples.py:28:6:28:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:28:6:28:14 | ControlFlowNode for Attribute | Flow found |
|
||||
| examples.py:37:6:37:14 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:37:6:37:14 | ControlFlowNode for Attribute | Flow found |
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
| test1.py:1:8:1:12 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test2.py:1:6:1:10 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test2.py:1:6:1:10 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test2.py:1:19:1:21 | ControlFlowNode for ImportMember | mypkg.foo |
|
||||
| test2.py:1:24:1:26 | ControlFlowNode for ImportMember | mypkg.bar |
|
||||
| test3.py:1:8:1:16 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test3.py:2:8:2:16 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test4.py:1:8:1:16 | ControlFlowNode for ImportExpr | mypkg.foo |
|
||||
| test4.py:2:8:2:16 | ControlFlowNode for ImportExpr | mypkg.bar |
|
||||
| test5.py:1:8:1:12 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test5.py:9:6:9:10 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test5.py:9:19:9:29 | ControlFlowNode for ImportMember | mypkg.bar |
|
||||
| test6.py:1:8:1:12 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test6.py:5:8:5:16 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test7.py:1:6:1:10 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test7.py:1:19:1:21 | ControlFlowNode for ImportMember | mypkg.foo |
|
||||
| test7.py:5:8:5:16 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test7.py:9:6:9:10 | ControlFlowNode for ImportExpr | mypkg |
|
||||
| test7.py:9:19:9:21 | ControlFlowNode for ImportMember | mypkg.foo |
|
||||
| test_deep.py:1:6:1:21 | ControlFlowNode for ImportExpr | start.middle.end |
|
||||
| test_deep.py:1:6:1:21 | ControlFlowNode for ImportExpr | start.middle.end |
|
||||
| test_deep.py:1:30:1:32 | ControlFlowNode for ImportMember | start.middle.end.foo |
|
||||
| test_deep.py:1:35:1:37 | ControlFlowNode for ImportMember | start.middle.end.bar |
|
||||
@@ -1,4 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
query predicate importNode(DataFlow::Node res, string name) { res = DataFlow::importNode(name) }
|
||||
@@ -1 +0,0 @@
|
||||
Small tests that explore difference between `import mypkg.foo` and `from mypkg import foo`.
|
||||
@@ -1 +0,0 @@
|
||||
foo = 42
|
||||
@@ -1 +0,0 @@
|
||||
pass
|
||||
@@ -1 +0,0 @@
|
||||
pass
|
||||
@@ -1,6 +0,0 @@
|
||||
import mypkg
|
||||
print(mypkg.foo) # 42
|
||||
try:
|
||||
print(mypkg.bar)
|
||||
except AttributeError as e:
|
||||
print(e) # module 'mypkg' has no attribute 'bar'
|
||||
@@ -1,3 +0,0 @@
|
||||
from mypkg import foo, bar
|
||||
print(foo)
|
||||
print(bar)
|
||||
@@ -1,4 +0,0 @@
|
||||
import mypkg.foo
|
||||
import mypkg.bar
|
||||
print(mypkg.foo) # <module 'mypkg.foo' ...
|
||||
print(mypkg.bar) # <module 'mypkg.bar' ...
|
||||
@@ -1,4 +0,0 @@
|
||||
import mypkg.foo as _foo
|
||||
import mypkg.bar as _bar
|
||||
print(_foo) # <module 'mypkg.foo' ...
|
||||
print(_bar) # <module 'mypkg.bar' ...
|
||||
@@ -1,10 +0,0 @@
|
||||
import mypkg
|
||||
|
||||
print(mypkg.foo) # 42
|
||||
try:
|
||||
print(mypkg.bar)
|
||||
except AttributeError as e:
|
||||
print(e) # module 'mypkg' has no attribute 'bar'
|
||||
|
||||
from mypkg import bar as _bar
|
||||
print(mypkg.bar) # <module 'mypkg.bar' ...
|
||||
@@ -1,6 +0,0 @@
|
||||
import mypkg
|
||||
|
||||
print(mypkg.foo) # 42
|
||||
|
||||
import mypkg.foo
|
||||
print(mypkg.foo) # <module 'mypkg.foo' ...
|
||||
@@ -1,10 +0,0 @@
|
||||
from mypkg import foo
|
||||
|
||||
print(foo) # 42
|
||||
|
||||
import mypkg.foo
|
||||
print(foo) # 42
|
||||
print(mypkg.foo) # <module 'mypkg.foo' ...
|
||||
|
||||
from mypkg import foo
|
||||
print(foo) # <module 'mypkg.foo' ...
|
||||
@@ -1,3 +0,0 @@
|
||||
from start.middle.end import foo, bar
|
||||
print(foo)
|
||||
print(bar)
|
||||
@@ -1,6 +1,6 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import experimental.dataflow.TestUtil.PrintNode
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
query predicate conjunctive_lookup(
|
||||
DataFlow::MethodCallNode methCall, string call, string object, string methodName
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import experimental.dataflow.TestUtil.PrintNode
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
|
||||
|
||||
@@ -16,6 +16,15 @@ def test_access():
|
||||
tainted_list.copy(), # $ tainted
|
||||
)
|
||||
|
||||
for ((x, y, *z), a, b) in tainted_list:
|
||||
ensure_tainted(
|
||||
x, # $ tainted
|
||||
y, # $ tainted
|
||||
z, # $ tainted
|
||||
a, # $ tainted
|
||||
b, # $ tainted
|
||||
)
|
||||
|
||||
|
||||
def list_clear():
|
||||
tainted_string = TAINTED_STRING
|
||||
|
||||
@@ -52,6 +52,8 @@ def test_access(x, y, z):
|
||||
reversed(tainted_list), # $ tainted
|
||||
iter(tainted_list), # $ tainted
|
||||
next(iter(tainted_list)), # $ tainted
|
||||
[i for i in tainted_list], # $ tainted
|
||||
[tainted_list for _i in [1,2,3]], # $ MISSING: tainted
|
||||
)
|
||||
|
||||
a, b, c = tainted_list[0:3]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TypeTracker
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
private DataFlow::TypeTrackingNode module_tracker(TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("module")
|
||||
result = API::moduleImport("module").getAUse()
|
||||
or
|
||||
exists(TypeTracker t2 | result = module_tracker(t2).track(t2, t))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TypeTracker
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// tracked
|
||||
@@ -119,7 +120,7 @@ class TrackedSelfTest extends InlineExpectationsTest {
|
||||
/** Gets a reference to `foo` (fictive module). */
|
||||
private DataFlow::TypeTrackingNode foo(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("foo")
|
||||
result = API::moduleImport("foo").getAUse()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = foo(t2).track(t2, t))
|
||||
}
|
||||
@@ -130,7 +131,7 @@ DataFlow::Node foo() { foo(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
/** Gets a reference to `foo.bar` (fictive module). */
|
||||
private DataFlow::TypeTrackingNode foo_bar(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("foo.bar")
|
||||
result = API::moduleImport("foo.bar").getAUse()
|
||||
or
|
||||
t.startInAttr("bar") and
|
||||
result = foo()
|
||||
@@ -144,7 +145,7 @@ DataFlow::Node foo_bar() { foo_bar(DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
/** Gets a reference to `foo.bar.baz` (fictive attribute on `foo.bar` module). */
|
||||
private DataFlow::TypeTrackingNode foo_bar_baz(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("foo.bar.baz")
|
||||
result = API::moduleImport("foo.bar.baz").getAUse()
|
||||
or
|
||||
t.startInAttr("baz") and
|
||||
result = foo_bar()
|
||||
|
||||
@@ -2,7 +2,7 @@ import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.Concepts
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import experimental.dataflow.TestUtil.PrintNode
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
class SystemCommandExecutionTest extends InlineExpectationsTest {
|
||||
SystemCommandExecutionTest() { this = "SystemCommandExecutionTest" }
|
||||
|
||||
@@ -14,7 +14,7 @@ import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import experimental.dataflow.TestUtil.PrintNode
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
DataFlow::Node shouldBeTainted() {
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
|
||||
@@ -80,6 +80,7 @@ nodes
|
||||
| ldap_bad.py:48:21:48:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:55:9:55:10 | ControlFlowNode for dn | semmle.label | ControlFlowNode for dn |
|
||||
| ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter | semmle.label | ControlFlowNode for search_filter |
|
||||
subpaths
|
||||
#select
|
||||
| ldap3_bad.py:21:17:21:18 | ControlFlowNode for dn | ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | ldap3_bad.py:21:17:21:18 | ControlFlowNode for dn | $@ LDAP query parameter comes from $@. | ldap3_bad.py:21:17:21:18 | ControlFlowNode for dn | This | ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | This | ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | a user-provided value |
|
||||
|
||||
@@ -21,6 +21,7 @@ nodes
|
||||
| re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern |
|
||||
subpaths
|
||||
#select
|
||||
| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | This | re_bad.py:13:22:13:28 | ControlFlowNode for request | user-provided value | re_bad.py:14:5:14:13 | Attribute | re.search |
|
||||
| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:24:22:24:28 | ControlFlowNode for request | user-provided value | re_bad.py:26:5:26:27 | Attribute | re.search |
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
| code/h_classes.py:3:1:3:16 | ControlFlowNode for ClassExpr | code/h_classes.py:10:1:10:9 | ControlFlowNode for type() |
|
||||
| code/h_classes.py:3:1:3:16 | ControlFlowNode for ClassExpr | code/h_classes.py:15:5:15:13 | ControlFlowNode for type() |
|
||||
| code/l_calls.py:3:13:3:14 | ControlFlowNode for List | code/l_calls.py:4:12:4:12 | ControlFlowNode for x |
|
||||
| code/l_calls.py:6:13:6:14 | ControlFlowNode for List | code/l_calls.py:7:16:7:16 | ControlFlowNode for x |
|
||||
| code/l_calls.py:12:1:12:20 | ControlFlowNode for ClassExpr | code/l_calls.py:16:16:16:18 | ControlFlowNode for cls |
|
||||
| code/l_calls.py:12:1:12:20 | ControlFlowNode for ClassExpr | code/l_calls.py:24:13:24:22 | ControlFlowNode for Attribute() |
|
||||
| code/l_calls.py:12:1:12:20 | ControlFlowNode for ClassExpr | code/l_calls.py:25:16:25:16 | ControlFlowNode for a |
|
||||
|
||||
@@ -55,10 +55,10 @@ async def test_taint(request: web.Request): # $ requestHandler
|
||||
await request.content.readline(), # $ tainted
|
||||
await request.content.readchunk(), # $ tainted
|
||||
(await request.content.readchunk())[0], # $ tainted
|
||||
[line async for line in request.content], # $ MISSING: tainted
|
||||
[data async for data in request.content.iter_chunked(1024)], # $ MISSING: tainted
|
||||
[data async for data in request.content.iter_any()], # $ MISSING: tainted
|
||||
[data async for data, _ in request.content.iter_chunks()], # $ MISSING: tainted
|
||||
[line async for line in request.content], # $ tainted
|
||||
[data async for data in request.content.iter_chunked(1024)], # $ tainted
|
||||
[data async for data in request.content.iter_any()], # $ tainted
|
||||
[data async for data, _ in request.content.iter_chunks()], # $ tainted
|
||||
request.content.read_nowait(), # $ tainted
|
||||
|
||||
# aiohttp.StreamReader
|
||||
|
||||
@@ -11,6 +11,9 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
|
||||
# Manually inspected all fields of the HttpRequest object
|
||||
# https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects
|
||||
|
||||
import django.urls
|
||||
django.urls.ResolverMatch
|
||||
|
||||
ensure_tainted(
|
||||
request, # $ tainted
|
||||
|
||||
@@ -35,8 +38,8 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
|
||||
request.GET, # $ tainted
|
||||
request.GET["key"], # $ tainted
|
||||
request.GET.get("key"), # $ tainted
|
||||
request.GET.getlist("key"), # $ MISSING: tainted
|
||||
request.GET.getlist("key")[0], # $ MISSING: tainted
|
||||
request.GET.getlist("key"), # $ tainted
|
||||
request.GET.getlist("key")[0], # $ tainted
|
||||
request.GET.pop("key"), # $ tainted
|
||||
request.GET.pop("key")[0], # $ tainted
|
||||
# key
|
||||
@@ -45,9 +48,10 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
|
||||
request.GET.popitem()[1], # $ tainted
|
||||
# values[0]
|
||||
request.GET.popitem()[1][0], # $ tainted
|
||||
request.GET.dict(), # $ MISSING: tainted
|
||||
request.GET.dict()["key"], # $ MISSING: tainted
|
||||
request.GET.urlencode(), # $ MISSING: tainted
|
||||
request.GET.lists(), # $ tainted
|
||||
request.GET.dict(), # $ tainted
|
||||
request.GET.dict()["key"], # $ tainted
|
||||
request.GET.urlencode(), # $ tainted
|
||||
|
||||
# django.http.QueryDict (same as above, did not duplicate tests)
|
||||
request.POST, # $ tainted
|
||||
@@ -60,22 +64,23 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
|
||||
# MultiValueDict[str, UploadedFile]
|
||||
request.FILES, # $ tainted
|
||||
request.FILES["key"], # $ tainted
|
||||
request.FILES["key"].content_type, # $ MISSING: tainted
|
||||
request.FILES["key"].content_type_extra, # $ MISSING: tainted
|
||||
request.FILES["key"].content_type_extra["key"], # $ MISSING: tainted
|
||||
request.FILES["key"].charset, # $ MISSING: tainted
|
||||
request.FILES["key"].name, # $ MISSING: tainted
|
||||
request.FILES["key"].file, # $ MISSING: tainted
|
||||
request.FILES["key"].file.read(), # $ MISSING: tainted
|
||||
request.FILES["key"].content_type, # $ tainted
|
||||
request.FILES["key"].content_type_extra, # $ tainted
|
||||
request.FILES["key"].content_type_extra["key"], # $ tainted
|
||||
request.FILES["key"].charset, # $ tainted
|
||||
request.FILES["key"].name, # $ tainted
|
||||
request.FILES["key"].file, # $ tainted
|
||||
request.FILES["key"].file.read(), # $ tainted
|
||||
|
||||
request.FILES.get("key"), # $ tainted
|
||||
request.FILES.get("key").name, # $ MISSING: tainted
|
||||
request.FILES.getlist("key"), # $ MISSING: tainted
|
||||
request.FILES.getlist("key")[0], # $ MISSING: tainted
|
||||
request.FILES.getlist("key")[0].name, # $ MISSING: tainted
|
||||
request.FILES.dict(), # $ MISSING: tainted
|
||||
request.FILES.dict()["key"], # $ MISSING: tainted
|
||||
request.FILES.dict()["key"].name, # $ MISSING: tainted
|
||||
request.FILES.get("key").name, # $ tainted
|
||||
request.FILES.getlist("key"), # $ tainted
|
||||
request.FILES.getlist("key")[0], # $ tainted
|
||||
request.FILES.getlist("key")[0].name, # $ tainted
|
||||
request.FILES.dict(), # $ tainted
|
||||
request.FILES.dict()["key"], # $ tainted
|
||||
request.FILES.dict()["key"].name, # $ tainted
|
||||
request.FILES.dict().get("key").name, # $ tainted
|
||||
|
||||
# Dict[str, Any]
|
||||
request.META, # $ tainted
|
||||
@@ -89,21 +94,21 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
|
||||
|
||||
# django.urls.ResolverMatch
|
||||
request.resolver_match, # $ tainted
|
||||
request.resolver_match.args, # $ MISSING: tainted
|
||||
request.resolver_match.args[0], # $ MISSING: tainted
|
||||
request.resolver_match.kwargs, # $ MISSING: tainted
|
||||
request.resolver_match.kwargs["key"], # $ MISSING: tainted
|
||||
request.resolver_match.args, # $ tainted
|
||||
request.resolver_match.args[0], # $ tainted
|
||||
request.resolver_match.kwargs, # $ tainted
|
||||
request.resolver_match.kwargs["key"], # $ tainted
|
||||
|
||||
request.get_full_path(), # $ MISSING: tainted
|
||||
request.get_full_path_info(), # $ MISSING: tainted
|
||||
request.get_full_path(), # $ tainted
|
||||
request.get_full_path_info(), # $ tainted
|
||||
# build_absolute_uri handled below
|
||||
# get_signed_cookie handled below
|
||||
|
||||
request.read(), # $ MISSING: tainted
|
||||
request.readline(), # $ MISSING: tainted
|
||||
request.readlines(), # $ MISSING: tainted
|
||||
request.readlines()[0], # $ MISSING: tainted
|
||||
[line for line in request], # $ MISSING: tainted
|
||||
request.read(), # $ tainted
|
||||
request.readline(), # $ tainted
|
||||
request.readlines(), # $ tainted
|
||||
request.readlines()[0], # $ tainted
|
||||
[line for line in request], # $ tainted
|
||||
)
|
||||
|
||||
# django.urls.ResolverMatch also supports iterable unpacking
|
||||
@@ -129,9 +134,9 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
|
||||
# build_absolute_uri
|
||||
####################################
|
||||
ensure_tainted(
|
||||
request.build_absolute_uri(), # $ MISSING: tainted
|
||||
request.build_absolute_uri(request.GET["key"]), # $ MISSING: tainted
|
||||
request.build_absolute_uri(location=request.GET["key"]), # $ MISSING: tainted
|
||||
request.build_absolute_uri(), # $ tainted
|
||||
request.build_absolute_uri(request.GET["key"]), # $ tainted
|
||||
request.build_absolute_uri(location=request.GET["key"]), # $ tainted
|
||||
)
|
||||
ensure_not_tainted(
|
||||
request.build_absolute_uri("/hardcoded/"),
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from flask import Flask, request
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/save-uploaded-file") # $routeSetup="/save-uploaded-file"
|
||||
def test_taint(): # $requestHandler
|
||||
request.files['key'].save("path") # $ getAPathArgument="path"
|
||||
@@ -44,7 +44,16 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
|
||||
# werkzeug.datastructures.Authorization (a dict, with some properties)
|
||||
request.authorization, # $ tainted
|
||||
request.authorization['username'], # $ tainted
|
||||
request.authorization.username, # $ MISSING: tainted
|
||||
request.authorization.username, # $ tainted
|
||||
request.authorization.password, # $ tainted
|
||||
request.authorization.realm, # $ tainted
|
||||
request.authorization.nonce, # $ tainted
|
||||
request.authorization.uri, # $ tainted
|
||||
request.authorization.nc, # $ tainted
|
||||
request.authorization.cnonce, # $ tainted
|
||||
request.authorization.response, # $ tainted
|
||||
request.authorization.opaque, # $ tainted
|
||||
request.authorization.qop, # $ tainted
|
||||
|
||||
# werkzeug.datastructures.RequestCacheControl
|
||||
request.cache_control, # $ tainted
|
||||
@@ -68,14 +77,16 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
|
||||
# a werkzeug.datastructures.MultiDict, mapping [str, werkzeug.datastructures.FileStorage]
|
||||
request.files, # $ tainted
|
||||
request.files['key'], # $ tainted
|
||||
request.files['key'].filename, # $ MISSING: tainted
|
||||
request.files['key'].stream, # $ MISSING: tainted
|
||||
request.files['key'].filename, # $ tainted
|
||||
request.files['key'].stream, # $ tainted
|
||||
request.files['key'].read(), # $ tainted
|
||||
request.files['key'].stream.read(), # $ tainted
|
||||
request.files.get('key'), # $ tainted
|
||||
request.files.get('key').filename, # $ MISSING: tainted
|
||||
request.files.get('key').stream, # $ MISSING: tainted
|
||||
request.files.get('key').filename, # $ tainted
|
||||
request.files.get('key').stream, # $ tainted
|
||||
request.files.getlist('key'), # $ tainted
|
||||
request.files.getlist('key')[0].filename, # $ MISSING: tainted
|
||||
request.files.getlist('key')[0].stream, # $ MISSING: tainted
|
||||
request.files.getlist('key')[0].filename, # $ tainted
|
||||
request.files.getlist('key')[0].stream, # $ tainted
|
||||
|
||||
# By default werkzeug.datastructures.ImmutableMultiDict -- although can be changed :\
|
||||
request.form, # $ tainted
|
||||
@@ -94,11 +105,15 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
|
||||
request.headers, # $ tainted
|
||||
request.headers['key'], # $ tainted
|
||||
request.headers.get('key'), # $ tainted
|
||||
request.headers.get_all('key'), # $ MISSING: tainted
|
||||
request.headers.getlist('key'), # $ MISSING: tainted
|
||||
request.headers.get_all('key'), # $ tainted
|
||||
request.headers.getlist('key'), # $ tainted
|
||||
# popitem returns `(key, value)`
|
||||
request.headers.popitem(), # $ tainted
|
||||
request.headers.popitem()[0], # $ tainted
|
||||
request.headers.popitem()[1], # $ tainted
|
||||
# two ways to get (k, v) lists
|
||||
list(request.headers), # $ tainted
|
||||
request.headers.to_wsgi_list(), # $ MISSING: tainted
|
||||
request.headers.to_wsgi_list(), # $ tainted
|
||||
|
||||
request.json, # $ tainted
|
||||
request.json['foo'], # $ tainted
|
||||
|
||||
@@ -26,6 +26,7 @@ nodes
|
||||
| test.py:90:11:90:14 | ControlFlowNode for bm() | semmle.label | ControlFlowNode for bm() |
|
||||
| test.py:91:10:91:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val |
|
||||
| test.py:107:11:107:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() |
|
||||
subpaths
|
||||
#select
|
||||
| test.py:22:10:22:24 | ControlFlowNode for Attribute() | test.py:21:11:21:18 | ControlFlowNode for source() | test.py:22:10:22:24 | ControlFlowNode for Attribute() | test flow (naive): test_simple |
|
||||
| test.py:33:10:33:12 | ControlFlowNode for val | test.py:29:11:29:18 | ControlFlowNode for source() | test.py:33:10:33:12 | ControlFlowNode for val | test flow (naive): test_alias |
|
||||
|
||||
@@ -66,6 +66,7 @@ nodes
|
||||
| test.py:103:46:103:47 | ControlFlowNode for bm | semmle.label | ControlFlowNode for bm |
|
||||
| test.py:107:11:107:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() |
|
||||
| test.py:108:46:108:58 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
subpaths
|
||||
#select
|
||||
| test.py:22:10:22:24 | ControlFlowNode for Attribute() | test.py:21:11:21:18 | ControlFlowNode for source() | test.py:22:10:22:24 | ControlFlowNode for Attribute() | test flow (proper): test_simple |
|
||||
| test.py:33:10:33:12 | ControlFlowNode for val | test.py:29:11:29:18 | ControlFlowNode for source() | test.py:33:10:33:12 | ControlFlowNode for val | test flow (proper): test_alias |
|
||||
|
||||
@@ -58,17 +58,17 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||
self.headers, # $ tainted
|
||||
self.headers['Foo'], # $ tainted
|
||||
self.headers.get('Foo'), # $ tainted
|
||||
self.headers.get_all('Foo'), # $ MISSING: tainted
|
||||
self.headers.keys(), # $ MISSING: tainted
|
||||
self.headers.get_all('Foo'), # $ tainted
|
||||
self.headers.keys(), # $ tainted
|
||||
self.headers.values(), # $ tainted
|
||||
self.headers.items(), # $ tainted
|
||||
self.headers.as_bytes(), # $ MISSING: tainted
|
||||
self.headers.as_string(), # $ MISSING: tainted
|
||||
self.headers.as_bytes(), # $ tainted
|
||||
self.headers.as_string(), # $ tainted
|
||||
str(self.headers), # $ tainted
|
||||
bytes(self.headers), # $ tainted
|
||||
|
||||
self.rfile, # $ tainted
|
||||
self.rfile.read(), # $ MISSING: tainted
|
||||
self.rfile.read(), # $ tainted
|
||||
)
|
||||
|
||||
form = cgi.FieldStorage(
|
||||
|
||||
@@ -61,15 +61,16 @@ class TaintTest(tornado.web.RequestHandler):
|
||||
# dict-like, see https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders
|
||||
request.headers, # $ tainted
|
||||
request.headers["header-name"], # $ tainted
|
||||
request.headers.get_list("header-name"), # $ MISSING: tainted
|
||||
request.headers.get_all(), # $ MISSING: tainted
|
||||
[(k, v) for (k, v) in request.headers.get_all()], # $ MISSING: tainted
|
||||
request.headers.get_list("header-name"), # $ tainted
|
||||
request.headers.get_all(), # $ tainted
|
||||
[(k, v) for (k, v) in request.headers.get_all()], # $ tainted
|
||||
|
||||
# Dict[str, http.cookies.Morsel]
|
||||
request.cookies, # $ tainted
|
||||
request.cookies["cookie-name"], # $ tainted
|
||||
request.cookies["cookie-name"].key, # $ MISSING: tainted
|
||||
request.cookies["cookie-name"].value, # $ MISSING: tainted
|
||||
request.cookies["cookie-name"].key, # $ tainted
|
||||
request.cookies["cookie-name"].value, # $ tainted
|
||||
request.cookies["cookie-name"].coded_value, # $ tainted
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
| x\| | 0 | 2 | x\| | 0 | 1 | x |
|
||||
| x\| | 0 | 2 | x\| | 2 | 2 | |
|
||||
| x\|(?<!\\w)l | 0 | 10 | x\|(?<!\\w)l | 0 | 1 | x |
|
||||
| x\|(?<!\\w)l | 0 | 10 | x\|(?<!\\w)l | 2 | 10 | (?<!\\w)l |
|
||||
| x\|(?<!\\w)l | 0 | 10 | x\|(?<!\\w)l | 2 | 10 | (?<!\\w)l |
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
| [^A-Z] | 2 | 3 |
|
||||
| [^A-Z] | 4 | 5 |
|
||||
| [^]] | 2 | 3 |
|
||||
| \\+0 | 0 | 2 |
|
||||
| \\+0 | 2 | 3 |
|
||||
| \\A[+-]?\\d+ | 0 | 2 |
|
||||
| \\A[+-]?\\d+ | 3 | 4 |
|
||||
| \\A[+-]?\\d+ | 4 | 5 |
|
||||
|
||||
12
python/ql/test/library-tests/regex/Consistency.ql
Normal file
12
python/ql/test/library-tests/regex/Consistency.ql
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Flags regular expressions that are parsed ambigously
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.regex
|
||||
|
||||
from string str, Location loc, int counter
|
||||
where
|
||||
counter = strictcount(Regex term | term.getLocation() = loc and term.getText() = str) and
|
||||
counter > 1
|
||||
select str, counter, loc
|
||||
@@ -42,6 +42,8 @@
|
||||
| [^A-Z] | last | 0 | 6 |
|
||||
| [^]] | first | 0 | 4 |
|
||||
| [^]] | last | 0 | 4 |
|
||||
| \\+0 | first | 0 | 2 |
|
||||
| \\+0 | last | 2 | 3 |
|
||||
| \\A[+-]?\\d+ | first | 0 | 2 |
|
||||
| \\A[+-]?\\d+ | last | 7 | 9 |
|
||||
| \\A[+-]?\\d+ | last | 7 | 10 |
|
||||
|
||||
@@ -113,6 +113,9 @@
|
||||
| [^]] | char | 2 | 3 |
|
||||
| [^]] | char-set | 0 | 4 |
|
||||
| [^]] | sequence | 0 | 4 |
|
||||
| \\+0 | char | 0 | 2 |
|
||||
| \\+0 | char | 2 | 3 |
|
||||
| \\+0 | sequence | 0 | 3 |
|
||||
| \\A[+-]?\\d+ | char | 0 | 2 |
|
||||
| \\A[+-]?\\d+ | char | 3 | 4 |
|
||||
| \\A[+-]?\\d+ | char | 4 | 5 |
|
||||
|
||||
@@ -24,7 +24,8 @@ except re.error:
|
||||
|
||||
re.compile(r'[^A-Z]') #$ charRange=2:3-4:5
|
||||
|
||||
re.compile(r'[\0-\09]') #$ charRange=1:3-4:7
|
||||
re.compile(r'[\0-\09]') #$ charRange=1:3-4:6
|
||||
re.compile(r'[\0-\07]') #$ charRange=1:3-4:7
|
||||
|
||||
re.compile(r'[\0123-5]') #$ charRange=5:6-7:8
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ re.compile(r'[\---]') #$ escapedCharacter=1:3
|
||||
re.compile(r'[--\-]') #$ escapedCharacter=3:5
|
||||
re.compile(r'[\--\-]') #$ escapedCharacter=1:3 escapedCharacter=4:6
|
||||
re.compile(r'[0\-9-A-Z]') #$ escapedCharacter=2:4
|
||||
re.compile(r'[\0-\09]') #$ escapedCharacter=1:3 escapedCharacter=4:7
|
||||
re.compile(r'[\0-\09]') #$ escapedCharacter=1:3 escapedCharacter=4:6
|
||||
re.compile(r'[\0-\07]') #$ escapedCharacter=1:3 escapedCharacter=4:7
|
||||
re.compile(r'[\0123-5]') #$ escapedCharacter=1:5
|
||||
re.compile(r'\1754\1854\17\18\07\08') #$ escapedCharacter=0:4 escapedCharacter=16:19 escapedCharacter=19:21
|
||||
|
||||
#ODASA-3985
|
||||
#Half Surrogate pairs
|
||||
@@ -21,3 +23,9 @@ re.compile(u'[\U00010000-\U0010ffff]') # not escapes
|
||||
|
||||
#Misparsed on LGTM
|
||||
re.compile(r"\[(?P<txt>[^[]*)\]\((?P<uri>[^)]*)") #$ escapedCharacter=0:2 escapedCharacter=16:18 escapedCharacter=18:20
|
||||
|
||||
#Non-raw string
|
||||
re_blank = re.compile('(\n|\r|\\s)*\n', re.M) #$ escapedCharacter=5:7
|
||||
|
||||
#Backreference confusion
|
||||
re.compile(r'\+0') #$ escapedCharacter=0:2
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user