mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Merge branch 'main' into python/support-grouped-exceptions
This commit is contained in:
@@ -1,3 +1,17 @@
|
||||
## 0.6.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.4
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The ReDoS libraries in `semmle.code.python.security.regexp` have been moved to a shared pack inside the `shared/` folder, and the previous location has been deprecated.
|
||||
|
||||
## 0.6.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
7
python/ql/lib/change-notes/2022-11-17-deleted-deps.md
Normal file
7
python/ql/lib/change-notes/2022-11-17-deleted-deps.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted the deprecated `importNode` predicate from the `DataFlowUtil.qll` file.
|
||||
* Deleted the deprecated features from `PEP249.qll` that were not inside the `PEP249` module.
|
||||
* Deleted the deprecated `werkzeug` from the `Werkzeug` module in `Werkzeug.qll`.
|
||||
* Deleted the deprecated `methodResult` predicate from `PEP249::Cursor`.
|
||||
4
python/ql/lib/change-notes/2022-11-17-py-pam-improve.md
Normal file
4
python/ql/lib/change-notes/2022-11-17-py-pam-improve.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* The _PAM authorization bypass due to incorrect usage_ (`py/pam-auth-bypass`) query has been converted to a taint-tracking query, resulting in significantly fewer false positives.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The data-flow library has been rewritten to no longer rely on the points-to analysis in order to
|
||||
resolve references to modules. Improvements in the module resolution can lead to more results.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
- Added `subprocess.getoutput` and `subprocess.getoutputstatus` as new command injection sinks for the StdLib.
|
||||
|
||||
5
python/ql/lib/change-notes/released/0.6.4.md
Normal file
5
python/ql/lib/change-notes/released/0.6.4.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.6.4
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The ReDoS libraries in `semmle.code.python.security.regexp` have been moved to a shared pack inside the `shared/` folder, and the previous location has been deprecated.
|
||||
3
python/ql/lib/change-notes/released/0.6.5.md
Normal file
3
python/ql/lib/change-notes/released/0.6.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.5
|
||||
|
||||
No user-facing changes.
|
||||
3
python/ql/lib/change-notes/released/0.6.6.md
Normal file
3
python/ql/lib/change-notes/released/0.6.6.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.6
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.3
|
||||
lastReleaseVersion: 0.6.6
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
name: codeql/python-all
|
||||
version: 0.6.4-dev
|
||||
version: 0.7.0-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/regex: ${workspace}
|
||||
dataExtensions:
|
||||
- semmle/python/frameworks/**/model.yml
|
||||
|
||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Frameworks
|
||||
private import semmle.python.security.internal.EncryptionKeySizes
|
||||
|
||||
/**
|
||||
* A data-flow node that executes an operating system command,
|
||||
@@ -311,7 +312,7 @@ module CodeExecution {
|
||||
* Often, it is worthy of an alert if an SQL statement is constructed such that
|
||||
* executing it would be a security risk.
|
||||
*
|
||||
* If it is important that the SQL statement is indeed executed, then use `SQLExecution`.
|
||||
* If it is important that the SQL statement is indeed executed, then use `SqlExecution`.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `SqlConstruction::Range` instead.
|
||||
@@ -329,7 +330,7 @@ module SqlConstruction {
|
||||
* Often, it is worthy of an alert if an SQL statement is constructed such that
|
||||
* executing it would be a security risk.
|
||||
*
|
||||
* If it is important that the SQL statement is indeed executed, then use `SQLExecution`.
|
||||
* If it is important that the SQL statement is indeed executed, then use `SqlExecution`.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `SqlConstruction` instead.
|
||||
@@ -1141,21 +1142,21 @@ module Cryptography {
|
||||
abstract class RsaRange extends Range {
|
||||
final override string getName() { result = "RSA" }
|
||||
|
||||
final override int minimumSecureKeySize() { result = 2048 }
|
||||
final override int minimumSecureKeySize() { result = minSecureKeySizeRsa() }
|
||||
}
|
||||
|
||||
/** A data-flow node that generates a new DSA key-pair. */
|
||||
abstract class DsaRange extends Range {
|
||||
final override string getName() { result = "DSA" }
|
||||
|
||||
final override int minimumSecureKeySize() { result = 2048 }
|
||||
final override int minimumSecureKeySize() { result = minSecureKeySizeDsa() }
|
||||
}
|
||||
|
||||
/** A data-flow node that generates a new ECC key-pair. */
|
||||
abstract class EccRange extends Range {
|
||||
final override string getName() { result = "ECC" }
|
||||
|
||||
final override int minimumSecureKeySize() { result = 224 }
|
||||
final override int minimumSecureKeySize() { result = minSecureKeySizeEcc() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,7 @@
|
||||
private import python
|
||||
|
||||
/** A control flow node which might correspond to a special method call. */
|
||||
class PotentialSpecialMethodCallNode extends ControlFlowNode {
|
||||
PotentialSpecialMethodCallNode() { this instanceof SpecialMethod::Potential }
|
||||
}
|
||||
class PotentialSpecialMethodCallNode extends ControlFlowNode instanceof SpecialMethod::Potential { }
|
||||
|
||||
/**
|
||||
* Machinery for detecting special method calls.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* This file contains test-related utility functions */
|
||||
/** This file contains test-related utility functions */
|
||||
|
||||
import python
|
||||
|
||||
/** Removes everything up to the occurrence of `sub` in the string `str` */
|
||||
|
||||
@@ -9,11 +9,12 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
/**
|
||||
* Provides utility predicates related to regular expressions.
|
||||
*/
|
||||
module RegExpPatterns {
|
||||
deprecated module RegExpPatterns {
|
||||
/**
|
||||
* Gets a pattern that matches common top-level domain names in lower case.
|
||||
* DEPRECATED: use the similarly named predicate from `HostnameRegex` from the `regex` pack instead.
|
||||
*/
|
||||
string getACommonTld() {
|
||||
deprecated string getACommonTld() {
|
||||
// according to ranking by http://google.com/search?q=site:.<<TLD>>
|
||||
result = "(?:com|org|edu|gov|uk|net|io)(?![a-z0-9])"
|
||||
}
|
||||
|
||||
@@ -15,13 +15,9 @@ private import semmle.python.Concepts
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `RemoteFlowSource::Range` instead.
|
||||
*/
|
||||
class RemoteFlowSource extends DataFlow::Node {
|
||||
RemoteFlowSource::Range self;
|
||||
|
||||
RemoteFlowSource() { this = self }
|
||||
|
||||
class RemoteFlowSource extends DataFlow::Node instanceof RemoteFlowSource::Range {
|
||||
/** Gets a string that describes the type of this remote flow source. */
|
||||
string getSourceType() { result = self.getSourceType() }
|
||||
string getSourceType() { result = super.getSourceType() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new sources of remote user input. */
|
||||
|
||||
@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
|
||||
private import semmle.python.Frameworks
|
||||
private import semmle.python.security.internal.SensitiveDataHeuristics as SensitiveDataHeuristics
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
// We export these explicitly, so we don't also export the `HeuristicNames` module.
|
||||
class SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClassification;
|
||||
@@ -20,15 +21,22 @@ module SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClass
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `SensitiveDataSource::Range` instead.
|
||||
*/
|
||||
class SensitiveDataSource extends DataFlow::Node {
|
||||
SensitiveDataSource::Range range;
|
||||
|
||||
SensitiveDataSource() { this = range }
|
||||
class SensitiveDataSource extends DataFlow::Node instanceof SensitiveDataSource::Range {
|
||||
SensitiveDataSource() {
|
||||
// ignore sensitive password sources in getpass.py, that can escape through `getpass.getpass()` return value,
|
||||
// since `getpass.getpass()` is considered a source itself.
|
||||
not exists(Module getpass |
|
||||
getpass.getName() = "getpass" and
|
||||
this.getScope().getEnclosingModule() = getpass and
|
||||
// do allow this call if we're analyzing getpass.py as part of CPython though
|
||||
not exists(getpass.getFile().getRelativePath())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the classification of the sensitive data.
|
||||
*/
|
||||
SensitiveDataClassification getClassification() { result = range.getClassification() }
|
||||
SensitiveDataClassification getClassification() { result = super.getClassification() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new sources of sensitive data, such as secrets, certificates, or passwords. */
|
||||
@@ -312,6 +320,17 @@ private module SensitiveDataModeling {
|
||||
|
||||
override SensitiveDataClassification getClassification() { result = classification }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `getpass.getpass`, see https://docs.python.org/3.10/library/getpass.html#getpass.getpass
|
||||
*/
|
||||
class GetPassCall extends SensitiveDataSource::Range, API::CallNode {
|
||||
GetPassCall() { this = API::moduleImport("getpass").getMember("getpass").getACall() }
|
||||
|
||||
override SensitiveDataClassification getClassification() {
|
||||
result = SensitiveDataClassification::password()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
predicate sensitiveDataExtraStepForCalls = SensitiveDataModeling::extraStepForCalls/2;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -915,18 +915,57 @@ private module Cached {
|
||||
TDataFlowCallNone() or
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParamNodeOption =
|
||||
TParamNodeNone() or
|
||||
TParamNodeSome(ParamNode p)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
TAccessPathFrontNone() or
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFrontOption =
|
||||
TApproxAccessPathFrontNone() or
|
||||
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1304,6 +1343,113 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParamNode`. */
|
||||
class ParamNodeOption extends TParamNodeOption {
|
||||
string toString() {
|
||||
this = TParamNodeNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParamNode p |
|
||||
this = TParamNodeSome(p) and
|
||||
result = p.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return context used to calculate flow summaries in reverse flow.
|
||||
*
|
||||
* The possible values are:
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
string toString() {
|
||||
this = TReturnCtxNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
this = TReturnCtxMaybeFlowThrough(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional approximated access path front. */
|
||||
class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
string toString() {
|
||||
this = TApproxAccessPathFrontNone() and result = "<none>"
|
||||
or
|
||||
this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
@@ -1336,7 +1482,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
abstract ApproxAccessPathFront toApprox();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
@@ -1350,7 +1496,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
@@ -1362,7 +1508,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -136,6 +136,18 @@ module Consistency {
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate readStepIsLocal(Node n1, Node n2, string msg) {
|
||||
readStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Read step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
|
||||
storeStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Store step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getNodeType(_) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
@@ -232,4 +244,25 @@ module Consistency {
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
|
||||
query predicate uniqueContentApprox(Content c, string msg) {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -948,3 +948,10 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) {
|
||||
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
|
||||
}
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox = Unit;
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
@@ -26,67 +26,3 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
*/
|
||||
pragma[inline]
|
||||
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`.
|
||||
*
|
||||
* This predicate handles (with optional `... as <new-name>`):
|
||||
* 1. `import <name>`
|
||||
* 2. `from <package> import <module>` when `<name> = <package> + "." + <module>`
|
||||
* 3. `from <module> import <member>` when `<name> = <module> + "." + <member>`
|
||||
*
|
||||
* Finally, in `from <module> import <member>` we consider the `ImportExpr` corresponding to
|
||||
* `<module>` to be a reference to that module.
|
||||
*
|
||||
* Note:
|
||||
* While it is technically possible that `import mypkg.foo` and `from mypkg import foo` can give different values,
|
||||
* it's highly unlikely that this will be a problem in production level code.
|
||||
* Example: If `mypkg/__init__.py` contains `foo = 42`, then `from mypkg import foo` will not import the module
|
||||
* `mypkg/foo.py` but the variable `foo` containing `42` -- however, `import mypkg.foo` will always cause `mypkg.foo`
|
||||
* to refer to the module.
|
||||
*/
|
||||
deprecated Node importNode(string name) {
|
||||
exists(Variable var, Import imp, Alias alias |
|
||||
alias = imp.getAName() and
|
||||
alias.getAsname() = var.getAStore() and
|
||||
(
|
||||
name = alias.getValue().(ImportMember).getImportedModuleName()
|
||||
or
|
||||
name = alias.getValue().(ImportExpr).getImportedModuleName()
|
||||
) and
|
||||
result.asExpr() = alias.getValue()
|
||||
)
|
||||
or
|
||||
// Although it may seem superfluous to consider the `foo` part of `from foo import bar as baz` to
|
||||
// be a reference to a module (since that reference only makes sense locally within the `import`
|
||||
// statement), it's important for our use of type trackers to consider this local reference to
|
||||
// also refer to the `foo` module. That way, if one wants to track references to the `bar`
|
||||
// attribute using a type tracker, one can simply write
|
||||
//
|
||||
// ```ql
|
||||
// DataFlow::Node bar_attr_tracker(TypeTracker t) {
|
||||
// t.startInAttr("bar") and
|
||||
// result = foo_module_tracker()
|
||||
// or
|
||||
// exists(TypeTracker t2 | result = bar_attr_tracker(t2).track(t2, t))
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Where `foo_module_tracker` is a type tracker that tracks references to the `foo` module.
|
||||
// Because named imports are modeled as `AttrRead`s, the statement `from foo import bar as baz`
|
||||
// is interpreted as if it was an assignment `baz = foo.bar`, which means `baz` gets tracked as a
|
||||
// reference to `foo.bar`, as desired.
|
||||
exists(ImportExpr imp_expr |
|
||||
imp_expr.getName() = name and
|
||||
result.asCfgNode().getNode() = imp_expr and
|
||||
// in `import foo.bar` we DON'T want to give a result for `importNode("foo.bar")`,
|
||||
// only for `importNode("foo")`. We exclude those cases with the following clause.
|
||||
not exists(Import imp | imp.getAName().getValue() = imp_expr)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -241,19 +241,25 @@ module Public {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the summary is auto generated.
|
||||
* Holds if the summary is auto generated and not manually generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { none() }
|
||||
}
|
||||
|
||||
/** A callable with a flow summary stating there is no flow via the callable. */
|
||||
class NegativeSummarizedCallable extends SummarizedCallableBase {
|
||||
NegativeSummarizedCallable() { negativeSummaryElement(this, _) }
|
||||
|
||||
/**
|
||||
* Holds if the negative summary is auto generated.
|
||||
* Holds if the summary has the given provenance where `true` is
|
||||
* `generated` and `false` is `manual`.
|
||||
*/
|
||||
predicate isAutoGenerated() { negativeSummaryElement(this, true) }
|
||||
predicate hasProvenance(boolean generated) { none() }
|
||||
}
|
||||
|
||||
/** A callable where there is no flow via the callable. */
|
||||
class NeutralCallable extends SummarizedCallableBase {
|
||||
NeutralCallable() { neutralElement(this, _) }
|
||||
|
||||
/**
|
||||
* Holds if the neutral is auto generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { neutralElement(this, true) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,7 +526,8 @@ module Private {
|
||||
predicate summaryParameterNodeRange(SummarizedCallable c, ParameterPosition pos) {
|
||||
parameterReadState(c, _, pos)
|
||||
or
|
||||
isParameterPostUpdate(_, c, pos)
|
||||
// Same as `isParameterPostUpdate(_, c, pos)`, but can be used in a negative context
|
||||
any(SummaryNodeState state).isOutputState(c, SummaryComponentStack::argument(pos))
|
||||
}
|
||||
|
||||
private predicate callbackOutput(
|
||||
@@ -1011,6 +1018,10 @@ module Private {
|
||||
}
|
||||
|
||||
override predicate isAutoGenerated() { this.relevantSummaryElementGenerated(_, _, _) }
|
||||
|
||||
override predicate hasProvenance(boolean generated) {
|
||||
summaryElement(this, _, _, _, generated)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if component `c` of specification `spec` cannot be parsed. */
|
||||
@@ -1160,9 +1171,9 @@ module Private {
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** A flow summary to include in the `negativeSummary/1` query predicate. */
|
||||
abstract class RelevantNegativeSummarizedCallable instanceof NegativeSummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
/** A model to include in the `neutral/1` query predicate. */
|
||||
abstract class RelevantNeutralCallable instanceof NeutralCallable {
|
||||
/** Gets the string representation of this callable used by `neutral/1`. */
|
||||
abstract string getCallableCsv();
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
@@ -1179,13 +1190,13 @@ module Private {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
private string renderProvenanceNegative(NegativeSummarizedCallable c) {
|
||||
private string renderProvenanceNeutral(NeutralCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
/**
|
||||
* A query predicate for outputting flow summaries in semi-colon separated format in QL tests.
|
||||
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind;provenance"",
|
||||
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind;provenance",
|
||||
* ext is hardcoded to empty.
|
||||
*/
|
||||
query predicate summary(string csv) {
|
||||
@@ -1204,14 +1215,14 @@ module Private {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary `csv` exists (semi-colon separated format). Used for testing purposes.
|
||||
* Holds if a neutral model `csv` exists (semi-colon separated format). Used for testing purposes.
|
||||
* The syntax is: "namespace;type;name;signature;provenance"",
|
||||
*/
|
||||
query predicate negativeSummary(string csv) {
|
||||
exists(RelevantNegativeSummarizedCallable c |
|
||||
query predicate neutral(string csv) {
|
||||
exists(RelevantNeutralCallable c |
|
||||
csv =
|
||||
c.getCallableCsv() // Callable information
|
||||
+ renderProvenanceNegative(c) // provenance
|
||||
+ renderProvenanceNeutral(c) // provenance
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,11 +92,11 @@ predicate summaryElement(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary exists for `c`, which means that there is no
|
||||
* flow through `c`. The flag `generated` states whether the summary is autogenerated.
|
||||
* Note. Negative flow summaries has not been implemented for Python.
|
||||
* Holds if a neutral model exists for `c`, which means that there is no
|
||||
* flow through `c`. The flag `generated` states whether the neutral model is autogenerated.
|
||||
* Note. Neutral models have not been implemented for Python.
|
||||
*/
|
||||
predicate negativeSummaryElement(FlowSummary::SummarizedCallable c, boolean generated) { none() }
|
||||
predicate neutralElement(FlowSummary::SummarizedCallable c, boolean generated) { none() }
|
||||
|
||||
/**
|
||||
* Gets the summary component for specification component `c`, if any.
|
||||
|
||||
@@ -1,14 +1,75 @@
|
||||
/**
|
||||
* INTERNAL. DO NOT USE.
|
||||
*
|
||||
* Provides predicates for resolving imports.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
private import semmle.python.dataflow.new.TypeTracker
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Python modules and the way imports are resolved are... complicated. Here's a crash course in how
|
||||
* it works, as well as some caveats to bear in mind when looking at the implementation in this
|
||||
* module.
|
||||
*
|
||||
* First, let's consider the humble `import` statement:
|
||||
* ```python
|
||||
* import foo
|
||||
* import bar.baz
|
||||
* import ham.eggs as spam
|
||||
* ```
|
||||
*
|
||||
* In the AST, all imports are aliased, as in the last import above. That is, `import foo` becomes
|
||||
* `import foo as foo`, and `import bar.baz` becomes `import bar as bar`. Note that `import` is
|
||||
* exclusively used to import modules -- if `eggs` is an attribute of the `ham` module (and not a
|
||||
* submodule of the `ham` package), then the third line above is an error.
|
||||
*
|
||||
* Next, we have the `from` statement. This one is a bit more complicated, but still has the same
|
||||
* aliasing desugaring as above applied to it. Thus, `from foo import bar` becomes
|
||||
* `from foo import bar as bar`.
|
||||
*
|
||||
* In general, `from foo import bar` can mean two different things:
|
||||
*
|
||||
* 1. If `foo` is a module, and `bar` is an attribute of `foo`, then `from foo import bar` imports
|
||||
* the attribute `bar` into the current module (binding it to the name `bar`).
|
||||
* 2. If `foo` is a package, and `bar` is already defined in `foo/__init__.py`,
|
||||
* that value will be imported. If it is not defined, and `bar` is a submodule of `foo`, then
|
||||
* `bar` is imported to `foo`, and the `bar` submodule imported.
|
||||
* Note: We don't currently model if the attribute is already defined in `__init__.py`
|
||||
* and always assume that the submodule will be used.
|
||||
*
|
||||
* Now, when it comes to how these imports are represented in the AST, things get a bit complicated.
|
||||
* First of all, both of the above forms of imports get mapped to the same kind of AST node:
|
||||
* `Import`. An `Import` node has a sequence of names, each of which is an `Alias` node. This `Alias`
|
||||
* node represents the `x as y` bit of each imported module.
|
||||
*
|
||||
* The same is true for `from` imports. So, how then do we distinguish between the two forms of
|
||||
* imports? The distinguishing feature is the left hand side of the `as` node. If the left hand side
|
||||
* is an `ImportExpr`, then it is a plain import. If it is an `ImportMember`, then it is a `from`
|
||||
* import. (And to confuse matters even more, this `ImportMember` contains another `ImportExpr` for
|
||||
* the bit between the `from` and `import` keywords.)
|
||||
*
|
||||
* Caveats:
|
||||
*
|
||||
* - A relative import of the form `from .foo import bar as baz` not only imports `bar` and binds it
|
||||
* to the name `baz`, but also imports `foo` and binds it to the name `foo`. This only happens with
|
||||
* relative imports. `from foo import bar as baz` only binds `bar` to `baz`.
|
||||
* - Modules may also be packages, so e.g. `import foo.bar` may import the `bar` submodule in the `foo`
|
||||
* package, or the `bar` subpackage of the `foo` package. The practical difference here is the name of
|
||||
* the module that is imported, as the package `foo.bar` will have the "name" `foo.bar.__init__`,
|
||||
* corresponding to the fact that the code that is executed is in the `__init__.py` file of the
|
||||
* `bar` subpackage.
|
||||
*/
|
||||
module ImportResolution {
|
||||
/**
|
||||
* Holds if the module `m` defines a name `name` by assigning `defn` to it. This is an
|
||||
* overapproximation, as `name` may not in fact be exported (e.g. by defining an `__all__` that does
|
||||
* not include `name`).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate module_export(Module m, string name, DataFlow::CfgNode defn) {
|
||||
exists(EssaVariable v |
|
||||
v.getName() = name and
|
||||
@@ -18,12 +79,223 @@ module ImportResolution {
|
||||
or
|
||||
defn.getNode() = v.getDefinition().(ArgumentRefinement).getArgument()
|
||||
)
|
||||
}
|
||||
|
||||
Module getModule(DataFlow::CfgNode node) {
|
||||
exists(ModuleValue mv |
|
||||
node.getNode().pointsTo(mv) and
|
||||
result = mv.getScope()
|
||||
or
|
||||
exists(Alias a |
|
||||
defn.asExpr() = [a.getValue(), a.getValue().(ImportMember).getModule()] and
|
||||
a.getAsname().(Name).getId() = name and
|
||||
defn.getScope() = m
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the module `m` explicitly exports the name `name` by listing it in `__all__`. Only
|
||||
* handles simple cases where we can statically tell that this is the case.
|
||||
*/
|
||||
private predicate all_mentions_name(Module m, string name) {
|
||||
exists(DefinitionNode def, SequenceNode n |
|
||||
def.getValue() = n and
|
||||
def.(NameNode).getId() = "__all__" and
|
||||
def.getScope() = m and
|
||||
any(StrConst s | s.getText() = name) = n.getAnElement().getNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the module `m` either does not set `__all__` (and so implicitly exports anything that
|
||||
* doesn't start with an underscore), or sets `__all__` in a way that's too complicated for us to
|
||||
* handle (in which case we _also_ pretend that it just exports all such names).
|
||||
*/
|
||||
private predicate no_or_complicated_all(Module m) {
|
||||
// No mention of `__all__` in the module
|
||||
not exists(DefinitionNode def | def.getScope() = m and def.(NameNode).getId() = "__all__")
|
||||
or
|
||||
// `__all__` is set to a non-sequence value
|
||||
exists(DefinitionNode def |
|
||||
def.(NameNode).getId() = "__all__" and
|
||||
def.getScope() = m and
|
||||
not def.getValue() instanceof SequenceNode
|
||||
)
|
||||
or
|
||||
// `__all__` is used in some way that doesn't involve storing a value in it. This usually means
|
||||
// it is being mutated through `append` or `extend`, which we don't handle.
|
||||
exists(NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
|
||||
}
|
||||
|
||||
private predicate potential_module_export(Module m, string name) {
|
||||
all_mentions_name(m, name)
|
||||
or
|
||||
no_or_complicated_all(m) and
|
||||
(
|
||||
exists(NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
|
||||
or
|
||||
exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the module `reexporter` exports the module `reexported` under the name
|
||||
* `reexported_name`.
|
||||
*/
|
||||
private predicate module_reexport(Module reexporter, string reexported_name, Module reexported) {
|
||||
exists(DataFlow::Node ref |
|
||||
ref = getImmediateModuleReference(reexported) and
|
||||
module_export(reexporter, reexported_name, ref) and
|
||||
potential_module_export(reexporter, reexported_name)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to `sys.modules`.
|
||||
*/
|
||||
private DataFlow::Node sys_modules_reference() {
|
||||
result =
|
||||
any(DataFlow::AttrRef a |
|
||||
a.getAttributeName() = "modules" and a.getObject().asExpr().(Name).getId() = "sys"
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a module that may have been added to `sys.modules`. */
|
||||
private Module sys_modules_module_with_name(string name) {
|
||||
exists(ControlFlowNode n, DataFlow::Node mod |
|
||||
exists(SubscriptNode sub |
|
||||
sub.getObject() = sys_modules_reference().asCfgNode() and
|
||||
sub.getIndex() = n and
|
||||
n.getNode().(StrConst).getText() = name and
|
||||
sub.(DefinitionNode).getValue() = mod.asCfgNode() and
|
||||
mod = getModuleReference(result)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Module getModuleImportedByImportStar(ImportStar i) {
|
||||
isPreferredModuleForName(result.getFile(), i.getImportedModuleName())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node that may be a reference to a module with the name `module_name`.
|
||||
*
|
||||
* This is a helper predicate for `getImmediateModuleReference`. It captures the fact that in an
|
||||
* import such as `import foo`,
|
||||
* - `foo` may simply be the name of a module, or
|
||||
* - `foo` may be the name of a package (in which case its name is actually `foo.__init__`), or
|
||||
* - `foo` may be a module name that has been added to `sys.modules` (in which case its actual name can
|
||||
* be anything, for instance `os.path` is either `posixpath` or `ntpath`).
|
||||
*/
|
||||
private DataFlow::Node getReferenceToModuleName(string module_name) {
|
||||
// Regular import statements, e.g.
|
||||
// import foo # implicitly `import foo as foo`
|
||||
// import foo as foo_alias
|
||||
exists(Import i, Alias a | a = i.getAName() |
|
||||
result.asExpr() = a.getAsname() and
|
||||
module_name = a.getValue().(ImportExpr).getImportedModuleName()
|
||||
)
|
||||
or
|
||||
// The module part of a `from ... import ...` statement, e.g. the `..foo.bar` in
|
||||
// from ..foo.bar import baz # ..foo.bar might point to, say, package.subpackage.foo.bar
|
||||
exists(ImportMember i | result.asExpr() = i.getModule() |
|
||||
module_name = i.getModule().(ImportExpr).getImportedModuleName()
|
||||
)
|
||||
or
|
||||
// Modules (not attributes) imported via `from ... import ... statements`, e.g.
|
||||
// from foo.bar import baz # imports foo.bar.baz as baz
|
||||
// from foo.bar import baz as baz_alias # imports foo.bar.baz as baz_alias
|
||||
exists(Import i, Alias a, ImportMember im | a = i.getAName() and im = a.getValue() |
|
||||
result.asExpr() = a.getAsname() and
|
||||
module_name = im.getModule().(ImportExpr).getImportedModuleName() + "." + im.getName()
|
||||
)
|
||||
or
|
||||
// For parity with the points-to based solution, the `ImportExpr` and `ImportMember` bits of the
|
||||
// above cases should _also_ point to the right modules.
|
||||
result.asExpr() = any(ImportExpr i | i.getImportedModuleName() = module_name)
|
||||
or
|
||||
result.asExpr() =
|
||||
any(ImportMember i |
|
||||
i.getModule().(ImportExpr).getImportedModuleName() + "." + i.getName() = module_name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a dataflow node that is an immediate reference to the module `m`.
|
||||
*
|
||||
* Because of attribute lookups, this is mutually recursive with `getModuleReference`.
|
||||
*/
|
||||
DataFlow::Node getImmediateModuleReference(Module m) {
|
||||
exists(string module_name | result = getReferenceToModuleName(module_name) |
|
||||
// Depending on whether the referenced module is a package or not, we may need to add a
|
||||
// trailing `.__init__` to the module name.
|
||||
isPreferredModuleForName(m.getFile(), module_name + ["", ".__init__"])
|
||||
or
|
||||
// Module defined via `sys.modules`
|
||||
m = sys_modules_module_with_name(module_name)
|
||||
)
|
||||
or
|
||||
// Reading an attribute on a module may return a submodule (or subpackage).
|
||||
exists(DataFlow::AttrRead ar, Module p, string attr_name |
|
||||
ar.accesses(getModuleReference(p), attr_name) and
|
||||
result = ar
|
||||
|
|
||||
isPreferredModuleForName(m.getFile(), p.getPackageName() + "." + attr_name + ["", ".__init__"])
|
||||
)
|
||||
or
|
||||
// This is also true for attributes that come from reexports.
|
||||
exists(Module reexporter, string attr_name |
|
||||
result.(DataFlow::AttrRead).accesses(getModuleReference(reexporter), attr_name) and
|
||||
module_reexport(reexporter, attr_name, m)
|
||||
)
|
||||
or
|
||||
// Submodules that are implicitly defined with relative imports of the form `from .foo import ...`.
|
||||
// In practice, we create a definition for each module in a package, even if it is not imported.
|
||||
exists(string submodule, Module package |
|
||||
SsaSource::init_module_submodule_defn(result.asVar().getSourceVariable(),
|
||||
package.getEntryNode()) and
|
||||
isPreferredModuleForName(m.getFile(),
|
||||
package.getPackageName() + "." + submodule + ["", ".__init__"])
|
||||
)
|
||||
}
|
||||
|
||||
/** Join-order helper for `getModuleReference`. */
|
||||
pragma[nomagic]
|
||||
private predicate module_reference_in_scope(DataFlow::Node node, Scope s, string name, Module m) {
|
||||
node.getScope() = s and
|
||||
node.asExpr().(Name).getId() = name and
|
||||
pragma[only_bind_into](node) = getImmediateModuleReference(pragma[only_bind_into](m))
|
||||
}
|
||||
|
||||
/** Join-order helper for `getModuleReference`. */
|
||||
pragma[nomagic]
|
||||
private predicate module_name_in_scope(DataFlow::Node node, Scope s, string name) {
|
||||
node.getScope() = s and
|
||||
exists(Name n | n = node.asExpr() |
|
||||
n.getId() = name and
|
||||
pragma[only_bind_into](n).isUse()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the module `m` (including through certain kinds of local and global flow).
|
||||
*/
|
||||
DataFlow::Node getModuleReference(Module m) {
|
||||
// Immedate references to the module
|
||||
result = getImmediateModuleReference(m)
|
||||
or
|
||||
// Flow (local or global) forward to a later reference to the module.
|
||||
exists(DataFlow::Node ref | ref = getModuleReference(m) |
|
||||
simpleLocalFlowStepForTypetracking(ref, result)
|
||||
or
|
||||
exists(DataFlow::ModuleVariableNode mv |
|
||||
mv.getAWrite() = ref and
|
||||
result = mv.getARead()
|
||||
)
|
||||
)
|
||||
or
|
||||
// A reference to a name that is bound to a module in an enclosing scope.
|
||||
exists(DataFlow::Node def, Scope def_scope, Scope use_scope, string name |
|
||||
module_reference_in_scope(pragma[only_bind_into](def), pragma[only_bind_into](def_scope),
|
||||
pragma[only_bind_into](name), pragma[only_bind_into](m)) and
|
||||
module_name_in_scope(result, use_scope, name) and
|
||||
use_scope.getEnclosingScope*() = def_scope
|
||||
)
|
||||
}
|
||||
|
||||
Module getModule(DataFlow::CfgNode node) { node = getModuleReference(result) }
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ module ImportStar {
|
||||
exists(ImportStar i, DataFlow::CfgNode imported_module |
|
||||
imported_module.getNode().getNode() = i.getModule() and
|
||||
i.getScope() = m and
|
||||
result = ImportResolution::getModule(imported_module)
|
||||
result = ImportResolution::getModuleImportedByImportStar(i)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -207,16 +207,13 @@ class AssignmentTarget extends ControlFlowNode {
|
||||
}
|
||||
|
||||
/** A direct (or top-level) target of an unpacking assignment. */
|
||||
class UnpackingAssignmentDirectTarget extends ControlFlowNode {
|
||||
class UnpackingAssignmentDirectTarget extends ControlFlowNode instanceof SequenceNode {
|
||||
Expr value;
|
||||
|
||||
UnpackingAssignmentDirectTarget() {
|
||||
this instanceof SequenceNode and
|
||||
(
|
||||
value = this.(AssignmentTarget).getValue()
|
||||
or
|
||||
value = this.(ForTarget).getSource()
|
||||
)
|
||||
value = this.(AssignmentTarget).getValue()
|
||||
or
|
||||
value = this.(ForTarget).getSource()
|
||||
}
|
||||
|
||||
Expr getValue() { result = value }
|
||||
|
||||
@@ -197,9 +197,7 @@ class TaintTrackingNode extends TTaintTrackingNode {
|
||||
* It is implemented as a separate class for clarity and to keep the code
|
||||
* in `TaintTracking::Configuration` simpler.
|
||||
*/
|
||||
class TaintTrackingImplementation extends string {
|
||||
TaintTrackingImplementation() { this instanceof TaintTracking::Configuration }
|
||||
|
||||
class TaintTrackingImplementation extends string instanceof TaintTracking::Configuration {
|
||||
/**
|
||||
* Hold if there is a flow from `source`, which is a taint source, to
|
||||
* `sink`, which is a taint sink, with this configuration.
|
||||
@@ -218,7 +216,7 @@ class TaintTrackingImplementation extends string {
|
||||
) {
|
||||
context = TNoParam() and
|
||||
path = TNoAttribute() and
|
||||
this.(TaintTracking::Configuration).isSource(node, kind)
|
||||
super.isSource(node, kind)
|
||||
}
|
||||
|
||||
/** Hold if `source` is a source of taint. */
|
||||
@@ -234,7 +232,7 @@ class TaintTrackingImplementation extends string {
|
||||
exists(DataFlow::Node node, AttributePath path, TaintKind kind |
|
||||
sink = TTaintTrackingNode_(node, _, path, kind, this) and
|
||||
path = TNoAttribute() and
|
||||
this.(TaintTracking::Configuration).isSink(node, kind)
|
||||
super.isSink(node, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -259,11 +257,11 @@ class TaintTrackingImplementation extends string {
|
||||
) {
|
||||
this.unprunedStep(src, node, context, path, kind, edgeLabel) and
|
||||
node.getBasicBlock().likelyReachable() and
|
||||
not this.(TaintTracking::Configuration).isBarrier(node) and
|
||||
not super.isBarrier(node) and
|
||||
(
|
||||
not path = TNoAttribute()
|
||||
or
|
||||
not this.(TaintTracking::Configuration).isBarrier(node, kind) and
|
||||
not super.isBarrier(node, kind) and
|
||||
exists(DataFlow::Node srcnode, TaintKind srckind |
|
||||
src = TTaintTrackingNode_(srcnode, _, _, srckind, this) and
|
||||
not this.prunedEdge(srcnode, node, srckind, kind)
|
||||
@@ -274,9 +272,9 @@ class TaintTrackingImplementation extends string {
|
||||
private predicate prunedEdge(
|
||||
DataFlow::Node srcnode, DataFlow::Node destnode, TaintKind srckind, TaintKind destkind
|
||||
) {
|
||||
this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode, srckind, destkind)
|
||||
super.isBarrierEdge(srcnode, destnode, srckind, destkind)
|
||||
or
|
||||
srckind = destkind and this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode)
|
||||
srckind = destkind and super.isBarrierEdge(srcnode, destnode)
|
||||
}
|
||||
|
||||
private predicate unprunedStep(
|
||||
@@ -314,14 +312,14 @@ class TaintTrackingImplementation extends string {
|
||||
this.legacyExtensionStep(src, node, context, path, kind, edgeLabel)
|
||||
or
|
||||
exists(DataFlow::Node srcnode, TaintKind srckind |
|
||||
this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node, srckind, kind) and
|
||||
super.isAdditionalFlowStep(srcnode, node, srckind, kind) and
|
||||
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
|
||||
path.noAttribute() and
|
||||
edgeLabel = "additional with kind"
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node srcnode |
|
||||
this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node) and
|
||||
super.isAdditionalFlowStep(srcnode, node) and
|
||||
src = TTaintTrackingNode_(srcnode, context, path, kind, this) and
|
||||
path.noAttribute() and
|
||||
edgeLabel = "additional"
|
||||
@@ -618,7 +616,7 @@ class TaintTrackingImplementation extends string {
|
||||
TaintKind kind, string edgeLabel
|
||||
) {
|
||||
exists(TaintTracking::Extension extension, DataFlow::Node srcnode, TaintKind srckind |
|
||||
this.(TaintTracking::Configuration).isExtension(extension) and
|
||||
super.isExtension(extension) and
|
||||
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
|
||||
srcnode.asCfgNode() = extension
|
||||
|
|
||||
@@ -646,9 +644,7 @@ class TaintTrackingImplementation extends string {
|
||||
* Another taint-tracking class to help partition the code for clarity
|
||||
* This class handle tracking of ESSA variables.
|
||||
*/
|
||||
private class EssaTaintTracking extends string {
|
||||
EssaTaintTracking() { this instanceof TaintTracking::Configuration }
|
||||
|
||||
private class EssaTaintTracking extends string instanceof TaintTracking::Configuration {
|
||||
pragma[noinline]
|
||||
predicate taintedDefinition(
|
||||
TaintTrackingNode src, EssaDefinition defn, TaintTrackingContext context, AttributePath path,
|
||||
@@ -691,7 +687,7 @@ private class EssaTaintTracking extends string {
|
||||
defn = phi.asVariable().getDefinition() and
|
||||
predvar = defn.getInput(pred) and
|
||||
not pred.unlikelySuccessor(defn.getBasicBlock()) and
|
||||
not this.(TaintTracking::Configuration).isBarrierEdge(srcnode, phi) and
|
||||
not super.isBarrierEdge(srcnode, phi) and
|
||||
srcnode.asVariable() = predvar
|
||||
)
|
||||
}
|
||||
@@ -781,7 +777,7 @@ private class EssaTaintTracking extends string {
|
||||
exists(DataFlow::Node srcnode |
|
||||
src = TTaintTrackingNode_(srcnode, context, path, kind, this) and
|
||||
srcnode.asVariable() = defn.getInput() and
|
||||
not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense())
|
||||
not super.isBarrierTest(defn.getTest(), defn.getSense())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -801,7 +797,7 @@ private class EssaTaintTracking extends string {
|
||||
) {
|
||||
exists(DataFlow::Node srcnode, ControlFlowNode use |
|
||||
src = TTaintTrackingNode_(srcnode, context, path, kind, this) and
|
||||
not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) and
|
||||
not super.isBarrierTest(defn.getTest(), defn.getSense()) and
|
||||
defn.getSense() = this.testEvaluates(defn, defn.getTest(), use, src)
|
||||
)
|
||||
}
|
||||
@@ -815,7 +811,7 @@ private class EssaTaintTracking extends string {
|
||||
src = TTaintTrackingNode_(srcnode, context, path, kind, this) and
|
||||
piNodeTestAndUse(defn, test, use) and
|
||||
srcnode.asVariable() = defn.getInput() and
|
||||
not this.(TaintTracking::Configuration).isBarrierTest(test, defn.getSense()) and
|
||||
not super.isBarrierTest(test, defn.getSense()) and
|
||||
testEvaluatesMaybe(test, use)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -207,22 +207,19 @@ class BuiltinVariable extends SsaSourceVariable {
|
||||
override CallNode redefinedAtCallSite() { none() }
|
||||
}
|
||||
|
||||
class ModuleVariable extends SsaSourceVariable {
|
||||
class ModuleVariable extends SsaSourceVariable instanceof GlobalVariable {
|
||||
ModuleVariable() {
|
||||
this instanceof GlobalVariable and
|
||||
(
|
||||
exists(this.(Variable).getAStore())
|
||||
or
|
||||
this.(Variable).getId() = "__name__"
|
||||
or
|
||||
this.(Variable).getId() = "__package__"
|
||||
or
|
||||
exists(ImportStar is | is.getScope() = this.(Variable).getScope())
|
||||
)
|
||||
exists(this.(Variable).getAStore())
|
||||
or
|
||||
this.(Variable).getId() = "__name__"
|
||||
or
|
||||
this.(Variable).getId() = "__package__"
|
||||
or
|
||||
exists(ImportStar is | is.getScope() = this.(Variable).getScope())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() }
|
||||
private Scope scope_as_global_variable() { result = GlobalVariable.super.getScope() }
|
||||
|
||||
pragma[noinline]
|
||||
CallNode global_variable_callnode() { result.getScope() = this.scope_as_global_variable() }
|
||||
@@ -265,7 +262,7 @@ class ModuleVariable extends SsaSourceVariable {
|
||||
class_with_global_metaclass(s, this)
|
||||
or
|
||||
/* Variable is used in scope */
|
||||
this.(GlobalVariable).getAUse().getScope() = s
|
||||
GlobalVariable.super.getAUse().getScope() = s
|
||||
)
|
||||
or
|
||||
exists(ImportTimeScope scope | scope.entryEdge(_, result) |
|
||||
|
||||
@@ -59,24 +59,24 @@ module AiohttpWebModel {
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `AiohttpRouteSetup::Range` instead.
|
||||
*/
|
||||
class AiohttpRouteSetup extends Http::Server::RouteSetup::Range {
|
||||
AiohttpRouteSetup::Range range;
|
||||
|
||||
AiohttpRouteSetup() { this = range }
|
||||
|
||||
class AiohttpRouteSetup extends Http::Server::RouteSetup::Range instanceof AiohttpRouteSetup::Range {
|
||||
override Parameter getARoutedParameter() { none() }
|
||||
|
||||
override string getFramework() { result = "aiohttp.web" }
|
||||
|
||||
/** Gets the argument specifying the handler (either a coroutine or a view-class). */
|
||||
DataFlow::Node getHandlerArg() { result = range.getHandlerArg() }
|
||||
DataFlow::Node getHandlerArg() { result = super.getHandlerArg() }
|
||||
|
||||
override DataFlow::Node getUrlPatternArg() { result = range.getUrlPatternArg() }
|
||||
override DataFlow::Node getUrlPatternArg() {
|
||||
result = AiohttpRouteSetup::Range.super.getUrlPatternArg()
|
||||
}
|
||||
|
||||
/** Gets the view-class that is referenced in the view-class handler argument, if any. */
|
||||
Class getViewClass() { result = range.getViewClass() }
|
||||
Class getViewClass() { result = super.getViewClass() }
|
||||
|
||||
override Function getARequestHandler() { result = range.getARequestHandler() }
|
||||
override Function getARequestHandler() {
|
||||
result = AiohttpRouteSetup::Range.super.getARequestHandler()
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new aiohttp.web route setups. */
|
||||
|
||||
@@ -13,35 +13,36 @@ private import semmle.python.frameworks.data.ModelsAsData
|
||||
private module Asyncpg {
|
||||
class AsyncpgModel extends ModelInput::TypeModelCsv {
|
||||
override predicate row(string row) {
|
||||
// package1;type1;package2;type2;path
|
||||
// type1;type2;path
|
||||
row =
|
||||
[
|
||||
// a `ConnectionPool` that is created when the result of `asyncpg.create_pool()` is awaited.
|
||||
"asyncpg;ConnectionPool;asyncpg;;Member[create_pool].ReturnValue.Awaited",
|
||||
"asyncpg.ConnectionPool;asyncpg;Member[create_pool].ReturnValue.Awaited",
|
||||
// a `Connection` that is created when
|
||||
// * - the result of `asyncpg.connect()` is awaited.
|
||||
// * - the result of calling `acquire` on a `ConnectionPool` is awaited.
|
||||
"asyncpg;Connection;asyncpg;;Member[connect].ReturnValue.Awaited",
|
||||
"asyncpg;Connection;asyncpg;ConnectionPool;Member[acquire].ReturnValue.Awaited",
|
||||
"asyncpg.Connection;asyncpg;Member[connect].ReturnValue.Awaited",
|
||||
"asyncpg.Connection;asyncpg.ConnectionPool;Member[acquire].ReturnValue.Awaited",
|
||||
// Creating an internal `~Connection` type that contains both `Connection` and `ConnectionPool`.
|
||||
"asyncpg;~Connection;asyncpg;Connection;", "asyncpg;~Connection;asyncpg;ConnectionPool;"
|
||||
"asyncpg.~Connection;asyncpg.Connection;", //
|
||||
"asyncpg.~Connection;asyncpg.ConnectionPool;"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncpgSink extends ModelInput::SinkModelCsv {
|
||||
// package;type;path;kind
|
||||
// type;path;kind
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// `Connection`s and `ConnectionPool`s provide some methods that execute SQL.
|
||||
"asyncpg;~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection",
|
||||
"asyncpg;~Connection;Member[executemany].Argument[0,command:];sql-injection",
|
||||
"asyncpg.~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection",
|
||||
"asyncpg.~Connection;Member[executemany].Argument[0,command:];sql-injection",
|
||||
// A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system.
|
||||
"asyncpg;~Connection;Member[copy_from_query,copy_from_table].Argument[output:];path-injection",
|
||||
"asyncpg;~Connection;Member[copy_to_table].Argument[source:];path-injection",
|
||||
"asyncpg.~Connection;Member[copy_from_query,copy_from_table].Argument[output:];path-injection",
|
||||
"asyncpg.~Connection;Member[copy_to_table].Argument[source:];path-injection",
|
||||
// the `PreparedStatement` class in `asyncpg`.
|
||||
"asyncpg;Connection;Member[prepare].Argument[0,query:];sql-injection",
|
||||
"asyncpg.Connection;Member[prepare].Argument[0,query:];sql-injection",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58,7 +59,7 @@ private module Asyncpg {
|
||||
module Cursor {
|
||||
class CursorConstruction extends SqlConstruction::Range, API::CallNode {
|
||||
CursorConstruction() {
|
||||
this = ModelOutput::getATypeNode("asyncpg", "Connection").getMember("cursor").getACall()
|
||||
this = ModelOutput::getATypeNode("asyncpg.Connection").getMember("cursor").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
|
||||
@@ -76,7 +77,7 @@ private module Asyncpg {
|
||||
or
|
||||
exists(API::CallNode prepareCall |
|
||||
prepareCall =
|
||||
ModelOutput::getATypeNode("asyncpg", "Connection").getMember("prepare").getACall()
|
||||
ModelOutput::getATypeNode("asyncpg.Connection").getMember("prepare").getACall()
|
||||
|
|
||||
sql = prepareCall.getParameter(0, "query").asSink() and
|
||||
this =
|
||||
|
||||
@@ -2526,11 +2526,10 @@ module PrivateDjango {
|
||||
*
|
||||
* Needs this subclass to be considered a RegexString.
|
||||
*/
|
||||
private class DjangoRouteRegex extends RegexString {
|
||||
private class DjangoRouteRegex extends RegexString instanceof StrConst {
|
||||
DjangoRegexRouteSetup rePathCall;
|
||||
|
||||
DjangoRouteRegex() {
|
||||
this instanceof StrConst and
|
||||
rePathCall.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(this)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,29 +8,170 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
import semmle.python.frameworks.internal.PEP249Impl
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `PEP249::PEP249ModuleApiNode` instead.
|
||||
* Provides classes modeling database interfaces following PEP 249.
|
||||
* See https://www.python.org/dev/peps/pep-0249/.
|
||||
*/
|
||||
deprecated class PEP249ModuleApiNode = PEP249::PEP249ModuleApiNode;
|
||||
module PEP249 {
|
||||
/**
|
||||
* An API graph node representing a module that implements PEP 249.
|
||||
*/
|
||||
abstract class PEP249ModuleApiNode extends API::Node {
|
||||
/** Gets a string representation of this element. */
|
||||
override string toString() { result = this.(API::Node).toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `PEP249::Connection` instead.
|
||||
*/
|
||||
deprecated module Connection = PEP249::Connection;
|
||||
/** Gets a reference to the `connect` function of a module that implements PEP 249. */
|
||||
DataFlow::Node connect() {
|
||||
result = any(PEP249ModuleApiNode a).getMember("connect").getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `PEP249::Cursor` instead.
|
||||
*/
|
||||
deprecated module cursor = PEP249::Cursor;
|
||||
/**
|
||||
* Provides models for database connections (following PEP 249).
|
||||
*
|
||||
* See https://www.python.org/dev/peps/pep-0249/#connection-objects.
|
||||
*/
|
||||
module Connection {
|
||||
/**
|
||||
* A source of database connections (following PEP 249), 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 external
|
||||
* libraries.
|
||||
*
|
||||
* Use the predicate `Connection::instance()` to get references to database connections (following PEP 249).
|
||||
*
|
||||
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
|
||||
* a connection than going through `connect`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `PEP249::execute` instead.
|
||||
*/
|
||||
deprecated predicate execute = PEP249::execute/0;
|
||||
/** A call to the `connect` function of a module that implements PEP 249. */
|
||||
private class ConnectCall extends InstanceSource, DataFlow::CallCfgNode {
|
||||
ConnectCall() { this.getFunction() = connect() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `PEP249::connect` instead.
|
||||
*/
|
||||
deprecated predicate connect = PEP249::connect/0;
|
||||
/** Gets a reference to a database connection (following PEP 249). */
|
||||
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 database connection (following PEP 249). */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for database cursors (following PEP 249).
|
||||
*
|
||||
* These are returned by the `cursor` method on a database connection.
|
||||
* See https://www.python.org/dev/peps/pep-0249/#cursor.
|
||||
*/
|
||||
module Cursor {
|
||||
/**
|
||||
* A source of database cursors (following PEP 249), 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 external
|
||||
* libraries.
|
||||
*
|
||||
* Use the predicate `Cursor::instance()` to get references to database cursors (following PEP 249).
|
||||
*
|
||||
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
|
||||
* a connection than going through `connect`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to a database cursor. */
|
||||
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 database cursor. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** Gets a reference to the `cursor` method on a database connection. */
|
||||
private DataFlow::TypeTrackingNode methodRef(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("cursor") and
|
||||
result = Connection::instance()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `cursor` method on a database connection. */
|
||||
DataFlow::Node methodRef() { methodRef(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** A call to the `cursor` method on a database connection */
|
||||
private class CursorCall extends InstanceSource, DataFlow::CallCfgNode {
|
||||
CursorCall() { this.getFunction() = methodRef() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `execute` method on a cursor (or on a connection).
|
||||
*
|
||||
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
||||
*
|
||||
* See https://peps.python.org/pep-0249/#execute.
|
||||
*/
|
||||
private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("execute") and
|
||||
result in [Cursor::instance(), Connection::instance()]
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `execute` method on a cursor (or on a connection).
|
||||
*
|
||||
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
||||
*
|
||||
* See https://peps.python.org/pep-0249/#execute.
|
||||
*/
|
||||
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* A call to the `execute` method on a cursor or a connection.
|
||||
*
|
||||
* See https://peps.python.org/pep-0249/#execute
|
||||
*
|
||||
* Note: While `execute` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
||||
*/
|
||||
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
|
||||
ExecuteCall() { this.getFunction() = execute() }
|
||||
|
||||
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
|
||||
}
|
||||
|
||||
private DataFlow::TypeTrackingNode executemany(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("executemany") and
|
||||
result in [Cursor::instance(), Connection::instance()]
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = executemany(t2).track(t2, t))
|
||||
}
|
||||
|
||||
private DataFlow::Node executemany() { executemany(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* A call to the `executemany` method on a cursor or a connection.
|
||||
*
|
||||
* See https://peps.python.org/pep-0249/#executemany
|
||||
*
|
||||
* Note: While `executemany` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `executemany` on it.
|
||||
*/
|
||||
private class ExecutemanyCall extends SqlExecution::Range, DataFlow::CallCfgNode {
|
||||
ExecutemanyCall() { this.getFunction() = executemany() }
|
||||
|
||||
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1163,13 +1163,16 @@ private module StdlibPrivate {
|
||||
API::Node subprocess() { result = API::moduleImport("subprocess") }
|
||||
|
||||
/**
|
||||
* A call to `subprocess.Popen` or helper functions (call, check_call, check_output, run)
|
||||
* A call to `subprocess.Popen` or helper functions (call, check_call, check_output, run, getoutput, getstatusoutput)
|
||||
* See https://docs.python.org/3.8/library/subprocess.html#subprocess.Popen
|
||||
* ref: https://docs.python.org/3/library/subprocess.html#legacy-shell-invocation-functions
|
||||
*/
|
||||
private class SubprocessPopenCall extends SystemCommandExecution::Range, DataFlow::CallCfgNode {
|
||||
SubprocessPopenCall() {
|
||||
exists(string name |
|
||||
name in ["Popen", "call", "check_call", "check_output", "run"] and
|
||||
name in [
|
||||
"Popen", "call", "check_call", "check_output", "run", "getoutput", "getstatusoutput"
|
||||
] and
|
||||
this = subprocess().getMember(name).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -385,13 +385,10 @@ module Tornado {
|
||||
*
|
||||
* Needs this subclass to be considered a RegexString.
|
||||
*/
|
||||
private class TornadoRouteRegex extends RegexString {
|
||||
private class TornadoRouteRegex extends RegexString instanceof StrConst {
|
||||
TornadoRouteSetup setup;
|
||||
|
||||
TornadoRouteRegex() {
|
||||
this instanceof StrConst and
|
||||
setup.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(this)
|
||||
}
|
||||
TornadoRouteRegex() { setup.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(this) }
|
||||
|
||||
TornadoRouteSetup getRouteSetup() { result = setup }
|
||||
}
|
||||
|
||||
@@ -231,119 +231,4 @@ module Werkzeug {
|
||||
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.
|
||||
*
|
||||
* 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").getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* 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).getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,20 @@
|
||||
*
|
||||
* The CSV specification has the following columns:
|
||||
* - Sources:
|
||||
* `package; type; path; kind`
|
||||
* `type; path; kind`
|
||||
* - Sinks:
|
||||
* `package; type; path; kind`
|
||||
* `type; path; kind`
|
||||
* - Summaries:
|
||||
* `package; type; path; input; output; kind`
|
||||
* `type; path; input; output; kind`
|
||||
* - Types:
|
||||
* `package1; type1; package2; type2; path`
|
||||
* `type1; type2; path`
|
||||
*
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `package` column selects a package name, as it would be referenced in the source code,
|
||||
* such as an NPM package, PIP package, or Ruby gem. (See `ModelsAsData.qll` for language-specific details).
|
||||
* It may also be a synthetic package used for a type definition (see type definitions below).
|
||||
* 2. The `type` column selects all instances of a named type originating from that package,
|
||||
* or the empty string if referring to the package itself.
|
||||
* 1. The `type` column selects all instances of a named type. The syntax of this column is language-specific.
|
||||
* The language defines some type names that the analysis knows how to identify without models.
|
||||
* It can also be a synthetic type name defined by a type definition (see type definitions below).
|
||||
* 3. The `path` column is a `.`-separated list of "access path tokens" to resolve, starting at the node selected by `package` and `type`.
|
||||
* 2. The `path` column is a `.`-separated list of "access path tokens" to resolve, starting at the node selected by `type`.
|
||||
*
|
||||
* Every language supports the following tokens:
|
||||
* - Argument[n]: the n-th argument to a call. May be a range of form `x..y` (inclusive) and/or a comma-separated list.
|
||||
@@ -42,10 +39,10 @@
|
||||
*
|
||||
* For the time being, please consult `ApiGraphModelsSpecific.qll` to see which language-specific tokens are currently supported.
|
||||
*
|
||||
* 4. The `input` and `output` columns specify how data enters and leaves the element selected by the
|
||||
* first `(package, type, path)` tuple. Both strings are `.`-separated access paths
|
||||
* 3. The `input` and `output` columns specify how data enters and leaves the element selected by the
|
||||
* first `(type, path)` tuple. Both strings are `.`-separated access paths
|
||||
* of the same syntax as the `path` column.
|
||||
* 5. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* 4. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources `"remote"` indicates a default remote flow source, and for summaries
|
||||
* `"taint"` indicates a default additional taint step and `"value"` indicates a
|
||||
@@ -53,17 +50,17 @@
|
||||
*
|
||||
* ### Types
|
||||
*
|
||||
* A type row of form `package1; type1; package2; type2; path` indicates that `package2; type2; path`
|
||||
* should be seen as an instance of the type `package1; type1`.
|
||||
* A type row of form `type1; type2; path` indicates that `type2; path`
|
||||
* should be seen as an instance of the type `type1`.
|
||||
*
|
||||
* A `(package,type)` pair may refer to a static type or a synthetic type name used internally in the model.
|
||||
* A type may refer to a static type or a synthetic type name used internally in the model.
|
||||
* Synthetic type names can be used to reuse intermediate sub-paths, when there are multiple ways to access the same
|
||||
* element.
|
||||
* See `ModelsAsData.qll` for the language-specific interpretation of packages and static type names.
|
||||
* See `ModelsAsData.qll` for the language-specific interpretation of type names.
|
||||
*
|
||||
* By convention, if one wants to avoid clashes with static types from the package, the type name
|
||||
* should be prefixed with a tilde character (`~`). For example, `(foo, ~Bar)` can be used to indicate that
|
||||
* the type is related to the `foo` package but is not intended to match a static type.
|
||||
* By convention, if one wants to avoid clashes with static types, the type name
|
||||
* should be prefixed with a tilde character (`~`). For example, `~Bar` can be used to indicate that
|
||||
* the type is not intended to match a static type.
|
||||
*/
|
||||
|
||||
private import ApiGraphModelsSpecific as Specific
|
||||
@@ -75,6 +72,7 @@ private module API = Specific::API;
|
||||
private module DataFlow = Specific::DataFlow;
|
||||
|
||||
private import Specific::AccessPathSyntax
|
||||
private import ApiGraphModelsExtensions as Extensions
|
||||
|
||||
/** Module containing hooks for providing input data to be interpreted as a model. */
|
||||
module ModelInput {
|
||||
@@ -89,9 +87,9 @@ module ModelInput {
|
||||
*
|
||||
* A row of form
|
||||
* ```
|
||||
* package;type;path;kind
|
||||
* type;path;kind
|
||||
* ```
|
||||
* indicates that the value at `(package, type, path)` should be seen as a flow
|
||||
* indicates that the value at `(type, path)` should be seen as a flow
|
||||
* source of the given `kind`.
|
||||
*
|
||||
* The kind `remote` represents a general remote flow source.
|
||||
@@ -110,9 +108,9 @@ module ModelInput {
|
||||
*
|
||||
* A row of form
|
||||
* ```
|
||||
* package;type;path;kind
|
||||
* type;path;kind
|
||||
* ```
|
||||
* indicates that the value at `(package, type, path)` should be seen as a sink
|
||||
* indicates that the value at `(type, path)` should be seen as a sink
|
||||
* of the given `kind`.
|
||||
*/
|
||||
abstract predicate row(string row);
|
||||
@@ -129,9 +127,9 @@ module ModelInput {
|
||||
*
|
||||
* A row of form
|
||||
* ```
|
||||
* package;type;path;input;output;kind
|
||||
* type;path;input;output;kind
|
||||
* ```
|
||||
* indicates that for each call to `(package, type, path)`, the value referred to by `input`
|
||||
* indicates that for each call to `(type, path)`, the value referred to by `input`
|
||||
* can flow to the value referred to by `output`.
|
||||
*
|
||||
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
|
||||
@@ -151,9 +149,9 @@ module ModelInput {
|
||||
*
|
||||
* A row of form,
|
||||
* ```
|
||||
* package1;type1;package2;type2;path
|
||||
* type1;type2;path
|
||||
* ```
|
||||
* indicates that `(package2, type2, path)` should be seen as an instance of `(package1, type1)`.
|
||||
* indicates that `(type2, path)` should be seen as an instance of `type1`.
|
||||
*/
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
@@ -163,28 +161,28 @@ module ModelInput {
|
||||
*/
|
||||
class TypeModel extends Unit {
|
||||
/**
|
||||
* Gets a data-flow node that is a source of the type `package;type`.
|
||||
* Gets a data-flow node that is a source of the given `type`.
|
||||
*
|
||||
* This must not depend on API graphs, but ensures that an API node is generated for
|
||||
* the source.
|
||||
*/
|
||||
DataFlow::Node getASource(string package, string type) { none() }
|
||||
DataFlow::Node getASource(string type) { none() }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node that is a sink of the type `package;type`,
|
||||
* Gets a data-flow node that is a sink of the given `type`,
|
||||
* usually because it is an argument passed to a parameter of that type.
|
||||
*
|
||||
* This must not depend on API graphs, but ensures that an API node is generated for
|
||||
* the sink.
|
||||
*/
|
||||
DataFlow::Node getASink(string package, string type) { none() }
|
||||
DataFlow::Node getASink(string type) { none() }
|
||||
|
||||
/**
|
||||
* Gets an API node that is a source or sink of the type `package;type`.
|
||||
* Gets an API node that is a source or sink of the given `type`.
|
||||
*
|
||||
* Unlike `getASource` and `getASink`, this may depend on API graphs.
|
||||
*/
|
||||
API::Node getAnApiNode(string package, string type) { none() }
|
||||
API::Node getAnApiNode(string type) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +207,7 @@ private import ModelInput
|
||||
/**
|
||||
* An empty class, except in specific tests.
|
||||
*
|
||||
* If this is non-empty, all models are parsed even if the package is not
|
||||
* If this is non-empty, all models are parsed even if the type name is not
|
||||
* considered relevant for the current database.
|
||||
*/
|
||||
abstract class TestAllModels extends Unit { }
|
||||
@@ -232,54 +230,53 @@ private predicate typeModel(string row) { any(TypeModelCsv s).row(inversePad(row
|
||||
private predicate typeVariableModel(string row) { any(TypeVariableModelCsv s).row(inversePad(row)) }
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(string package, string type, string path, string kind) {
|
||||
predicate sourceModel(string type, string path, string kind) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
row.splitAt(";", 0) = package and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = path and
|
||||
row.splitAt(";", 3) = kind
|
||||
row.splitAt(";", 0) = type and
|
||||
row.splitAt(";", 1) = path and
|
||||
row.splitAt(";", 2) = kind
|
||||
)
|
||||
or
|
||||
Extensions::sourceModel(type, path, kind)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
private predicate sinkModel(string package, string type, string path, string kind) {
|
||||
private predicate sinkModel(string type, string path, string kind) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
row.splitAt(";", 0) = package and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = path and
|
||||
row.splitAt(";", 3) = kind
|
||||
row.splitAt(";", 0) = type and
|
||||
row.splitAt(";", 1) = path and
|
||||
row.splitAt(";", 2) = kind
|
||||
)
|
||||
or
|
||||
Extensions::sinkModel(type, path, kind)
|
||||
}
|
||||
|
||||
/** Holds if a summary model `row` exists for the given parameters. */
|
||||
private predicate summaryModel(
|
||||
string package, string type, string path, string input, string output, string kind
|
||||
) {
|
||||
private predicate summaryModel(string type, string path, string input, string output, string kind) {
|
||||
exists(string row |
|
||||
summaryModel(row) and
|
||||
row.splitAt(";", 0) = package and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = path and
|
||||
row.splitAt(";", 3) = input and
|
||||
row.splitAt(";", 4) = output and
|
||||
row.splitAt(";", 5) = kind
|
||||
row.splitAt(";", 0) = type and
|
||||
row.splitAt(";", 1) = path and
|
||||
row.splitAt(";", 2) = input and
|
||||
row.splitAt(";", 3) = output and
|
||||
row.splitAt(";", 4) = kind
|
||||
)
|
||||
or
|
||||
Extensions::summaryModel(type, path, input, output, kind)
|
||||
}
|
||||
|
||||
/** Holds if a type model exists for the given parameters. */
|
||||
private predicate typeModel(
|
||||
string package1, string type1, string package2, string type2, string path
|
||||
) {
|
||||
private predicate typeModel(string type1, string type2, string path) {
|
||||
exists(string row |
|
||||
typeModel(row) and
|
||||
row.splitAt(";", 0) = package1 and
|
||||
row.splitAt(";", 1) = type1 and
|
||||
row.splitAt(";", 2) = package2 and
|
||||
row.splitAt(";", 3) = type2 and
|
||||
row.splitAt(";", 4) = path
|
||||
row.splitAt(";", 0) = type1 and
|
||||
row.splitAt(";", 1) = type2 and
|
||||
row.splitAt(";", 2) = path
|
||||
)
|
||||
or
|
||||
Extensions::typeModel(type1, type2, path)
|
||||
}
|
||||
|
||||
/** Holds if a type variable model exists for the given parameters. */
|
||||
@@ -289,64 +286,55 @@ private predicate typeVariableModel(string name, string path) {
|
||||
row.splitAt(";", 0) = name and
|
||||
row.splitAt(";", 1) = path
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a package that should be seen as an alias for the given other `package`,
|
||||
* or the `package` itself.
|
||||
*/
|
||||
bindingset[package]
|
||||
bindingset[result]
|
||||
string getAPackageAlias(string package) {
|
||||
typeModel(package, "", result, "", "")
|
||||
or
|
||||
result = package
|
||||
Extensions::typeVariableModel(name, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if CSV rows involving `package` might be relevant for the analysis of this database.
|
||||
* Holds if CSV rows involving `type` might be relevant for the analysis of this database.
|
||||
*/
|
||||
private predicate isRelevantPackage(string package) {
|
||||
predicate isRelevantType(string type) {
|
||||
(
|
||||
sourceModel(package, _, _, _) or
|
||||
sinkModel(package, _, _, _) or
|
||||
summaryModel(package, _, _, _, _, _) or
|
||||
typeModel(_, _, package, _, _)
|
||||
sourceModel(type, _, _) or
|
||||
sinkModel(type, _, _) or
|
||||
summaryModel(type, _, _, _, _) or
|
||||
typeModel(_, type, _)
|
||||
) and
|
||||
(
|
||||
Specific::isPackageUsed(package)
|
||||
Specific::isTypeUsed(type)
|
||||
or
|
||||
exists(TestAllModels t)
|
||||
)
|
||||
or
|
||||
exists(string other |
|
||||
isRelevantPackage(other) and
|
||||
typeModel(package, _, other, _, _)
|
||||
exists(string other | isRelevantType(other) |
|
||||
typeModel(type, other, _)
|
||||
or
|
||||
Specific::hasImplicitTypeModel(type, other)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `package,type,path` is used in some CSV row.
|
||||
* Holds if `type,path` is used in some CSV row.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate isRelevantFullPath(string package, string type, string path) {
|
||||
isRelevantPackage(package) and
|
||||
predicate isRelevantFullPath(string type, string path) {
|
||||
isRelevantType(type) and
|
||||
(
|
||||
sourceModel(package, type, path, _) or
|
||||
sinkModel(package, type, path, _) or
|
||||
summaryModel(package, type, path, _, _, _) or
|
||||
typeModel(_, _, package, type, path)
|
||||
sourceModel(type, path, _) or
|
||||
sinkModel(type, path, _) or
|
||||
summaryModel(type, path, _, _, _) or
|
||||
typeModel(_, type, path)
|
||||
)
|
||||
}
|
||||
|
||||
/** A string from a CSV row that should be parsed as an access path. */
|
||||
private class AccessPathRange extends AccessPath::Range {
|
||||
AccessPathRange() {
|
||||
isRelevantFullPath(_, _, this)
|
||||
isRelevantFullPath(_, this)
|
||||
or
|
||||
exists(string package | isRelevantPackage(package) |
|
||||
summaryModel(package, _, _, this, _, _) or
|
||||
summaryModel(package, _, _, _, this, _)
|
||||
exists(string type | isRelevantType(type) |
|
||||
summaryModel(type, _, this, _, _) or
|
||||
summaryModel(type, _, _, this, _)
|
||||
)
|
||||
or
|
||||
typeVariableModel(_, this)
|
||||
@@ -400,83 +388,73 @@ private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, A
|
||||
}
|
||||
|
||||
private class TypeModelUseEntry extends API::EntryPoint {
|
||||
private string package;
|
||||
private string type;
|
||||
|
||||
TypeModelUseEntry() {
|
||||
exists(any(TypeModel tm).getASource(package, type)) and
|
||||
this = "TypeModelUseEntry;" + package + ";" + type
|
||||
exists(any(TypeModel tm).getASource(type)) and
|
||||
this = "TypeModelUseEntry;" + type
|
||||
}
|
||||
|
||||
override DataFlow::LocalSourceNode getASource() {
|
||||
result = any(TypeModel tm).getASource(package, type)
|
||||
}
|
||||
override DataFlow::LocalSourceNode getASource() { result = any(TypeModel tm).getASource(type) }
|
||||
|
||||
API::Node getNodeForType(string package_, string type_) {
|
||||
package = package_ and type = type_ and result = this.getANode()
|
||||
}
|
||||
API::Node getNodeForType(string type_) { type = type_ and result = this.getANode() }
|
||||
}
|
||||
|
||||
private class TypeModelDefEntry extends API::EntryPoint {
|
||||
private string package;
|
||||
private string type;
|
||||
|
||||
TypeModelDefEntry() {
|
||||
exists(any(TypeModel tm).getASink(package, type)) and
|
||||
this = "TypeModelDefEntry;" + package + ";" + type
|
||||
exists(any(TypeModel tm).getASink(type)) and
|
||||
this = "TypeModelDefEntry;" + type
|
||||
}
|
||||
|
||||
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(package, type) }
|
||||
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(type) }
|
||||
|
||||
API::Node getNodeForType(string package_, string type_) {
|
||||
package = package_ and type = type_ and result = this.getANode()
|
||||
}
|
||||
API::Node getNodeForType(string type_) { type = type_ and result = this.getANode() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an API node identified by the given `(package,type)` pair.
|
||||
* Gets an API node identified by the given `type`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private API::Node getNodeFromType(string package, string type) {
|
||||
exists(string package2, string type2, AccessPath path2 |
|
||||
typeModel(package, type, package2, type2, path2) and
|
||||
result = getNodeFromPath(package2, type2, path2)
|
||||
private API::Node getNodeFromType(string type) {
|
||||
exists(string type2, AccessPath path2 |
|
||||
typeModel(type, type2, path2) and
|
||||
result = getNodeFromPath(type2, path2)
|
||||
)
|
||||
or
|
||||
result = any(TypeModelUseEntry e).getNodeForType(package, type)
|
||||
result = any(TypeModelUseEntry e).getNodeForType(type)
|
||||
or
|
||||
result = any(TypeModelDefEntry e).getNodeForType(package, type)
|
||||
result = any(TypeModelDefEntry e).getNodeForType(type)
|
||||
or
|
||||
result = any(TypeModel t).getAnApiNode(package, type)
|
||||
result = any(TypeModel t).getAnApiNode(type)
|
||||
or
|
||||
result = Specific::getExtraNodeFromType(package, type)
|
||||
result = Specific::getExtraNodeFromType(type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API node identified by the first `n` tokens of `path` in the given `(package, type, path)` tuple.
|
||||
* Gets the API node identified by the first `n` tokens of `path` in the given `(type, path)` tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private API::Node getNodeFromPath(string package, string type, AccessPath path, int n) {
|
||||
isRelevantFullPath(package, type, path) and
|
||||
private API::Node getNodeFromPath(string type, AccessPath path, int n) {
|
||||
isRelevantFullPath(type, path) and
|
||||
(
|
||||
n = 0 and
|
||||
result = getNodeFromType(package, type)
|
||||
result = getNodeFromType(type)
|
||||
or
|
||||
result = Specific::getExtraNodeFromPath(package, type, path, n)
|
||||
result = Specific::getExtraNodeFromPath(type, path, n)
|
||||
)
|
||||
or
|
||||
result = getSuccessorFromNode(getNodeFromPath(package, type, path, n - 1), path.getToken(n - 1))
|
||||
result = getSuccessorFromNode(getNodeFromPath(type, path, n - 1), path.getToken(n - 1))
|
||||
or
|
||||
// Similar to the other recursive case, but where the path may have stepped through one or more call-site filters
|
||||
result =
|
||||
getSuccessorFromInvoke(getInvocationFromPath(package, type, path, n - 1), path.getToken(n - 1))
|
||||
result = getSuccessorFromInvoke(getInvocationFromPath(type, path, n - 1), path.getToken(n - 1))
|
||||
or
|
||||
// Apply a subpath
|
||||
result =
|
||||
getNodeFromSubPath(getNodeFromPath(package, type, path, n - 1), getSubPathAt(path, n - 1))
|
||||
result = getNodeFromSubPath(getNodeFromPath(type, path, n - 1), getSubPathAt(path, n - 1))
|
||||
or
|
||||
// Apply a type step
|
||||
typeStep(getNodeFromPath(package, type, path, n), result)
|
||||
typeStep(getNodeFromPath(type, path, n), result)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -496,15 +474,15 @@ private AccessPath getSubPathAt(AccessPath path, int n) {
|
||||
pragma[nomagic]
|
||||
private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n) {
|
||||
exists(AccessPath path, int k |
|
||||
base = [getNodeFromPath(_, _, path, k), getNodeFromSubPath(_, path, k)] and
|
||||
base = [getNodeFromPath(_, path, k), getNodeFromSubPath(_, path, k)] and
|
||||
subPath = getSubPathAt(path, k) and
|
||||
result = base and
|
||||
n = 0
|
||||
)
|
||||
or
|
||||
exists(string package, string type, AccessPath basePath |
|
||||
typeStepModel(package, type, basePath, subPath) and
|
||||
base = getNodeFromPath(package, type, basePath) and
|
||||
exists(string type, AccessPath basePath |
|
||||
typeStepModel(type, basePath, subPath) and
|
||||
base = getNodeFromPath(type, basePath) and
|
||||
result = base and
|
||||
n = 0
|
||||
)
|
||||
@@ -543,42 +521,40 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath) {
|
||||
result = getNodeFromSubPath(base, subPath, subPath.getNumToken())
|
||||
}
|
||||
|
||||
/** Gets the node identified by the given `(package, type, path)` tuple. */
|
||||
private API::Node getNodeFromPath(string package, string type, AccessPath path) {
|
||||
result = getNodeFromPath(package, type, path, path.getNumToken())
|
||||
/** Gets the node identified by the given `(type, path)` tuple. */
|
||||
private API::Node getNodeFromPath(string type, AccessPath path) {
|
||||
result = getNodeFromPath(type, path, path.getNumToken())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typeStepModel(string package, string type, AccessPath basePath, AccessPath output) {
|
||||
summaryModel(package, type, basePath, "", output, "type")
|
||||
private predicate typeStepModel(string type, AccessPath basePath, AccessPath output) {
|
||||
summaryModel(type, basePath, "", output, "type")
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typeStep(API::Node pred, API::Node succ) {
|
||||
exists(string package, string type, AccessPath basePath, AccessPath output |
|
||||
typeStepModel(package, type, basePath, output) and
|
||||
pred = getNodeFromPath(package, type, basePath) and
|
||||
exists(string type, AccessPath basePath, AccessPath output |
|
||||
typeStepModel(type, basePath, output) and
|
||||
pred = getNodeFromPath(type, basePath) and
|
||||
succ = getNodeFromSubPath(pred, output)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an invocation identified by the given `(package, type, path)` tuple.
|
||||
* Gets an invocation identified by the given `(type, path)` tuple.
|
||||
*
|
||||
* Unlike `getNodeFromPath`, the `path` may end with one or more call-site filters.
|
||||
*/
|
||||
private Specific::InvokeNode getInvocationFromPath(
|
||||
string package, string type, AccessPath path, int n
|
||||
) {
|
||||
result = Specific::getAnInvocationOf(getNodeFromPath(package, type, path, n))
|
||||
private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path, int n) {
|
||||
result = Specific::getAnInvocationOf(getNodeFromPath(type, path, n))
|
||||
or
|
||||
result = getInvocationFromPath(package, type, path, n - 1) and
|
||||
result = getInvocationFromPath(type, path, n - 1) and
|
||||
invocationMatchesCallSiteFilter(result, path.getToken(n - 1))
|
||||
}
|
||||
|
||||
/** Gets an invocation identified by the given `(package, type, path)` tuple. */
|
||||
private Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) {
|
||||
result = getInvocationFromPath(package, type, path, path.getNumToken())
|
||||
/** Gets an invocation identified by the given `(type, path)` tuple. */
|
||||
private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path) {
|
||||
result = getInvocationFromPath(type, path, path.getNumToken())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -631,9 +607,9 @@ module ModelOutput {
|
||||
*/
|
||||
cached
|
||||
API::Node getASourceNode(string kind) {
|
||||
exists(string package, string type, string path |
|
||||
sourceModel(package, type, path, kind) and
|
||||
result = getNodeFromPath(package, type, path)
|
||||
exists(string type, string path |
|
||||
sourceModel(type, path, kind) and
|
||||
result = getNodeFromPath(type, path)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -642,9 +618,9 @@ module ModelOutput {
|
||||
*/
|
||||
cached
|
||||
API::Node getASinkNode(string kind) {
|
||||
exists(string package, string type, string path |
|
||||
sinkModel(package, type, path, kind) and
|
||||
result = getNodeFromPath(package, type, path)
|
||||
exists(string type, string path |
|
||||
sinkModel(type, path, kind) and
|
||||
result = getNodeFromPath(type, path)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -653,32 +629,31 @@ module ModelOutput {
|
||||
*/
|
||||
cached
|
||||
predicate relevantSummaryModel(
|
||||
string package, string type, string path, string input, string output, string kind
|
||||
string type, string path, string input, string output, string kind
|
||||
) {
|
||||
isRelevantPackage(package) and
|
||||
summaryModel(package, type, path, input, output, kind)
|
||||
isRelevantType(type) and
|
||||
summaryModel(type, path, input, output, kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `baseNode` is an invocation identified by the `package,type,path` part of a summary row.
|
||||
* Holds if a `baseNode` is an invocation identified by the `type,path` part of a summary row.
|
||||
*/
|
||||
cached
|
||||
predicate resolvedSummaryBase(
|
||||
string package, string type, string path, Specific::InvokeNode baseNode
|
||||
) {
|
||||
summaryModel(package, type, path, _, _, _) and
|
||||
baseNode = getInvocationFromPath(package, type, path)
|
||||
predicate resolvedSummaryBase(string type, string path, Specific::InvokeNode baseNode) {
|
||||
summaryModel(type, path, _, _, _) and
|
||||
baseNode = getInvocationFromPath(type, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
|
||||
* Holds if `node` is seen as an instance of `type` due to a type definition
|
||||
* contributed by a CSV model.
|
||||
*/
|
||||
cached
|
||||
API::Node getATypeNode(string package, string type) { result = getNodeFromType(package, type) }
|
||||
API::Node getATypeNode(string type) { result = getNodeFromType(type) }
|
||||
}
|
||||
|
||||
import Cached
|
||||
import Specific::ModelOutputSpecific
|
||||
|
||||
/**
|
||||
* Gets an error message relating to an invalid CSV row in a model.
|
||||
@@ -686,13 +661,13 @@ module ModelOutput {
|
||||
string getAWarning() {
|
||||
// Check number of columns
|
||||
exists(string row, string kind, int expectedArity, int actualArity |
|
||||
any(SourceModelCsv csv).row(row) and kind = "source" and expectedArity = 4
|
||||
any(SourceModelCsv csv).row(row) and kind = "source" and expectedArity = 3
|
||||
or
|
||||
any(SinkModelCsv csv).row(row) and kind = "sink" and expectedArity = 4
|
||||
any(SinkModelCsv csv).row(row) and kind = "sink" and expectedArity = 3
|
||||
or
|
||||
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 6
|
||||
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 5
|
||||
or
|
||||
any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 5
|
||||
any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 3
|
||||
or
|
||||
any(TypeVariableModelCsv csv).row(row) and kind = "type-variable" and expectedArity = 2
|
||||
|
|
||||
@@ -705,7 +680,7 @@ module ModelOutput {
|
||||
or
|
||||
// Check names and arguments of access path tokens
|
||||
exists(AccessPath path, AccessPathToken token |
|
||||
(isRelevantFullPath(_, _, path) or typeVariableModel(_, path)) and
|
||||
(isRelevantFullPath(_, path) or typeVariableModel(_, path)) and
|
||||
token = path.getToken(_)
|
||||
|
|
||||
not isValidTokenNameInIdentifyingAccessPath(token.getName()) and
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Defines extensible predicates for contributing library models from data extensions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds if the value at `(type, path)` should be seen as a flow
|
||||
* source of the given `kind`.
|
||||
*
|
||||
* The kind `remote` represents a general remote flow source.
|
||||
*/
|
||||
extensible predicate sourceModel(string type, string path, string kind);
|
||||
|
||||
/**
|
||||
* Holds if the value at `(type, path)` should be seen as a sink
|
||||
* of the given `kind`.
|
||||
*/
|
||||
extensible predicate sinkModel(string type, string path, string kind);
|
||||
|
||||
/**
|
||||
* Holds if calls to `(type, path)`, the value referred to by `input`
|
||||
* can flow to the value referred to by `output`.
|
||||
*
|
||||
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
|
||||
* respectively.
|
||||
*/
|
||||
extensible predicate summaryModel(string type, string path, string input, string output, string kind);
|
||||
|
||||
/**
|
||||
* Holds if `(type2, path)` should be seen as an instance of `type1`.
|
||||
*/
|
||||
extensible predicate typeModel(string type1, string type2, string path);
|
||||
|
||||
/**
|
||||
* Holds if `path` can be substituted for a token `TypeVar[name]`.
|
||||
*/
|
||||
extensible predicate typeVariableModel(string name, string path);
|
||||
@@ -31,19 +31,22 @@ import semmle.python.dataflow.new.DataFlow::DataFlow as DataFlow
|
||||
private import AccessPathSyntax
|
||||
|
||||
/**
|
||||
* Holds if models describing `package` may be relevant for the analysis of this database.
|
||||
* Holds if models describing `type` may be relevant for the analysis of this database.
|
||||
*/
|
||||
predicate isPackageUsed(string package) { API::moduleImportExists(package) }
|
||||
predicate isTypeUsed(string type) { API::moduleImportExists(type) }
|
||||
|
||||
/** Gets a Python-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
|
||||
bindingset[package, type, path]
|
||||
API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) { none() }
|
||||
/**
|
||||
* Holds if `type` can be obtained from an instance of `otherType` due to
|
||||
* language semantics modeled by `getExtraNodeFromType`.
|
||||
*/
|
||||
predicate hasImplicitTypeModel(string type, string otherType) { none() }
|
||||
|
||||
/** Gets a Python-specific interpretation of the `(package, type)` tuple. */
|
||||
API::Node getExtraNodeFromType(string package, string type) {
|
||||
type = "" and
|
||||
result = API::moduleImport(package)
|
||||
}
|
||||
/** Gets a Python-specific interpretation of the `(type, path)` tuple after resolving the first `n` access path tokens. */
|
||||
bindingset[type, path]
|
||||
API::Node getExtraNodeFromPath(string type, AccessPath path, int n) { none() }
|
||||
|
||||
/** Gets a Python-specific interpretation of the given `type`. */
|
||||
API::Node getExtraNodeFromType(string type) { result = API::moduleImport(type) }
|
||||
|
||||
/**
|
||||
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
|
||||
@@ -121,9 +124,9 @@ predicate invocationMatchesExtraCallSiteFilter(API::CallNode invoke, AccessPathT
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate relevantInputOutputPath(API::CallNode base, AccessPath inputOrOutput) {
|
||||
exists(string package, string type, string input, string output, string path |
|
||||
ModelOutput::relevantSummaryModel(package, type, path, input, output, _) and
|
||||
ModelOutput::resolvedSummaryBase(package, type, path, base) and
|
||||
exists(string type, string input, string output, string path |
|
||||
ModelOutput::relevantSummaryModel(type, path, input, output, _) and
|
||||
ModelOutput::resolvedSummaryBase(type, path, base) and
|
||||
inputOrOutput = [input, output]
|
||||
)
|
||||
}
|
||||
@@ -153,12 +156,9 @@ private API::Node getNodeFromInputOutputPath(API::CallNode baseNode, AccessPath
|
||||
* Holds if a CSV summary contributed the step `pred -> succ` of the given `kind`.
|
||||
*/
|
||||
predicate summaryStep(API::Node pred, API::Node succ, string kind) {
|
||||
exists(
|
||||
string package, string type, string path, API::CallNode base, AccessPath input,
|
||||
AccessPath output
|
||||
|
|
||||
ModelOutput::relevantSummaryModel(package, type, path, input, output, kind) and
|
||||
ModelOutput::resolvedSummaryBase(package, type, path, base) and
|
||||
exists(string type, string path, API::CallNode base, AccessPath input, AccessPath output |
|
||||
ModelOutput::relevantSummaryModel(type, path, input, output, kind) and
|
||||
ModelOutput::resolvedSummaryBase(type, path, base) and
|
||||
pred = getNodeFromInputOutputPath(base, input) and
|
||||
succ = getNodeFromInputOutputPath(base, output)
|
||||
)
|
||||
@@ -201,3 +201,5 @@ predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string a
|
||||
argument.regexpMatch("\\w+:") // keyword argument
|
||||
)
|
||||
}
|
||||
|
||||
module ModelOutputSpecific { }
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
extensions:
|
||||
# Contribute empty data sets to avoid errors about an undefined extensionals
|
||||
- addsTo:
|
||||
pack: codeql/python-all
|
||||
extensible: sourceModel
|
||||
data: []
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/python-all
|
||||
extensible: sinkModel
|
||||
data: []
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/python-all
|
||||
extensible: summaryModel
|
||||
data: []
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/python-all
|
||||
extensible: typeModel
|
||||
data: []
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/python-all
|
||||
extensible: typeVariableModel
|
||||
data: []
|
||||
@@ -1,204 +0,0 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides internal implementation of PEP249. This currently resides in a different
|
||||
* file than `python/ql/src/semmle/python/frameworks/PEP249.qll`, since we used to
|
||||
* export everything without being encapsulated in a module, and shadowing rules means
|
||||
* that we can't just add the module directly to that file :(
|
||||
*
|
||||
* So once we can remove those deprecated things (Start of July 2022), we can also move
|
||||
* the core implementation into its' proper place.
|
||||
*
|
||||
* Provides classes modeling PEP 249.
|
||||
* See https://www.python.org/dev/peps/pep-0249/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides classes modeling database interfaces following PEP 249.
|
||||
* See https://www.python.org/dev/peps/pep-0249/.
|
||||
*/
|
||||
module PEP249 {
|
||||
/**
|
||||
* An API graph node representing a module that implements PEP 249.
|
||||
*/
|
||||
abstract class PEP249ModuleApiNode extends API::Node {
|
||||
/** Gets a string representation of this element. */
|
||||
override string toString() { result = this.(API::Node).toString() }
|
||||
}
|
||||
|
||||
/** Gets a reference to the `connect` function of a module that implements PEP 249. */
|
||||
DataFlow::Node connect() {
|
||||
result = any(PEP249ModuleApiNode a).getMember("connect").getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for database connections (following PEP 249).
|
||||
*
|
||||
* See https://www.python.org/dev/peps/pep-0249/#connection-objects.
|
||||
*/
|
||||
module Connection {
|
||||
/**
|
||||
* A source of database connections (following PEP 249), 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 external
|
||||
* libraries.
|
||||
*
|
||||
* Use the predicate `Connection::instance()` to get references to database connections (following PEP 249).
|
||||
*
|
||||
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
|
||||
* a connection than going through `connect`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::Node { }
|
||||
|
||||
/** A call to the `connect` function of a module that implements PEP 249. */
|
||||
private class ConnectCall extends InstanceSource, DataFlow::CallCfgNode {
|
||||
ConnectCall() { this.getFunction() = connect() }
|
||||
}
|
||||
|
||||
/** Gets a reference to a database connection (following PEP 249). */
|
||||
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 database connection (following PEP 249). */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for database cursors (following PEP 249).
|
||||
*
|
||||
* These are returned by the `cursor` method on a database connection.
|
||||
* See https://www.python.org/dev/peps/pep-0249/#cursor.
|
||||
*/
|
||||
module Cursor {
|
||||
/**
|
||||
* A source of database cursors (following PEP 249), 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 external
|
||||
* libraries.
|
||||
*
|
||||
* Use the predicate `Cursor::instance()` to get references to database cursors (following PEP 249).
|
||||
*
|
||||
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
|
||||
* a connection than going through `connect`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to a database cursor. */
|
||||
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 database cursor. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** Gets a reference to the `cursor` method on a database connection. */
|
||||
private DataFlow::TypeTrackingNode methodRef(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("cursor") and
|
||||
result = Connection::instance()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `cursor` method on a database connection. */
|
||||
DataFlow::Node methodRef() { methodRef(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** A call to the `cursor` method on a database connection */
|
||||
private class CursorCall extends InstanceSource, DataFlow::CallCfgNode {
|
||||
CursorCall() { this.getFunction() = methodRef() }
|
||||
}
|
||||
|
||||
/** Gets a reference to a result of calling the `cursor` method on a database connection. */
|
||||
private DataFlow::TypeTrackingNode methodResult(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asCfgNode().(CallNode).getFunction() = methodRef().asCfgNode()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = methodResult(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Cursor::instance()` to get references to database cursors instead.
|
||||
*
|
||||
* Gets a reference to a result of calling the `cursor` method on a database connection.
|
||||
*/
|
||||
deprecated DataFlow::Node methodResult() {
|
||||
methodResult(DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `execute` method on a cursor (or on a connection).
|
||||
*
|
||||
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
||||
*
|
||||
* See https://peps.python.org/pep-0249/#execute.
|
||||
*/
|
||||
private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("execute") and
|
||||
result in [Cursor::instance(), Connection::instance()]
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `execute` method on a cursor (or on a connection).
|
||||
*
|
||||
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
||||
*
|
||||
* See https://peps.python.org/pep-0249/#execute.
|
||||
*/
|
||||
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* A call to the `execute` method on a cursor or a connection.
|
||||
*
|
||||
* See https://peps.python.org/pep-0249/#execute
|
||||
*
|
||||
* Note: While `execute` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
||||
*/
|
||||
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
|
||||
ExecuteCall() { this.getFunction() = execute() }
|
||||
|
||||
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
|
||||
}
|
||||
|
||||
private DataFlow::TypeTrackingNode executemany(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("executemany") and
|
||||
result in [Cursor::instance(), Connection::instance()]
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = executemany(t2).track(t2, t))
|
||||
}
|
||||
|
||||
private DataFlow::Node executemany() { executemany(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* A call to the `executemany` method on a cursor or a connection.
|
||||
*
|
||||
* See https://peps.python.org/pep-0249/#executemany
|
||||
*
|
||||
* Note: While `executemany` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `executemany` on it.
|
||||
*/
|
||||
private class ExecutemanyCall extends SqlExecution::Range, DataFlow::CallCfgNode {
|
||||
ExecutemanyCall() { this.getFunction() = executemany() }
|
||||
|
||||
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
|
||||
}
|
||||
}
|
||||
@@ -2,155 +2,7 @@
|
||||
* Provides predicates for reasoning about bad tag filter vulnerabilities.
|
||||
*/
|
||||
|
||||
import regexp.RegexpMatching
|
||||
|
||||
/**
|
||||
* Holds if the regexp `root` should be tested against `str`.
|
||||
* Implements the `isRegexpMatchingCandidateSig` signature from `RegexpMatching`.
|
||||
* `ignorePrefix` toggles whether the regular expression should be treated as accepting any prefix if it's unanchored.
|
||||
* `testWithGroups` toggles whether it's tested which groups are filled by a given input string.
|
||||
*/
|
||||
private predicate isBadTagFilterCandidate(
|
||||
RootTerm root, string str, boolean ignorePrefix, boolean testWithGroups
|
||||
) {
|
||||
// the regexp must mention "<" and ">" explicitly.
|
||||
forall(string angleBracket | angleBracket = ["<", ">"] |
|
||||
any(RegExpConstant term | term.getValue().matches("%" + angleBracket + "%")).getRootTerm() =
|
||||
root
|
||||
) and
|
||||
ignorePrefix = true and
|
||||
(
|
||||
str = ["<!-- foo -->", "<!-- foo --!>", "<!- foo ->", "<foo>", "<script>"] and
|
||||
testWithGroups = true
|
||||
or
|
||||
str =
|
||||
[
|
||||
"<!-- foo -->", "<!- foo ->", "<!-- foo --!>", "<!-- foo\n -->", "<script>foo</script>",
|
||||
"<script \n>foo</script>", "<script >foo\n</script>", "<foo ></foo>", "<foo>",
|
||||
"<foo src=\"foo\"></foo>", "<script>", "<script src=\"foo\"></script>",
|
||||
"<script src='foo'></script>", "<SCRIPT>foo</SCRIPT>", "<script\tsrc=\"foo\"/>",
|
||||
"<script\tsrc='foo'></script>", "<sCrIpT>foo</ScRiPt>", "<script src=\"foo\">foo</script >",
|
||||
"<script src=\"foo\">foo</script foo=\"bar\">", "<script src=\"foo\">foo</script\t\n bar>"
|
||||
] and
|
||||
testWithGroups = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A regexp that matches some string from the `isBadTagFilterCandidate` predicate.
|
||||
*/
|
||||
class HtmlMatchingRegExp extends RootTerm {
|
||||
HtmlMatchingRegExp() { RegexpMatching<isBadTagFilterCandidate/4>::matches(this, _) }
|
||||
|
||||
/** Holds if this regexp matched `str`, where `str` is one of the string from `isBadTagFilterCandidate`. */
|
||||
predicate matches(string str) { RegexpMatching<isBadTagFilterCandidate/4>::matches(this, str) }
|
||||
|
||||
/** Holds if this regexp fills capture group `g' when matching `str', where `str` is one of the string from `isBadTagFilterCandidate`. */
|
||||
predicate fillsCaptureGroup(string str, int g) {
|
||||
RegexpMatching<isBadTagFilterCandidate/4>::fillsCaptureGroup(this, str, g)
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HtmlMatchingRegExp */
|
||||
deprecated class HTMLMatchingRegExp = HtmlMatchingRegExp;
|
||||
|
||||
/**
|
||||
* Holds if `regexp` matches some HTML tags, but misses some HTML tags that it should match.
|
||||
*
|
||||
* When adding a new case to this predicate, make sure the test string used in `matches(..)` calls are present in `HTMLMatchingRegExp::test` / `HTMLMatchingRegExp::testWithGroups`.
|
||||
*/
|
||||
predicate isBadRegexpFilter(HtmlMatchingRegExp regexp, string msg) {
|
||||
// CVE-2021-33829 - matching both "<!-- foo -->" and "<!-- foo --!>", but in different capture groups
|
||||
regexp.matches("<!-- foo -->") and
|
||||
regexp.matches("<!-- foo --!>") and
|
||||
exists(int a, int b | a != b |
|
||||
regexp.fillsCaptureGroup("<!-- foo -->", a) and
|
||||
// <!-- foo --> might be ambiguously parsed (matching both capture groups), and that is ok here.
|
||||
regexp.fillsCaptureGroup("<!-- foo --!>", b) and
|
||||
not regexp.fillsCaptureGroup("<!-- foo --!>", a) and
|
||||
msg =
|
||||
"Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group "
|
||||
+ a + " and comments ending with --!> are matched with capture group " +
|
||||
strictconcat(int i | regexp.fillsCaptureGroup("<!-- foo --!>", i) | i.toString(), ", ") +
|
||||
"."
|
||||
)
|
||||
or
|
||||
// CVE-2020-17480 - matching "<!-- foo -->" and other tags, but not "<!-- foo --!>".
|
||||
exists(int group, int other |
|
||||
group != other and
|
||||
regexp.fillsCaptureGroup("<!-- foo -->", group) and
|
||||
regexp.fillsCaptureGroup("<foo>", other) and
|
||||
not regexp.matches("<!-- foo --!>") and
|
||||
not regexp.fillsCaptureGroup("<!-- foo -->", any(int i | i != group)) and
|
||||
not regexp.fillsCaptureGroup("<!- foo ->", group) and
|
||||
not regexp.fillsCaptureGroup("<foo>", group) and
|
||||
not regexp.fillsCaptureGroup("<script>", group) and
|
||||
msg =
|
||||
"This regular expression only parses --> (capture group " + group +
|
||||
") and not --!> as an HTML comment end tag."
|
||||
)
|
||||
or
|
||||
regexp.matches("<!-- foo -->") and
|
||||
not regexp.matches("<!-- foo\n -->") and
|
||||
not regexp.matches("<!- foo ->") and
|
||||
not regexp.matches("<foo>") and
|
||||
not regexp.matches("<script>") and
|
||||
msg = "This regular expression does not match comments containing newlines."
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
regexp.matches("<script src=\"foo\"></script>") and
|
||||
not regexp.matches("<foo ></foo>") and
|
||||
(
|
||||
not regexp.matches("<script \n>foo</script>") and
|
||||
msg = "This regular expression matches <script></script>, but not <script \\n></script>"
|
||||
or
|
||||
not regexp.matches("<script >foo\n</script>") and
|
||||
msg = "This regular expression matches <script>...</script>, but not <script >...\\n</script>"
|
||||
)
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
regexp.matches("<script src=\"foo\"></script>") and
|
||||
not regexp.matches("<script src='foo'></script>") and
|
||||
not regexp.matches("<foo>") and
|
||||
msg = "This regular expression does not match script tags where the attribute uses single-quotes."
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
regexp.matches("<script src='foo'></script>") and
|
||||
not regexp.matches("<script src=\"foo\"></script>") and
|
||||
not regexp.matches("<foo>") and
|
||||
msg = "This regular expression does not match script tags where the attribute uses double-quotes."
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
regexp.matches("<script src='foo'></script>") and
|
||||
not regexp.matches("<script\tsrc='foo'></script>") and
|
||||
not regexp.matches("<foo>") and
|
||||
not regexp.matches("<foo src=\"foo\"></foo>") and
|
||||
msg = "This regular expression does not match script tags where tabs are used between attributes."
|
||||
or
|
||||
regexp.matches("<script>foo</script>") and
|
||||
not RegExpFlags::isIgnoreCase(regexp) and
|
||||
not regexp.matches("<foo>") and
|
||||
not regexp.matches("<foo ></foo>") and
|
||||
(
|
||||
not regexp.matches("<SCRIPT>foo</SCRIPT>") and
|
||||
msg = "This regular expression does not match upper case <SCRIPT> tags."
|
||||
or
|
||||
not regexp.matches("<sCrIpT>foo</ScRiPt>") and
|
||||
regexp.matches("<SCRIPT>foo</SCRIPT>") and
|
||||
msg = "This regular expression does not match mixed case <sCrIpT> tags."
|
||||
)
|
||||
or
|
||||
regexp.matches("<script src=\"foo\"></script>") and
|
||||
not regexp.matches("<foo>") and
|
||||
not regexp.matches("<foo ></foo>") and
|
||||
(
|
||||
not regexp.matches("<script src=\"foo\">foo</script >") and
|
||||
msg = "This regular expression does not match script end tags like </script >."
|
||||
or
|
||||
not regexp.matches("<script src=\"foo\">foo</script foo=\"bar\">") and
|
||||
msg = "This regular expression does not match script end tags like </script foo=\"bar\">."
|
||||
or
|
||||
not regexp.matches("<script src=\"foo\">foo</script\t\n bar>") and
|
||||
msg = "This regular expression does not match script end tags like </script\\t\\n bar>."
|
||||
)
|
||||
}
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
// BadTagFilterQuery should be used directly from the shared pack, and not from this file.
|
||||
deprecated import codeql.regex.nfa.BadTagFilterQuery::Make<TreeView> as Dep
|
||||
import Dep
|
||||
|
||||
@@ -2,288 +2,7 @@
|
||||
* Classes and predicates for working with suspicious character ranges.
|
||||
*/
|
||||
|
||||
// We don't need the NFA utils, just the regexp tree.
|
||||
// but the below is a nice shared library that exposes the API we need.
|
||||
import regexp.NfaUtils
|
||||
|
||||
/**
|
||||
* Gets a rank for `range` that is unique for ranges in the same file.
|
||||
* Prioritizes ranges that match more characters.
|
||||
*/
|
||||
int rankRange(RegExpCharacterRange range) {
|
||||
range =
|
||||
rank[result](RegExpCharacterRange r, Location l, int low, int high |
|
||||
r.getLocation() = l and
|
||||
isRange(r, low, high)
|
||||
|
|
||||
r order by (high - low) desc, l.getStartLine(), l.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `range` spans from the unicode code points `low` to `high` (both inclusive). */
|
||||
predicate isRange(RegExpCharacterRange range, int low, int high) {
|
||||
exists(string lowc, string highc |
|
||||
range.isRange(lowc, highc) and
|
||||
low.toUnicode() = lowc and
|
||||
high.toUnicode() = highc
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `char` is an alpha-numeric character. */
|
||||
predicate isAlphanumeric(string char) {
|
||||
// written like this to avoid having a bindingset for the predicate
|
||||
char = [[48 .. 57], [65 .. 90], [97 .. 122]].toUnicode() // 0-9, A-Z, a-z
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given ranges are from the same character class
|
||||
* and there exists at least one character matched by both ranges.
|
||||
*/
|
||||
predicate overlap(RegExpCharacterRange a, RegExpCharacterRange b) {
|
||||
exists(RegExpCharacterClass clz |
|
||||
a = clz.getAChild() and
|
||||
b = clz.getAChild() and
|
||||
a != b
|
||||
|
|
||||
exists(int alow, int ahigh, int blow, int bhigh |
|
||||
isRange(a, alow, ahigh) and
|
||||
isRange(b, blow, bhigh) and
|
||||
alow <= bhigh and
|
||||
blow <= ahigh
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `range` overlaps with the char class `escape` from the same character class.
|
||||
*/
|
||||
predicate overlapsWithCharEscape(RegExpCharacterRange range, RegExpCharacterClassEscape escape) {
|
||||
exists(RegExpCharacterClass clz, string low, string high |
|
||||
range = clz.getAChild() and
|
||||
escape = clz.getAChild() and
|
||||
range.isRange(low, high)
|
||||
|
|
||||
escape.getValue() = "w" and
|
||||
getInRange(low, high).regexpMatch("\\w")
|
||||
or
|
||||
escape.getValue() = "d" and
|
||||
getInRange(low, high).regexpMatch("\\d")
|
||||
or
|
||||
escape.getValue() = "s" and
|
||||
getInRange(low, high).regexpMatch("\\s")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the unicode code point for a `char`. */
|
||||
bindingset[char]
|
||||
int toCodePoint(string char) { result.toUnicode() = char }
|
||||
|
||||
/** A character range that appears to be overly wide. */
|
||||
class OverlyWideRange extends RegExpCharacterRange {
|
||||
OverlyWideRange() {
|
||||
exists(int low, int high, int numChars |
|
||||
isRange(this, low, high) and
|
||||
numChars = (1 + high - low) and
|
||||
this.getRootTerm().isUsedAsRegExp() and
|
||||
numChars >= 10
|
||||
|
|
||||
// across the Z-a range (which includes backticks)
|
||||
toCodePoint("Z") >= low and
|
||||
toCodePoint("a") <= high
|
||||
or
|
||||
// across the 9-A range (which includes e.g. ; and ?)
|
||||
toCodePoint("9") >= low and
|
||||
toCodePoint("A") <= high
|
||||
or
|
||||
// a non-alphanumeric char as part of the range boundaries
|
||||
exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and
|
||||
// while still being ascii
|
||||
low < 128 and
|
||||
high < 128
|
||||
) and
|
||||
// allowlist for known ranges
|
||||
not this = allowedWideRanges()
|
||||
}
|
||||
|
||||
/** Gets a string representation of a character class that matches the same chars as this range. */
|
||||
string printEquivalent() { result = RangePrinter::printEquivalentCharClass(this) }
|
||||
}
|
||||
|
||||
/** Gets a range that should not be reported as an overly wide range. */
|
||||
RegExpCharacterRange allowedWideRanges() {
|
||||
// ~ is the last printable ASCII character, it's used right in various wide ranges.
|
||||
result.isRange(_, "~")
|
||||
or
|
||||
// the same with " " and "!". " " is the first printable character, and "!" is the first non-white-space printable character.
|
||||
result.isRange([" ", "!"], _)
|
||||
or
|
||||
// the `[@-_]` range is intentional
|
||||
result.isRange("@", "_")
|
||||
or
|
||||
// starting from the zero byte is a good indication that it's purposely matching a large range.
|
||||
result.isRange(0.toUnicode(), _)
|
||||
}
|
||||
|
||||
/** Gets a char between (and including) `low` and `high`. */
|
||||
bindingset[low, high]
|
||||
private string getInRange(string low, string high) {
|
||||
result = [toCodePoint(low) .. toCodePoint(high)].toUnicode()
|
||||
}
|
||||
|
||||
/** A module computing an equivalent character class for an overly wide range. */
|
||||
module RangePrinter {
|
||||
bindingset[char]
|
||||
bindingset[result]
|
||||
private string next(string char) {
|
||||
exists(int prev, int next |
|
||||
prev.toUnicode() = char and
|
||||
next.toUnicode() = result and
|
||||
next = prev + 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the points where the parts of the pretty printed range should be cut off. */
|
||||
private string cutoffs() { result = ["A", "Z", "a", "z", "0", "9"] }
|
||||
|
||||
/** Gets the char to use in the low end of a range for a given `cut` */
|
||||
private string lowCut(string cut) {
|
||||
cut = ["A", "a", "0"] and
|
||||
result = cut
|
||||
or
|
||||
cut = ["Z", "z", "9"] and
|
||||
result = next(cut)
|
||||
}
|
||||
|
||||
/** Gets the char to use in the high end of a range for a given `cut` */
|
||||
private string highCut(string cut) {
|
||||
cut = ["Z", "z", "9"] and
|
||||
result = cut
|
||||
or
|
||||
cut = ["A", "a", "0"] and
|
||||
next(result) = cut
|
||||
}
|
||||
|
||||
/** Gets the cutoff char used for a given `part` of a range when pretty-printing it. */
|
||||
private string cutoff(OverlyWideRange range, int part) {
|
||||
exists(int low, int high | isRange(range, low, high) |
|
||||
result =
|
||||
rank[part + 1](string cut |
|
||||
cut = cutoffs() and low < toCodePoint(cut) and toCodePoint(cut) < high
|
||||
|
|
||||
cut order by toCodePoint(cut)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the number of parts we should print for a given `range`. */
|
||||
private int parts(OverlyWideRange range) { result = 1 + count(cutoff(range, _)) }
|
||||
|
||||
/** Holds if the given part of a range should span from `low` to `high`. */
|
||||
private predicate part(OverlyWideRange range, int part, string low, string high) {
|
||||
// first part.
|
||||
part = 0 and
|
||||
(
|
||||
range.isRange(low, high) and
|
||||
parts(range) = 1
|
||||
or
|
||||
parts(range) >= 2 and
|
||||
range.isRange(low, _) and
|
||||
high = highCut(cutoff(range, part))
|
||||
)
|
||||
or
|
||||
// middle
|
||||
part >= 1 and
|
||||
part < parts(range) - 1 and
|
||||
low = lowCut(cutoff(range, part - 1)) and
|
||||
high = highCut(cutoff(range, part))
|
||||
or
|
||||
// last.
|
||||
part = parts(range) - 1 and
|
||||
low = lowCut(cutoff(range, part - 1)) and
|
||||
range.isRange(_, high)
|
||||
}
|
||||
|
||||
/** Gets an escaped `char` for use in a character class. */
|
||||
bindingset[char]
|
||||
private string escape(string char) {
|
||||
exists(string reg | reg = "(\\[|\\]|\\\\|-|/)" |
|
||||
if char.regexpMatch(reg) then result = "\\" + char else result = char
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a part of the equivalent range. */
|
||||
private string printEquivalentCharClass(OverlyWideRange range, int part) {
|
||||
exists(string low, string high | part(range, part, low, high) |
|
||||
if
|
||||
isAlphanumeric(low) and
|
||||
isAlphanumeric(high)
|
||||
then result = low + "-" + high
|
||||
else
|
||||
result =
|
||||
strictconcat(string char | char = getInRange(low, high) | escape(char) order by char)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the entire pretty printed equivalent range. */
|
||||
string printEquivalentCharClass(OverlyWideRange range) {
|
||||
result =
|
||||
strictconcat(string r, int part |
|
||||
r = "[" and part = -1 and exists(range)
|
||||
or
|
||||
r = printEquivalentCharClass(range, part)
|
||||
or
|
||||
r = "]" and part = parts(range)
|
||||
|
|
||||
r order by part
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a char range that is overly large because of `reason`. */
|
||||
RegExpCharacterRange getABadRange(string reason, int priority) {
|
||||
result instanceof OverlyWideRange and
|
||||
priority = 0 and
|
||||
exists(string equiv | equiv = result.(OverlyWideRange).printEquivalent() |
|
||||
if equiv.length() <= 50
|
||||
then reason = "is equivalent to " + equiv
|
||||
else reason = "is equivalent to " + equiv.substring(0, 50) + "..."
|
||||
)
|
||||
or
|
||||
priority = 1 and
|
||||
exists(RegExpCharacterRange other |
|
||||
reason = "overlaps with " + other + " in the same character class" and
|
||||
rankRange(result) < rankRange(other) and
|
||||
overlap(result, other)
|
||||
)
|
||||
or
|
||||
priority = 2 and
|
||||
exists(RegExpCharacterClassEscape escape |
|
||||
reason = "overlaps with " + escape + " in the same character class" and
|
||||
overlapsWithCharEscape(result, escape)
|
||||
)
|
||||
or
|
||||
reason = "is empty" and
|
||||
priority = 3 and
|
||||
exists(int low, int high |
|
||||
isRange(result, low, high) and
|
||||
low > high
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `range` matches suspiciously many characters. */
|
||||
predicate problem(RegExpCharacterRange range, string reason) {
|
||||
reason =
|
||||
strictconcat(string m, int priority |
|
||||
range = getABadRange(m, priority)
|
||||
|
|
||||
m, ", and " order by priority desc
|
||||
) and
|
||||
// specifying a range using an escape is usually OK.
|
||||
not range.getAChild() instanceof RegExpEscape and
|
||||
// Unicode escapes in strings are interpreted before it turns into a regexp,
|
||||
// so e.g. [\u0001-\uFFFF] will just turn up as a range between two constants.
|
||||
// We therefore exclude these ranges.
|
||||
range.getRootTerm().getParent() instanceof RegExpLiteral and
|
||||
// is used as regexp (mostly for JS where regular expressions are parsed eagerly)
|
||||
range.getRootTerm().isUsedAsRegExp()
|
||||
}
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
// OverlyLargeRangeQuery should be used directly from the shared pack, and not from this file.
|
||||
deprecated import codeql.regex.OverlyLargeRangeQuery::Make<TreeView> as Dep
|
||||
import Dep
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "PAM Authorization" vulnerabilities.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "PAM Authorization" vulnerabilities.
|
||||
*/
|
||||
module PamAuthorizationCustomizations {
|
||||
/**
|
||||
* Models a node corresponding to the `pam` library
|
||||
*/
|
||||
API::Node libPam() {
|
||||
exists(API::CallNode findLibCall, API::CallNode cdllCall |
|
||||
findLibCall =
|
||||
API::moduleImport("ctypes").getMember("util").getMember("find_library").getACall() and
|
||||
findLibCall.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText() = "pam" and
|
||||
cdllCall = API::moduleImport("ctypes").getMember("CDLL").getACall() and
|
||||
cdllCall.getParameter(0).getAValueReachingSink() = findLibCall
|
||||
|
|
||||
result = cdllCall.getReturn()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow source for "PAM Authorization" vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "PAM Authorization" vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* A vulnerable `pam_authenticate` call considered as a flow sink.
|
||||
*/
|
||||
class VulnPamAuthCall extends API::CallNode, Sink {
|
||||
VulnPamAuthCall() {
|
||||
exists(DataFlow::Node h |
|
||||
this = libPam().getMember("pam_authenticate").getACall() and
|
||||
h = this.getArg(0) and
|
||||
not exists(API::CallNode acctMgmtCall |
|
||||
acctMgmtCall = libPam().getMember("pam_acct_mgmt").getACall() and
|
||||
DataFlow::localFlow(h, acctMgmtCall.getArg(0))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting "PAM Authorization" vulnerabilities.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `PamAuthorization::Configuration` is needed, otherwise
|
||||
* `PamAuthorizationCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import PamAuthorizationCustomizations::PamAuthorizationCustomizations
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting "PAM Authorization" vulnerabilities.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "PamAuthorization" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) { node instanceof Sink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// Models flow from a remotely supplied username field to a PAM `handle`.
|
||||
// `retval = pam_start(service, username, byref(conv), byref(handle))`
|
||||
exists(API::CallNode pamStart, DataFlow::Node handle, API::CallNode pointer |
|
||||
pointer = API::moduleImport("ctypes").getMember(["pointer", "byref"]).getACall() and
|
||||
pamStart = libPam().getMember("pam_start").getACall() and
|
||||
pointer = pamStart.getArg(3) and
|
||||
handle = pointer.getArg(0) and
|
||||
pamStart.getArg(1) = node1 and
|
||||
handle = node2
|
||||
)
|
||||
or
|
||||
// Flow from handle to the authenticate call in the final step
|
||||
exists(VulnPamAuthCall c | c.getArg(0) = node1 | node2 = c)
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
private import semmle.python.RegexTreeView
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
@@ -20,6 +20,9 @@ private import semmle.python.ApiGraphs
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module PolynomialReDoS {
|
||||
private import TreeView
|
||||
import codeql.regex.nfa.SuperlinearBackTracking::Make<TreeView>
|
||||
|
||||
/**
|
||||
* A data flow source for "polynomial regular expression denial of service (ReDoS)" vulnerabilities.
|
||||
*/
|
||||
|
||||
@@ -41,9 +41,7 @@ module StackTraceExposure {
|
||||
/**
|
||||
* A source of exception info, considered as a flow source.
|
||||
*/
|
||||
class ExceptionInfoAsSource extends Source {
|
||||
ExceptionInfoAsSource() { this instanceof ExceptionInfo }
|
||||
}
|
||||
class ExceptionInfoAsSource extends Source instanceof ExceptionInfo { }
|
||||
|
||||
/**
|
||||
* The body of a HTTP response that will be returned from a server, considered as a flow sink.
|
||||
|
||||
@@ -30,9 +30,7 @@ module XmlBomb {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/** A source of remote user input, considered as a flow source for XML bomb vulnerabilities. */
|
||||
class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
class RemoteFlowSourceAsSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* A call to an XML parser that is vulnerable to XML bombs.
|
||||
|
||||
@@ -30,9 +30,7 @@ module Xxe {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/** A source of remote user input, considered as a flow source for XXE vulnerabilities. */
|
||||
class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
class RemoteFlowSourceAsSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* A call to an XML parser that is vulnerable to XXE.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides predicates for recommended encryption key sizes.
|
||||
* Such that we can share this logic across our CodeQL analysis of different languages.
|
||||
*/
|
||||
|
||||
/** Returns the minimum recommended key size for RSA. */
|
||||
int minSecureKeySizeRsa() { result = 2048 }
|
||||
|
||||
/** Returns the minimum recommended key size for DSA. */
|
||||
int minSecureKeySizeDsa() { result = 2048 }
|
||||
|
||||
/** Returns the minimum recommended key size for DH. */
|
||||
int minSecureKeySizeDh() { result = 2048 }
|
||||
|
||||
/** Returns the minimum recommended key size for elliptic curve cryptography. */
|
||||
int minSecureKeySizeEcc() { result = 256 }
|
||||
|
||||
/** Returns the minimum recommended key size for AES. */
|
||||
int minSecureKeySizeAes() { result = 128 }
|
||||
@@ -62,284 +62,7 @@
|
||||
* a suffix `x` (possible empty) that is most likely __not__ accepted.
|
||||
*/
|
||||
|
||||
import NfaUtils
|
||||
|
||||
/**
|
||||
* Holds if state `s` might be inside a backtracking repetition.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate stateInsideBacktracking(State s) {
|
||||
s.getRepr().getParent*() instanceof MaybeBacktrackingRepetition
|
||||
}
|
||||
|
||||
/**
|
||||
* A infinitely repeating quantifier that might backtrack.
|
||||
*/
|
||||
private class MaybeBacktrackingRepetition extends InfiniteRepetitionQuantifier {
|
||||
MaybeBacktrackingRepetition() {
|
||||
exists(RegExpTerm child |
|
||||
child instanceof RegExpAlt or
|
||||
child instanceof RegExpQuantifier
|
||||
|
|
||||
child.getParent+() = this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A state in the product automaton.
|
||||
*/
|
||||
private newtype TStatePair =
|
||||
/**
|
||||
* We lazily only construct those states that we are actually
|
||||
* going to need: `(q, q)` for every fork state `q`, and any
|
||||
* pair of states that can be reached from a pair that we have
|
||||
* already constructed. To cut down on the number of states,
|
||||
* we only represent states `(q1, q2)` where `q1` is lexicographically
|
||||
* no bigger than `q2`.
|
||||
*
|
||||
* States are only constructed if both states in the pair are
|
||||
* inside a repetition that might backtrack.
|
||||
*/
|
||||
MkStatePair(State q1, State q2) {
|
||||
isFork(q1, _, _, _, _) and q2 = q1
|
||||
or
|
||||
(step(_, _, _, q1, q2) or step(_, _, _, q2, q1)) and
|
||||
rankState(q1) <= rankState(q2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a unique number for a `state`.
|
||||
* Is used to create an ordering of states, where states with the same `toString()` will be ordered differently.
|
||||
*/
|
||||
private int rankState(State state) {
|
||||
state =
|
||||
rank[result](State s, Location l |
|
||||
stateInsideBacktracking(s) and
|
||||
l = s.getRepr().getLocation()
|
||||
|
|
||||
s order by l.getStartLine(), l.getStartColumn(), s.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A state in the product automaton.
|
||||
*/
|
||||
private class StatePair extends TStatePair {
|
||||
State q1;
|
||||
State q2;
|
||||
|
||||
StatePair() { this = MkStatePair(q1, q2) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "(" + q1 + ", " + q2 + ")" }
|
||||
|
||||
/** Gets the first component of the state pair. */
|
||||
State getLeft() { result = q1 }
|
||||
|
||||
/** Gets the second component of the state pair. */
|
||||
State getRight() { result = q2 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for `(fork, fork)` state pairs when `isFork(fork, _, _, _, _)` holds.
|
||||
*
|
||||
* Used in `statePairDistToFork`
|
||||
*/
|
||||
private predicate isStatePairFork(StatePair p) {
|
||||
exists(State fork | p = MkStatePair(fork, fork) and isFork(fork, _, _, _, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are transitions from the components of `q` to the corresponding
|
||||
* components of `r`.
|
||||
*
|
||||
* Used in `statePairDistToFork`
|
||||
*/
|
||||
private predicate reverseStep(StatePair r, StatePair q) { step(q, _, _, r) }
|
||||
|
||||
/**
|
||||
* Gets the minimum length of a path from `q` to `r` in the
|
||||
* product automaton.
|
||||
*/
|
||||
private int statePairDistToFork(StatePair q, StatePair r) =
|
||||
shortestDistances(isStatePairFork/1, reverseStep/2)(r, q, result)
|
||||
|
||||
/**
|
||||
* Holds if there are transitions from `q` to `r1` and from `q` to `r2`
|
||||
* labelled with `s1` and `s2`, respectively, where `s1` and `s2` do not
|
||||
* trivially have an empty intersection.
|
||||
*
|
||||
* This predicate only holds for states associated with regular expressions
|
||||
* that have at least one repetition quantifier in them (otherwise the
|
||||
* expression cannot be vulnerable to ReDoS attacks anyway).
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, State r2) {
|
||||
stateInsideBacktracking(q) and
|
||||
exists(State q1, State q2 |
|
||||
q1 = epsilonSucc*(q) and
|
||||
delta(q1, s1, r1) and
|
||||
q2 = epsilonSucc*(q) and
|
||||
delta(q2, s2, r2) and
|
||||
// Use pragma[noopt] to prevent intersect(s1,s2) from being the starting point of the join.
|
||||
// From (s1,s2) it would find a huge number of intermediate state pairs (q1,q2) originating from different literals,
|
||||
// and discover at the end that no `q` can reach both `q1` and `q2` by epsilon transitions.
|
||||
exists(intersect(s1, s2))
|
||||
|
|
||||
s1 != s2
|
||||
or
|
||||
r1 != r2
|
||||
or
|
||||
r1 = r2 and q1 != q2
|
||||
or
|
||||
// If q can reach itself by epsilon transitions, then there are two distinct paths to the q1/q2 state:
|
||||
// one that uses the loop and one that doesn't. The engine will separately attempt to match with each path,
|
||||
// despite ending in the same state. The "fork" thus arises from the choice of whether to use the loop or not.
|
||||
// To avoid every state in the loop becoming a fork state,
|
||||
// we arbitrarily pick the InfiniteRepetitionQuantifier state as the canonical fork state for the loop
|
||||
// (every epsilon-loop must contain such a state).
|
||||
//
|
||||
// We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself.
|
||||
// This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`.
|
||||
// The below code is therefore a heuristic, that only flags regular expressions such as `/(a*)*b/`,
|
||||
// and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently.
|
||||
r1 = r2 and
|
||||
q1 = q2 and
|
||||
epsilonSucc+(q) = q and
|
||||
exists(RegExpTerm term | term = q.getRepr() | term instanceof InfiniteRepetitionQuantifier) and
|
||||
// One of the mid states is an infinite quantifier itself
|
||||
exists(State mid, RegExpTerm term |
|
||||
mid = epsilonSucc+(q) and
|
||||
term = mid.getRepr() and
|
||||
term instanceof InfiniteRepetitionQuantifier and
|
||||
q = epsilonSucc+(mid) and
|
||||
not mid = q
|
||||
)
|
||||
) and
|
||||
stateInsideBacktracking(r1) and
|
||||
stateInsideBacktracking(r2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state pair `(q1, q2)` or `(q2, q1)`; note that only
|
||||
* one or the other is defined.
|
||||
*/
|
||||
private StatePair mkStatePair(State q1, State q2) {
|
||||
result = MkStatePair(q1, q2) or result = MkStatePair(q2, q1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are transitions from the components of `q` to the corresponding
|
||||
* components of `r` labelled with `s1` and `s2`, respectively.
|
||||
*/
|
||||
private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, StatePair r) {
|
||||
exists(State r1, State r2 | step(q, s1, s2, r1, r2) and r = mkStatePair(r1, r2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are transitions from the components of `q` to `r1` and `r2`
|
||||
* labelled with `s1` and `s2`, respectively.
|
||||
*
|
||||
* We only consider transitions where the resulting states `(r1, r2)` are both
|
||||
* inside a repetition that might backtrack.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, State r1, State r2) {
|
||||
exists(State q1, State q2 | q.getLeft() = q1 and q.getRight() = q2 |
|
||||
deltaClosed(q1, s1, r1) and
|
||||
deltaClosed(q2, s2, r2) and
|
||||
// use noopt to force the join on `intersect` to happen last.
|
||||
exists(intersect(s1, s2))
|
||||
) and
|
||||
stateInsideBacktracking(r1) and
|
||||
stateInsideBacktracking(r2)
|
||||
}
|
||||
|
||||
private newtype TTrace =
|
||||
Nil() or
|
||||
Step(InputSymbol s1, InputSymbol s2, TTrace t) { isReachableFromFork(_, _, s1, s2, t, _) }
|
||||
|
||||
/**
|
||||
* A list of pairs of input symbols that describe a path in the product automaton
|
||||
* starting from some fork state.
|
||||
*/
|
||||
private class Trace extends TTrace {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
this = Nil() and result = "Nil()"
|
||||
or
|
||||
exists(InputSymbol s1, InputSymbol s2, Trace t | this = Step(s1, s2, t) |
|
||||
result = "Step(" + s1 + ", " + s2 + ", " + t + ")"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `r` is reachable from `(fork, fork)` under input `w`, and there is
|
||||
* a path from `r` back to `(fork, fork)` with `rem` steps.
|
||||
*/
|
||||
private predicate isReachableFromFork(State fork, StatePair r, Trace w, int rem) {
|
||||
exists(InputSymbol s1, InputSymbol s2, Trace v |
|
||||
isReachableFromFork(fork, r, s1, s2, v, rem) and
|
||||
w = Step(s1, s2, v)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isReachableFromFork(
|
||||
State fork, StatePair r, InputSymbol s1, InputSymbol s2, Trace v, int rem
|
||||
) {
|
||||
// base case
|
||||
exists(State q1, State q2 |
|
||||
isFork(fork, s1, s2, q1, q2) and
|
||||
r = MkStatePair(q1, q2) and
|
||||
v = Nil() and
|
||||
rem = statePairDistToFork(r, MkStatePair(fork, fork))
|
||||
)
|
||||
or
|
||||
// recursive case
|
||||
exists(StatePair p |
|
||||
isReachableFromFork(fork, p, v, rem + 1) and
|
||||
step(p, s1, s2, r) and
|
||||
rem = statePairDistToFork(r, MkStatePair(fork, fork))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a state in the product automaton from which `(fork, fork)` is
|
||||
* reachable in zero or more epsilon transitions.
|
||||
*/
|
||||
private StatePair getAForkPair(State fork) {
|
||||
isFork(fork, _, _, _, _) and
|
||||
result = MkStatePair(epsilonPred*(fork), epsilonPred*(fork))
|
||||
}
|
||||
|
||||
/** An implementation of a chain containing chars for use by `Concretizer`. */
|
||||
private module CharTreeImpl implements CharTree {
|
||||
class CharNode = Trace;
|
||||
|
||||
CharNode getPrev(CharNode t) { t = Step(_, _, result) }
|
||||
|
||||
/** Holds if `n` is a trace that is used by `concretize` in `isPumpable`. */
|
||||
predicate isARelevantEnd(CharNode n) {
|
||||
exists(State f | isReachableFromFork(f, getAForkPair(f), n, _))
|
||||
}
|
||||
|
||||
string getChar(CharNode t) {
|
||||
exists(InputSymbol s1, InputSymbol s2 | t = Step(s1, s2, _) | result = intersect(s1, s2))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fork` is a pumpable fork with word `w`.
|
||||
*/
|
||||
private predicate isPumpable(State fork, string w) {
|
||||
exists(StatePair q, Trace t |
|
||||
isReachableFromFork(fork, q, t, _) and
|
||||
q = getAForkPair(fork) and
|
||||
w = Concretizer<CharTreeImpl>::concretize(t)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `state` has exponential ReDoS */
|
||||
predicate hasReDoSResult = ReDoSPruning<isPumpable/2>::hasReDoSResult/4;
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
// ExponentialBackTracking should be used directly from the shared pack, and not from this file.
|
||||
deprecated private import codeql.regex.nfa.ExponentialBackTracking::Make<TreeView> as Dep
|
||||
import Dep
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Provides predicates for reasoning about regular expressions
|
||||
* that match URLs and hostname patterns.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeImpl
|
||||
private import semmle.python.dataflow.new.Regexp as Regexp
|
||||
private import codeql.regex.HostnameRegexp as Shared
|
||||
|
||||
private module Impl implements Shared::HostnameRegexpSig<TreeImpl> {
|
||||
class DataFlowNode = DataFlow::Node;
|
||||
|
||||
class RegExpPatternSource = Regexp::RegExpPatternSource;
|
||||
}
|
||||
|
||||
import Shared::Make<TreeImpl, Impl>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Provides Python-specific definitions for use in the NfaUtils module.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.RegexTreeView
|
||||
|
||||
/**
|
||||
* Holds if `term` is an escape class representing e.g. `\d`.
|
||||
* `clazz` is which character class it represents, e.g. "d" for `\d`.
|
||||
*/
|
||||
predicate isEscapeClass(RegExpTerm term, string clazz) {
|
||||
exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `term` is a possessive quantifier.
|
||||
* As python's regexes do not support possessive quantifiers, this never holds, but is used by the shared library.
|
||||
*/
|
||||
predicate isPossessive(RegExpQuantifier term) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against.
|
||||
* Not yet implemented for Python.
|
||||
*/
|
||||
predicate matchesAnyPrefix(RegExpTerm term) { any() }
|
||||
|
||||
/**
|
||||
* Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against.
|
||||
* Not yet implemented for Python.
|
||||
*/
|
||||
predicate matchesAnySuffix(RegExpTerm term) { any() }
|
||||
|
||||
/**
|
||||
* Holds if the regular expression should not be considered.
|
||||
*
|
||||
* We make the pragmatic performance optimization to ignore regular expressions in files
|
||||
* that does not belong to the project code (such as installed dependencies).
|
||||
*/
|
||||
predicate isExcluded(RegExpParent parent) {
|
||||
not exists(parent.getRegex().getLocation().getFile().getRelativePath())
|
||||
or
|
||||
// Regexes with many occurrences of ".*" may cause the polynomial ReDoS computation to explode, so
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
@@ -3,155 +3,7 @@
|
||||
* and for testing which capture groups are filled when a particular regexp matches a string.
|
||||
*/
|
||||
|
||||
import NfaUtils
|
||||
|
||||
/** A root term */
|
||||
class RootTerm extends RegExpTerm {
|
||||
RootTerm() { this.isRootTerm() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if it should be tested whether `root` matches `str`.
|
||||
*
|
||||
* If `ignorePrefix` is true, then a regexp without a start anchor will be treated as if it had a start anchor.
|
||||
* E.g. a regular expression `/foo$/` will match any string that ends with "foo",
|
||||
* but if `ignorePrefix` is true, it will only match "foo".
|
||||
*
|
||||
* If `testWithGroups` is true, then the `RegexpMatching::fillsCaptureGroup` predicate can be used to determine which capture
|
||||
* groups are filled by a string.
|
||||
*/
|
||||
signature predicate isRegexpMatchingCandidateSig(
|
||||
RootTerm root, string str, boolean ignorePrefix, boolean testWithGroups
|
||||
);
|
||||
|
||||
/**
|
||||
* A module for determining if a regexp matches a given string,
|
||||
* and reasoning about which capture groups are filled by a given string.
|
||||
*
|
||||
* The module parameter `isCandidate` determines which strings should be tested,
|
||||
* and the results can be read from the `matches` and `fillsCaptureGroup` predicates.
|
||||
*/
|
||||
module RegexpMatching<isRegexpMatchingCandidateSig/4 isCandidate> {
|
||||
/**
|
||||
* Gets a state the regular expression `reg` can be in after matching the `i`th char in `str`.
|
||||
* The regular expression is modeled as a non-determistic finite automaton,
|
||||
* the regular expression can therefore be in multiple states after matching a character.
|
||||
*
|
||||
* It's a forward search to all possible states, and there is thus no guarantee that the state is on a path to an accepting state.
|
||||
*/
|
||||
private State getAState(RootTerm reg, int i, string str, boolean ignorePrefix) {
|
||||
// start state, the -1 position before any chars have been matched
|
||||
i = -1 and
|
||||
isCandidate(reg, str, ignorePrefix, _) and
|
||||
result.getRepr().getRootTerm() = reg and
|
||||
isStartState(result)
|
||||
or
|
||||
// recursive case
|
||||
result = getAStateAfterMatching(reg, _, str, i, _, ignorePrefix)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next state after the `prev` state from `reg`.
|
||||
* `prev` is the state after matching `fromIndex` chars in `str`,
|
||||
* and the result is the state after matching `toIndex` chars in `str`.
|
||||
*
|
||||
* This predicate is used as a step relation in the forwards search (`getAState`),
|
||||
* and also as a step relation in the later backwards search (`getAStateThatReachesAccept`).
|
||||
*/
|
||||
private State getAStateAfterMatching(
|
||||
RootTerm reg, State prev, string str, int toIndex, int fromIndex, boolean ignorePrefix
|
||||
) {
|
||||
// the basic recursive case - outlined into a noopt helper to make performance work out.
|
||||
result = getAStateAfterMatchingAux(reg, prev, str, toIndex, fromIndex, ignorePrefix)
|
||||
or
|
||||
// we can skip past word boundaries if the next char is a non-word char.
|
||||
fromIndex = toIndex and
|
||||
prev.getRepr() instanceof RegExpWordBoundary and
|
||||
prev = getAState(reg, toIndex, str, ignorePrefix) and
|
||||
after(prev.getRepr()) = result and
|
||||
str.charAt(toIndex + 1).regexpMatch("\\W") // \W matches any non-word char.
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
private State getAStateAfterMatchingAux(
|
||||
RootTerm reg, State prev, string str, int toIndex, int fromIndex, boolean ignorePrefix
|
||||
) {
|
||||
prev = getAState(reg, fromIndex, str, ignorePrefix) and
|
||||
fromIndex = toIndex - 1 and
|
||||
exists(string char | char = str.charAt(toIndex) | specializedDeltaClosed(prev, char, result)) and
|
||||
not discardedPrefixStep(prev, result, ignorePrefix)
|
||||
}
|
||||
|
||||
/** Holds if a step from `prev` to `next` should be discarded when the `ignorePrefix` flag is set. */
|
||||
private predicate discardedPrefixStep(State prev, State next, boolean ignorePrefix) {
|
||||
prev = mkMatch(any(RegExpRoot r)) and
|
||||
ignorePrefix = true and
|
||||
next = prev
|
||||
}
|
||||
|
||||
// The `deltaClosed` relation specialized to the chars that exists in strings tested by a `MatchedRegExp`.
|
||||
private predicate specializedDeltaClosed(State prev, string char, State next) {
|
||||
deltaClosed(prev, specializedGetAnInputSymbolMatching(char), next)
|
||||
}
|
||||
|
||||
// The `getAnInputSymbolMatching` relation specialized to the chars that exists in strings tested by a `MatchedRegExp`.
|
||||
pragma[noinline]
|
||||
private InputSymbol specializedGetAnInputSymbolMatching(string char) {
|
||||
exists(string s, RootTerm r | isCandidate(r, s, _, _) | char = s.charAt(_)) and
|
||||
result = getAnInputSymbolMatching(char)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th state on a path to the accepting state when `reg` matches `str`.
|
||||
* Starts with an accepting state as found by `getAState` and searches backwards
|
||||
* to the start state through the reachable states (as found by `getAState`).
|
||||
*
|
||||
* This predicate satisfies the invariant that the result state can be reached with `i` steps from a start state,
|
||||
* and an accepting state can be found after (`str.length() - 1 - i`) steps from the result.
|
||||
* The result state is therefore always on a valid path where `reg` accepts `str`.
|
||||
*
|
||||
* This predicate is only used to find which capture groups a regular expression has filled,
|
||||
* and thus the search is only performed for the strings in the `testWithGroups(..)` predicate.
|
||||
*/
|
||||
private State getAStateThatReachesAccept(RootTerm reg, int i, string str, boolean ignorePrefix) {
|
||||
// base case, reaches an accepting state from the last state in `getAState(..)`
|
||||
isCandidate(reg, str, ignorePrefix, true) and
|
||||
i = str.length() - 1 and
|
||||
result = getAState(reg, i, str, ignorePrefix) and
|
||||
epsilonSucc*(result) = Accept(_)
|
||||
or
|
||||
// recursive case. `next` is the next state to be matched after matching `prev`.
|
||||
// this predicate is doing a backwards search, so `prev` is the result we are looking for.
|
||||
exists(State next, State prev, int fromIndex, int toIndex |
|
||||
next = getAStateThatReachesAccept(reg, toIndex, str, ignorePrefix) and
|
||||
next = getAStateAfterMatching(reg, prev, str, toIndex, fromIndex, ignorePrefix) and
|
||||
i = fromIndex and
|
||||
result = prev
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the capture group number that `term` belongs to. */
|
||||
private int group(RegExpTerm term) {
|
||||
exists(RegExpGroup grp | grp.getNumber() = result | term.getParent*() = grp)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `reg` matches `str`, where `str` is in the `isCandidate` predicate.
|
||||
*/
|
||||
predicate matches(RootTerm reg, string str) {
|
||||
exists(State state | state = getAState(reg, str.length() - 1, str, _) |
|
||||
epsilonSucc*(state) = Accept(_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if matching `str` against `reg` may fill capture group number `g`.
|
||||
* Only holds if `str` is in the `testWithGroups` predicate.
|
||||
*/
|
||||
predicate fillsCaptureGroup(RootTerm reg, string str, int g) {
|
||||
exists(State s |
|
||||
s = getAStateThatReachesAccept(reg, _, str, _) and
|
||||
g = group(s.getRepr())
|
||||
)
|
||||
}
|
||||
}
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
// RegexpMatching should be used directly from the shared pack, and not from this file.
|
||||
deprecated import codeql.regex.nfa.RegexpMatching::Make<TreeView> as Dep
|
||||
import Dep
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
/**
|
||||
* Provides classes for working with regular expressions that can
|
||||
* perform backtracking in superlinear time.
|
||||
*/
|
||||
|
||||
import NfaUtils
|
||||
|
||||
/*
|
||||
* This module implements the analysis described in the paper:
|
||||
* Valentin Wustholz, Oswaldo Olivo, Marijn J. H. Heule, and Isil Dillig:
|
||||
* Static Detection of DoS Vulnerabilities in
|
||||
@@ -42,377 +35,7 @@ import NfaUtils
|
||||
* It also doesn't find all transitions in the product automaton, which can cause false negatives.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets any root (start) state of a regular expression.
|
||||
*/
|
||||
private State getRootState() { result = mkMatch(any(RegExpRoot r)) }
|
||||
|
||||
private newtype TStateTuple =
|
||||
MkStateTuple(State q1, State q2, State q3) {
|
||||
// starts at (pivot, pivot, succ)
|
||||
isStartLoops(q1, q3) and q1 = q2
|
||||
or
|
||||
step(_, _, _, _, q1, q2, q3) and FeasibleTuple::isFeasibleTuple(q1, q2, q3)
|
||||
}
|
||||
|
||||
/**
|
||||
* A state in the product automaton.
|
||||
* The product automaton contains 3-tuples of states.
|
||||
*
|
||||
* We lazily only construct those states that we are actually
|
||||
* going to need.
|
||||
* Either a start state `(pivot, pivot, succ)`, or a state
|
||||
* where there exists a transition from an already existing state.
|
||||
*
|
||||
* The exponential variant of this query (`js/redos`) uses an optimization
|
||||
* trick where `q1 <= q2`. This trick cannot be used here as the order
|
||||
* of the elements matter.
|
||||
*/
|
||||
class StateTuple extends TStateTuple {
|
||||
State q1;
|
||||
State q2;
|
||||
State q3;
|
||||
|
||||
StateTuple() { this = MkStateTuple(q1, q2, q3) }
|
||||
|
||||
/**
|
||||
* Gest a string representation of this tuple.
|
||||
*/
|
||||
string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" }
|
||||
|
||||
/**
|
||||
* Holds if this tuple is `(r1, r2, r3)`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate isTuple(State r1, State r2, State r3) { r1 = q1 and r2 = q2 and r3 = q3 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for determining feasible tuples for the product automaton.
|
||||
*
|
||||
* The implementation is split into many predicates for performance reasons.
|
||||
*/
|
||||
private module FeasibleTuple {
|
||||
/**
|
||||
* Holds if the tuple `(r1, r2, r3)` might be on path from a start-state to an end-state in the product automaton.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate isFeasibleTuple(State r1, State r2, State r3) {
|
||||
// The first element is either inside a repetition (or the start state itself)
|
||||
isRepetitionOrStart(r1) and
|
||||
// The last element is inside a repetition
|
||||
stateInsideRepetition(r3) and
|
||||
// The states are reachable in the NFA in the order r1 -> r2 -> r3
|
||||
delta+(r1) = r2 and
|
||||
delta+(r2) = r3 and
|
||||
// The first element can reach a beginning (the "pivot" state in a `(pivot, succ)` pair).
|
||||
canReachABeginning(r1) and
|
||||
// The last element can reach a target (the "succ" state in a `(pivot, succ)` pair).
|
||||
canReachATarget(r3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `s` is either inside a repetition, or is the start state (which is a repetition).
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate isRepetitionOrStart(State s) { stateInsideRepetition(s) or s = getRootState() }
|
||||
|
||||
/**
|
||||
* Holds if state `s` might be inside a backtracking repetition.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate stateInsideRepetition(State s) {
|
||||
s.getRepr().getParent*() instanceof InfiniteRepetitionQuantifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a path in the NFA from `s` to a "pivot" state
|
||||
* (from a `(pivot, succ)` pair that starts the search).
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate canReachABeginning(State s) {
|
||||
delta+(s) = any(State pivot | isStartLoops(pivot, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a path in the NFA from `s` to a "succ" state
|
||||
* (from a `(pivot, succ)` pair that starts the search).
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate canReachATarget(State s) { delta+(s) = any(State succ | isStartLoops(_, succ)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pivot` and `succ` are a pair of loops that could be the beginning of a quadratic blowup.
|
||||
*
|
||||
* There is a slight implementation difference compared to the paper: this predicate requires that `pivot != succ`.
|
||||
* The case where `pivot = succ` causes exponential backtracking and is handled by the `js/redos` query.
|
||||
*/
|
||||
predicate isStartLoops(State pivot, State succ) {
|
||||
pivot != succ and
|
||||
succ.getRepr() instanceof InfiniteRepetitionQuantifier and
|
||||
delta+(pivot) = succ and
|
||||
(
|
||||
pivot.getRepr() instanceof InfiniteRepetitionQuantifier
|
||||
or
|
||||
pivot = mkMatch(any(RegExpRoot root))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a state for which there exists a transition in the NFA from `s'.
|
||||
*/
|
||||
State delta(State s) { delta(s, _, result) }
|
||||
|
||||
/**
|
||||
* Holds if there are transitions from the components of `q` to the corresponding
|
||||
* components of `r` labelled with `s1`, `s2`, and `s3`, respectively.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate step(StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, StateTuple r) {
|
||||
exists(State r1, State r2, State r3 |
|
||||
step(q, s1, s2, s3, r1, r2, r3) and r = MkStateTuple(r1, r2, r3)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are transitions from the components of `q` to `r1`, `r2`, and `r3
|
||||
* labelled with `s1`, `s2`, and `s3`, respectively.
|
||||
*/
|
||||
pragma[noopt]
|
||||
predicate step(
|
||||
StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, State r1, State r2, State r3
|
||||
) {
|
||||
exists(State q1, State q2, State q3 | q.isTuple(q1, q2, q3) |
|
||||
deltaClosed(q1, s1, r1) and
|
||||
deltaClosed(q2, s2, r2) and
|
||||
deltaClosed(q3, s3, r3) and
|
||||
// use noopt to force the join on `getAThreewayIntersect` to happen last.
|
||||
exists(getAThreewayIntersect(s1, s2, s3))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a char that is matched by all the edges `s1`, `s2`, and `s3`.
|
||||
*
|
||||
* The result is not complete, and might miss some combination of edges that share some character.
|
||||
*/
|
||||
pragma[noinline]
|
||||
string getAThreewayIntersect(InputSymbol s1, InputSymbol s2, InputSymbol s3) {
|
||||
result = minAndMaxIntersect(s1, s2) and result = [intersect(s2, s3), intersect(s1, s3)]
|
||||
or
|
||||
result = minAndMaxIntersect(s1, s3) and result = [intersect(s2, s3), intersect(s1, s2)]
|
||||
or
|
||||
result = minAndMaxIntersect(s2, s3) and result = [intersect(s1, s2), intersect(s1, s3)]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum and maximum characters that intersect between `a` and `b`.
|
||||
* This predicate is used to limit the size of `getAThreewayIntersect`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
string minAndMaxIntersect(InputSymbol a, InputSymbol b) {
|
||||
result = [min(intersect(a, b)), max(intersect(a, b))]
|
||||
}
|
||||
|
||||
private newtype TTrace =
|
||||
Nil() or
|
||||
Step(InputSymbol s1, InputSymbol s2, InputSymbol s3, TTrace t) {
|
||||
isReachableFromStartTuple(_, _, t, s1, s2, s3, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of tuples of input symbols that describe a path in the product automaton
|
||||
* starting from some start state.
|
||||
*/
|
||||
class Trace extends TTrace {
|
||||
/**
|
||||
* Gets a string representation of this Trace that can be used for debug purposes.
|
||||
*/
|
||||
string toString() {
|
||||
this = Nil() and result = "Nil()"
|
||||
or
|
||||
exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace t | this = Step(s1, s2, s3, t) |
|
||||
result = "Step(" + s1 + ", " + s2 + ", " + s3 + ", " + t + ")"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a transition from `r` to `q` in the product automaton.
|
||||
* Notice that the arguments are flipped, and thus the direction is backwards.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate tupleDeltaBackwards(StateTuple q, StateTuple r) { step(r, _, _, _, q) }
|
||||
|
||||
/**
|
||||
* Holds if `tuple` is an end state in our search.
|
||||
* That means there exists a pair of loops `(pivot, succ)` such that `tuple = (pivot, succ, succ)`.
|
||||
*/
|
||||
predicate isEndTuple(StateTuple tuple) { tuple = getAnEndTuple(_, _) }
|
||||
|
||||
/**
|
||||
* Gets the minimum length of a path from `r` to some an end state `end`.
|
||||
*
|
||||
* The implementation searches backwards from the end-tuple.
|
||||
* This approach was chosen because it is way more efficient if the first predicate given to `shortestDistances` is small.
|
||||
* The `end` argument must always be an end state.
|
||||
*/
|
||||
int distBackFromEnd(StateTuple r, StateTuple end) =
|
||||
shortestDistances(isEndTuple/1, tupleDeltaBackwards/2)(end, r, result)
|
||||
|
||||
/**
|
||||
* Holds if there exists a pair of repetitions `(pivot, succ)` in the regular expression such that:
|
||||
* `tuple` is reachable from `(pivot, pivot, succ)` in the product automaton,
|
||||
* and there is a distance of `dist` from `tuple` to the nearest end-tuple `(pivot, succ, succ)`,
|
||||
* and a path from a start-state to `tuple` follows the transitions in `trace`.
|
||||
*/
|
||||
private predicate isReachableFromStartTuple(
|
||||
State pivot, State succ, StateTuple tuple, Trace trace, int dist
|
||||
) {
|
||||
exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace v |
|
||||
isReachableFromStartTuple(pivot, succ, v, s1, s2, s3, tuple, dist) and
|
||||
trace = Step(s1, s2, s3, v)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isReachableFromStartTuple(
|
||||
State pivot, State succ, Trace trace, InputSymbol s1, InputSymbol s2, InputSymbol s3,
|
||||
StateTuple tuple, int dist
|
||||
) {
|
||||
// base case.
|
||||
exists(State q1, State q2, State q3 |
|
||||
isStartLoops(pivot, succ) and
|
||||
step(MkStateTuple(pivot, pivot, succ), s1, s2, s3, tuple) and
|
||||
tuple = MkStateTuple(q1, q2, q3) and
|
||||
trace = Nil() and
|
||||
dist = distBackFromEnd(tuple, MkStateTuple(pivot, succ, succ))
|
||||
)
|
||||
or
|
||||
// recursive case
|
||||
exists(StateTuple p |
|
||||
isReachableFromStartTuple(pivot, succ, p, trace, dist + 1) and
|
||||
dist = distBackFromEnd(tuple, MkStateTuple(pivot, succ, succ)) and
|
||||
step(p, s1, s2, s3, tuple)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tuple `(pivot, succ, succ)` from the product automaton.
|
||||
*/
|
||||
StateTuple getAnEndTuple(State pivot, State succ) {
|
||||
isStartLoops(pivot, succ) and
|
||||
result = MkStateTuple(pivot, succ, succ)
|
||||
}
|
||||
|
||||
/** An implementation of a chain containing chars for use by `Concretizer`. */
|
||||
private module CharTreeImpl implements CharTree {
|
||||
class CharNode = Trace;
|
||||
|
||||
CharNode getPrev(CharNode t) { t = Step(_, _, _, result) }
|
||||
|
||||
/** Holds if `n` is used in `isPumpable`. */
|
||||
predicate isARelevantEnd(CharNode n) {
|
||||
exists(State pivot, State succ |
|
||||
isReachableFromStartTuple(pivot, succ, getAnEndTuple(pivot, succ), n, _)
|
||||
)
|
||||
}
|
||||
|
||||
string getChar(CharNode t) {
|
||||
exists(InputSymbol s1, InputSymbol s2, InputSymbol s3 | t = Step(s1, s2, s3, _) |
|
||||
result = getAThreewayIntersect(s1, s2, s3)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if matching repetitions of `pump` can:
|
||||
* 1) Transition from `pivot` back to `pivot`.
|
||||
* 2) Transition from `pivot` to `succ`.
|
||||
* 3) Transition from `succ` to `succ`.
|
||||
*
|
||||
* From theorem 3 in the paper linked in the top of this file we can therefore conclude that
|
||||
* the regular expression has polynomial backtracking - if a rejecting suffix exists.
|
||||
*
|
||||
* This predicate is used by `SuperLinearReDoSConfiguration`, and the final results are
|
||||
* available in the `hasReDoSResult` predicate.
|
||||
*/
|
||||
predicate isPumpable(State pivot, State succ, string pump) {
|
||||
exists(StateTuple q, Trace t |
|
||||
isReachableFromStartTuple(pivot, succ, q, t, _) and
|
||||
q = getAnEndTuple(pivot, succ) and
|
||||
pump = Concretizer<CharTreeImpl>::concretize(t)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if states starting in `state` can have polynomial backtracking with the string `pump`.
|
||||
*/
|
||||
predicate isReDoSCandidate(State state, string pump) { isPumpable(_, state, pump) }
|
||||
|
||||
/**
|
||||
* Holds if repetitions of `pump` at `t` will cause polynomial backtracking.
|
||||
*/
|
||||
predicate polynomialReDoS(RegExpTerm t, string pump, string prefixMsg, RegExpTerm prev) {
|
||||
exists(State s, State pivot |
|
||||
ReDoSPruning<isReDoSCandidate/2>::hasReDoSResult(t, pump, s, prefixMsg) and
|
||||
isPumpable(pivot, s, _) and
|
||||
prev = pivot.getRepr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a message for why `term` can cause polynomial backtracking.
|
||||
*/
|
||||
string getReasonString(RegExpTerm term, string pump, string prefixMsg, RegExpTerm prev) {
|
||||
polynomialReDoS(term, pump, prefixMsg, prev) and
|
||||
result =
|
||||
"Strings " + prefixMsg + "with many repetitions of '" + pump +
|
||||
"' can start matching anywhere after the start of the preceeding " + prev
|
||||
}
|
||||
|
||||
/**
|
||||
* A term that may cause a regular expression engine to perform a
|
||||
* polynomial number of match attempts, relative to the input length.
|
||||
*/
|
||||
class PolynomialBackTrackingTerm extends InfiniteRepetitionQuantifier {
|
||||
string reason;
|
||||
string pump;
|
||||
string prefixMsg;
|
||||
RegExpTerm prev;
|
||||
|
||||
PolynomialBackTrackingTerm() {
|
||||
reason = getReasonString(this, pump, prefixMsg, prev) and
|
||||
// there might be many reasons for this term to have polynomial backtracking - we pick the shortest one.
|
||||
reason = min(string msg | msg = getReasonString(this, _, _, _) | msg order by msg.length(), msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all non-empty successors to the polynomial backtracking term matches the end of the line.
|
||||
*/
|
||||
predicate isAtEndLine() {
|
||||
forall(RegExpTerm succ | this.getSuccessor+() = succ and not matchesEpsilon(succ) |
|
||||
succ instanceof RegExpDollar
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string that should be repeated to cause this regular expression to perform polynomially.
|
||||
*/
|
||||
string getPumpString() { result = pump }
|
||||
|
||||
/**
|
||||
* Gets a message for which prefix a matching string must start with for this term to cause polynomial backtracking.
|
||||
*/
|
||||
string getPrefixMessage() { result = prefixMsg }
|
||||
|
||||
/**
|
||||
* Gets a predecessor to `this`, which also loops on the pump string, and thereby causes polynomial backtracking.
|
||||
*/
|
||||
RegExpTerm getPreviousLoop() { result = prev }
|
||||
|
||||
/**
|
||||
* Gets the reason for the number of match attempts.
|
||||
*/
|
||||
string getReason() { result = reason }
|
||||
}
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
// SuperlinearBackTracking should be used directly from the shared pack, and not from this file.
|
||||
deprecated private import codeql.regex.nfa.SuperlinearBackTracking::Make<TreeView> as Dep
|
||||
import Dep
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
## 0.5.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -13,17 +13,15 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
from CallNode call_to_super, string name
|
||||
from DataFlow::CallCfgNode call_to_super, string name
|
||||
where
|
||||
exists(GlobalVariable gv, ControlFlowNode cn |
|
||||
call_to_super = ClassValue::super_().getACall() and
|
||||
gv.getId() = "super" and
|
||||
cn = call_to_super.getArg(0) and
|
||||
name = call_to_super.getScope().getScope().(Class).getName() and
|
||||
exists(ClassValue other |
|
||||
cn.pointsTo(other) and
|
||||
not other.getScope().getName() = name
|
||||
)
|
||||
call_to_super = API::builtin("super").getACall() and
|
||||
name = call_to_super.getScope().getScope().(Class).getName() and
|
||||
exists(DataFlow::Node arg |
|
||||
arg = call_to_super.getArg(0) and
|
||||
arg.getALocalSource().asExpr().(Name).getId() != name
|
||||
)
|
||||
select call_to_super.getNode(), "First argument to super() should be " + name + "."
|
||||
|
||||
@@ -26,9 +26,10 @@ However, it is worth investigating why a module containing a syntax error
|
||||
was able to persist and address that problem as well.
|
||||
</p>
|
||||
<p>If you suspect that the syntax error is caused by the analysis using the
|
||||
wrong version of Python, consider specifying the version explicitly. For
|
||||
LGTM.com, you can customize extraction using an <code>lgtm.yml</code> file as
|
||||
described <a href="https://lgtm.com/help/lgtm/python-extraction">here</a>.
|
||||
wrong version of Python, consider specifying the version explicitly. When
|
||||
you run code scanning using the CodeQL action, you can configure the Python
|
||||
version to use. For more information, see
|
||||
<a href="https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#analyzing-python-dependencies">Analyzing Python dependencies</a>.
|
||||
</p>
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
@@ -3,200 +3,6 @@
|
||||
* that match URLs and hostname patterns.
|
||||
*/
|
||||
|
||||
private import HostnameRegexpSpecific
|
||||
|
||||
/**
|
||||
* Holds if the given constant is unlikely to occur in the origin part of a URL.
|
||||
*/
|
||||
predicate isConstantInvalidInsideOrigin(RegExpConstant term) {
|
||||
// Look for any of these cases:
|
||||
// - A character that can't occur in the origin
|
||||
// - Two dashes in a row
|
||||
// - A colon that is not part of port or scheme separator
|
||||
// - A slash that is not part of scheme separator
|
||||
term.getValue().regexpMatch(".*(?:[^a-zA-Z0-9.:/-]|--|:[^0-9/]|(?<![/:]|^)/).*")
|
||||
}
|
||||
|
||||
/** Holds if `term` is a dot constant of form `\.` or `[.]`. */
|
||||
predicate isDotConstant(RegExpTerm term) {
|
||||
term.(RegExpCharEscape).getValue() = "."
|
||||
or
|
||||
exists(RegExpCharacterClass cls |
|
||||
term = cls and
|
||||
not cls.isInverted() and
|
||||
cls.getNumChild() = 1 and
|
||||
cls.getAChild().(RegExpConstant).getValue() = "."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `term` is a wildcard `.` or an actual `.` character. */
|
||||
predicate isDotLike(RegExpTerm term) {
|
||||
term instanceof RegExpDot
|
||||
or
|
||||
isDotConstant(term)
|
||||
}
|
||||
|
||||
/** Holds if `term` will only ever be matched against the beginning of the input. */
|
||||
predicate matchesBeginningOfString(RegExpTerm term) {
|
||||
term.isRootTerm()
|
||||
or
|
||||
exists(RegExpTerm parent | matchesBeginningOfString(parent) |
|
||||
term = parent.(RegExpSequence).getChild(0)
|
||||
or
|
||||
parent.(RegExpSequence).getChild(0) instanceof RegExpCaret and
|
||||
term = parent.(RegExpSequence).getChild(1)
|
||||
or
|
||||
term = parent.(RegExpAlt).getAChild()
|
||||
or
|
||||
term = parent.(RegExpGroup).getAChild()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`,
|
||||
* excluding cases where this is at the very beginning of the regexp.
|
||||
*
|
||||
* `i` is bound to the index of the last child in the top-level domain part.
|
||||
*/
|
||||
predicate hasTopLevelDomainEnding(RegExpSequence seq, int i) {
|
||||
seq.getChild(i)
|
||||
.(RegExpConstant)
|
||||
.getValue()
|
||||
.regexpMatch("(?i)" + RegExpPatterns::getACommonTld() + "(:\\d+)?([/?#].*)?") and
|
||||
isDotLike(seq.getChild(i - 1)) and
|
||||
not (i = 1 and matchesBeginningOfString(seq))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given regular expression term contains top-level domain preceded by a dot,
|
||||
* such as `.com`.
|
||||
*/
|
||||
predicate hasTopLevelDomainEnding(RegExpSequence seq) { hasTopLevelDomainEnding(seq, _) }
|
||||
|
||||
/**
|
||||
* Holds if `term` will always match a hostname, that is, all disjunctions contain
|
||||
* a hostname pattern that isn't inside a quantifier.
|
||||
*/
|
||||
predicate alwaysMatchesHostname(RegExpTerm term) {
|
||||
hasTopLevelDomainEnding(term, _)
|
||||
or
|
||||
// `localhost` is considered a hostname pattern, but has no TLD
|
||||
term.(RegExpConstant).getValue().regexpMatch("\\blocalhost\\b")
|
||||
or
|
||||
not term instanceof RegExpAlt and
|
||||
not term instanceof RegExpQuantifier and
|
||||
alwaysMatchesHostname(term.getAChild())
|
||||
or
|
||||
alwaysMatchesHostnameAlt(term)
|
||||
}
|
||||
|
||||
/** Holds if every child of `alt` contains a hostname pattern. */
|
||||
predicate alwaysMatchesHostnameAlt(RegExpAlt alt) {
|
||||
alwaysMatchesHostnameAlt(alt, alt.getNumChild() - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the first `i` children of `alt` contains a hostname pattern.
|
||||
*
|
||||
* This is used instead of `forall` to avoid materializing the set of alternatives
|
||||
* that don't contains hostnames, which is much larger.
|
||||
*/
|
||||
predicate alwaysMatchesHostnameAlt(RegExpAlt alt, int i) {
|
||||
alwaysMatchesHostname(alt.getChild(0)) and i = 0
|
||||
or
|
||||
alwaysMatchesHostnameAlt(alt, i - 1) and
|
||||
alwaysMatchesHostname(alt.getChild(i))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `term` occurs inside a quantifier or alternative (and thus
|
||||
* can not be expected to correspond to a unique match), or as part of
|
||||
* a lookaround assertion (which are rarely used for capture groups).
|
||||
*/
|
||||
predicate isInsideChoiceOrSubPattern(RegExpTerm term) {
|
||||
exists(RegExpParent parent | parent = term.getParent() |
|
||||
parent instanceof RegExpAlt
|
||||
or
|
||||
parent instanceof RegExpQuantifier
|
||||
or
|
||||
parent instanceof RegExpSubPattern
|
||||
or
|
||||
isInsideChoiceOrSubPattern(parent)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `group` is likely to be used as a capture group.
|
||||
*/
|
||||
predicate isLikelyCaptureGroup(RegExpGroup group) {
|
||||
group.isCapture() and
|
||||
not isInsideChoiceOrSubPattern(group)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `seq` contains two consecutive dots `..` or escaped dots.
|
||||
*
|
||||
* At least one of these dots is not intended to be a subdomain separator,
|
||||
* so we avoid flagging the pattern in this case.
|
||||
*/
|
||||
predicate hasConsecutiveDots(RegExpSequence seq) {
|
||||
exists(int i |
|
||||
isDotLike(seq.getChild(i)) and
|
||||
isDotLike(seq.getChild(i + 1))
|
||||
)
|
||||
}
|
||||
|
||||
predicate isIncompleteHostNameRegExpPattern(RegExpTerm regexp, RegExpSequence seq, string msg) {
|
||||
seq = regexp.getAChild*() and
|
||||
exists(RegExpDot unescapedDot, int i, string hostname |
|
||||
hasTopLevelDomainEnding(seq, i) and
|
||||
not isConstantInvalidInsideOrigin(seq.getChild([0 .. i - 1]).getAChild*()) and
|
||||
not isLikelyCaptureGroup(seq.getChild([i .. seq.getNumChild() - 1]).getAChild*()) and
|
||||
unescapedDot = seq.getChild([0 .. i - 1]).getAChild*() and
|
||||
unescapedDot != seq.getChild(i - 1) and // Should not be the '.' immediately before the TLD
|
||||
not hasConsecutiveDots(unescapedDot.getParent()) and
|
||||
hostname =
|
||||
seq.getChild(i - 2).getRawValue() + seq.getChild(i - 1).getRawValue() +
|
||||
seq.getChild(i).getRawValue()
|
||||
|
|
||||
if unescapedDot.getParent() instanceof RegExpQuantifier
|
||||
then
|
||||
// `.*\.example.com` can match `evil.com/?x=.example.com`
|
||||
//
|
||||
// This problem only occurs when the pattern is applied against a full URL, not just a hostname/origin.
|
||||
// We therefore check if the pattern includes a suffix after the TLD, such as `.*\.example.com/`.
|
||||
// Note that a post-anchored pattern (`.*\.example.com$`) will usually fail to match a full URL,
|
||||
// and patterns with neither a suffix nor an anchor fall under the purview of MissingRegExpAnchor.
|
||||
seq.getChild(0) instanceof RegExpCaret and
|
||||
not seq.getAChild() instanceof RegExpDollar and
|
||||
seq.getChild([i .. i + 1]).(RegExpConstant).getValue().regexpMatch(".*[/?#].*") and
|
||||
msg =
|
||||
"has an unrestricted wildcard '" + unescapedDot.getParent().(RegExpQuantifier).getRawValue()
|
||||
+ "' which may cause '" + hostname +
|
||||
"' to be matched anywhere in the URL, outside the hostname."
|
||||
else
|
||||
msg =
|
||||
"has an unescaped '.' before '" + hostname +
|
||||
"', so it might match more hosts than expected."
|
||||
)
|
||||
}
|
||||
|
||||
predicate incompleteHostnameRegExp(
|
||||
RegExpSequence hostSequence, string message, DataFlow::Node aux, string label
|
||||
) {
|
||||
exists(RegExpPatternSource re, RegExpTerm regexp, string msg, string kind |
|
||||
regexp = re.getRegExpTerm() and
|
||||
isIncompleteHostNameRegExpPattern(regexp, hostSequence, msg) and
|
||||
(
|
||||
if re.getAParse() != re
|
||||
then (
|
||||
kind = "string, which is used as a regular expression $@," and
|
||||
aux = re.getAParse()
|
||||
) else (
|
||||
kind = "regular expression" and aux = re
|
||||
)
|
||||
)
|
||||
|
|
||||
message = "This " + kind + " " + msg and label = "here"
|
||||
)
|
||||
}
|
||||
// HostnameRegexp should be used directly from the shared regex pack, and not from this file.
|
||||
deprecated private import semmle.python.security.regexp.HostnameRegex as Dep
|
||||
import Dep
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import semmle.python.RegexTreeView
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.Regexp
|
||||
@@ -11,6 +11,6 @@
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import HostnameRegexpShared
|
||||
private import semmle.python.security.regexp.HostnameRegex as HostnameRegex
|
||||
|
||||
query predicate problems = incompleteHostnameRegExp/4;
|
||||
query predicate problems = HostnameRegex::incompleteHostnameRegExp/4;
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import semmle.python.security.OverlyLargeRangeQuery
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
import codeql.regex.OverlyLargeRangeQuery::Make<TreeView>
|
||||
|
||||
from RegExpCharacterRange range, string reason
|
||||
from TreeView::RegExpCharacterRange range, string reason
|
||||
where problem(range, reason)
|
||||
select range, "Suspicious character range that " + reason + "."
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
* external/cwe/cwe-186
|
||||
*/
|
||||
|
||||
import semmle.python.security.BadTagFilterQuery
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
import codeql.regex.nfa.BadTagFilterQuery::Make<TreeView>
|
||||
|
||||
from HtmlMatchingRegExp regexp, string msg
|
||||
where msg = min(string m | isBadRegexpFilter(regexp, m) | m order by m.length(), m) // there might be multiple, we arbitrarily pick the shortest one
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name PAM authorization bypass due to incorrect usage
|
||||
* @description Not using `pam_acct_mgmt` after `pam_authenticate` to check the validity of a login can lead to authorization bypass.
|
||||
* @kind problem
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 8.1
|
||||
* @precision high
|
||||
@@ -11,28 +11,12 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DataFlow::PathGraph
|
||||
import semmle.python.ApiGraphs
|
||||
import experimental.semmle.python.Concepts
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.security.dataflow.PamAuthorizationQuery
|
||||
|
||||
API::Node libPam() {
|
||||
exists(API::CallNode findLibCall, API::CallNode cdllCall |
|
||||
findLibCall = API::moduleImport("ctypes").getMember("util").getMember("find_library").getACall() and
|
||||
findLibCall.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText() = "pam" and
|
||||
cdllCall = API::moduleImport("ctypes").getMember("CDLL").getACall() and
|
||||
cdllCall.getParameter(0).getAValueReachingSink() = findLibCall
|
||||
|
|
||||
result = cdllCall.getReturn()
|
||||
)
|
||||
}
|
||||
|
||||
from API::CallNode authenticateCall, DataFlow::Node handle
|
||||
where
|
||||
authenticateCall = libPam().getMember("pam_authenticate").getACall() and
|
||||
handle = authenticateCall.getArg(0) and
|
||||
not exists(API::CallNode acctMgmtCall |
|
||||
acctMgmtCall = libPam().getMember("pam_acct_mgmt").getACall() and
|
||||
DataFlow::localFlow(handle, acctMgmtCall.getArg(0))
|
||||
)
|
||||
select authenticateCall,
|
||||
"This PAM authentication call may lead to an authorization bypass, since 'pam_acct_mgmt' is not called afterwards."
|
||||
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"This PAM authentication depends on a $@, and 'pam_acct_mgmt' is not called afterwards.",
|
||||
source.getNode(), "user-provided value"
|
||||
|
||||
@@ -11,13 +11,13 @@ As computational power increases, the ability to break ciphers grows and keys ne
|
||||
<p>
|
||||
The three main asymmetric key algorithms currently in use are Rivest–Shamir–Adleman (RSA) cryptography, Digital Signature Algorithm (DSA), and Elliptic-curve cryptography (ECC).
|
||||
With current technology, key sizes of 2048 bits for RSA and DSA,
|
||||
or 224 bits for ECC, are regarded as unbreakable.
|
||||
or 256 bits for ECC, are regarded as unbreakable.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Increase the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 224 bits.
|
||||
Increase the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 256 bits.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
@@ -45,4 +45,3 @@ Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Len
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.regexp.SuperlinearBackTracking
|
||||
import semmle.python.security.dataflow.PolynomialReDoSQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
* external/cwe/cwe-400
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.regexp.ExponentialBackTracking
|
||||
private import semmle.python.RegexTreeView::RegexTreeView as TreeView
|
||||
import codeql.regex.nfa.ExponentialBackTracking::Make<TreeView>
|
||||
|
||||
from RegExpTerm t, string pump, State s, string prefixMsg
|
||||
from TreeView::RegExpTerm t, string pump, State s, string prefixMsg
|
||||
where
|
||||
hasReDoSResult(t, pump, s, prefixMsg) and
|
||||
// exclude verbose mode regexes for now
|
||||
|
||||
@@ -80,9 +80,7 @@ class NoqaSuppressionComment extends LineSuppressionComment {
|
||||
/**
|
||||
* The scope of an alert suppression comment.
|
||||
*/
|
||||
class SuppressionScope extends @py_comment {
|
||||
SuppressionScope() { this instanceof SuppressionComment }
|
||||
|
||||
class SuppressionScope extends @py_comment instanceof SuppressionComment {
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
@@ -93,7 +91,7 @@ class SuppressionScope extends @py_comment {
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
|
||||
super.covers(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
|
||||
4
python/ql/src/change-notes/2022-11-22-getpass.md
Normal file
4
python/ql/src/change-notes/2022-11-22-getpass.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added modeling of `getpass.getpass` as a source of passwords, which will be an additional source for `py/clear-text-logging-sensitive-data`, `py/clear-text-storage-sensitive-data`, and `py/weak-sensitive-data-hashing`.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Bumped the minimum keysize we consider secure for elliptic curve cryptography from 224 to 256 bits, following current best practices. This might effect results from the _Use of weak cryptographic key_ (`py/weak-crypto-key`) query.
|
||||
3
python/ql/src/change-notes/released/0.5.4.md
Normal file
3
python/ql/src/change-notes/released/0.5.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.4
|
||||
|
||||
No user-facing changes.
|
||||
3
python/ql/src/change-notes/released/0.5.5.md
Normal file
3
python/ql/src/change-notes/released/0.5.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.5
|
||||
|
||||
No user-facing changes.
|
||||
3
python/ql/src/change-notes/released/0.5.6.md
Normal file
3
python/ql/src/change-notes/released/0.5.6.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.6
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.3
|
||||
lastReleaseVersion: 0.5.6
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<!-- Disabled since it refers to examples which do not exist. -->
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
@@ -4,7 +4,7 @@
|
||||
* destination file path is within the destination directory can cause files outside
|
||||
* the destination directory to be overwritten.
|
||||
* @kind path-problem
|
||||
* @id py/tarslip
|
||||
* @id py/tarslip-extended
|
||||
* @problem.severity error
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @problem.severity error
|
||||
* @security-severity 2.9
|
||||
* @sub-severity high
|
||||
* @id py/reflective-xss
|
||||
* @id py/reflective-xss-email
|
||||
* @tags security
|
||||
* external/cwe/cwe-079
|
||||
* external/cwe/cwe-116
|
||||
|
||||
@@ -42,14 +42,10 @@ module CopyFile {
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CopyFile::Range` instead.
|
||||
*/
|
||||
class CopyFile extends DataFlow::Node {
|
||||
CopyFile::Range range;
|
||||
class CopyFile extends DataFlow::Node instanceof CopyFile::Range {
|
||||
DataFlow::Node getAPathArgument() { result = super.getAPathArgument() }
|
||||
|
||||
CopyFile() { this = range }
|
||||
|
||||
DataFlow::Node getAPathArgument() { result = range.getAPathArgument() }
|
||||
|
||||
DataFlow::Node getfsrcArgument() { result = range.getfsrcArgument() }
|
||||
DataFlow::Node getfsrcArgument() { result = super.getfsrcArgument() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling log related APIs. */
|
||||
@@ -74,12 +70,8 @@ module LogOutput {
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `LogOutput::Range` instead.
|
||||
*/
|
||||
class LogOutput extends DataFlow::Node {
|
||||
LogOutput::Range range;
|
||||
|
||||
LogOutput() { this = range }
|
||||
|
||||
DataFlow::Node getAnInput() { result = range.getAnInput() }
|
||||
class LogOutput extends DataFlow::Node instanceof LogOutput::Range {
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling LDAP query execution-related APIs. */
|
||||
@@ -107,15 +99,11 @@ deprecated module LDAPQuery = LdapQuery;
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `LDAPQuery::Range` instead.
|
||||
*/
|
||||
class LdapQuery extends DataFlow::Node {
|
||||
LdapQuery::Range range;
|
||||
|
||||
LdapQuery() { this = range }
|
||||
|
||||
class LdapQuery extends DataFlow::Node instanceof LdapQuery::Range {
|
||||
/**
|
||||
* Gets the argument containing the executed expression.
|
||||
*/
|
||||
DataFlow::Node getQuery() { result = range.getQuery() }
|
||||
DataFlow::Node getQuery() { result = super.getQuery() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapQuery */
|
||||
@@ -146,15 +134,11 @@ deprecated module LDAPEscape = LdapEscape;
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `LDAPEscape::Range` instead.
|
||||
*/
|
||||
class LdapEscape extends DataFlow::Node {
|
||||
LdapEscape::Range range;
|
||||
|
||||
LdapEscape() { this = range }
|
||||
|
||||
class LdapEscape extends DataFlow::Node instanceof LdapEscape::Range {
|
||||
/**
|
||||
* Gets the argument containing the escaped expression.
|
||||
*/
|
||||
DataFlow::Node getAnInput() { result = range.getAnInput() }
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapEscape */
|
||||
@@ -198,25 +182,21 @@ deprecated module LDAPBind = LdapBind;
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `LDAPBind::Range` instead.
|
||||
*/
|
||||
class LdapBind extends DataFlow::Node {
|
||||
LdapBind::Range range;
|
||||
|
||||
LdapBind() { this = range }
|
||||
|
||||
class LdapBind extends DataFlow::Node instanceof LdapBind::Range {
|
||||
/**
|
||||
* Gets the argument containing the binding host.
|
||||
*/
|
||||
DataFlow::Node getHost() { result = range.getHost() }
|
||||
DataFlow::Node getHost() { result = super.getHost() }
|
||||
|
||||
/**
|
||||
* Gets the argument containing the binding expression.
|
||||
*/
|
||||
DataFlow::Node getPassword() { result = range.getPassword() }
|
||||
DataFlow::Node getPassword() { result = super.getPassword() }
|
||||
|
||||
/**
|
||||
* Holds if the binding process use SSL.
|
||||
*/
|
||||
predicate useSsl() { range.useSsl() }
|
||||
predicate useSsl() { super.useSsl() }
|
||||
|
||||
/** DEPRECATED: Alias for useSsl */
|
||||
deprecated predicate useSSL() { useSsl() }
|
||||
@@ -250,15 +230,11 @@ deprecated module SQLEscape = SqlEscape;
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `SQLEscape::Range` instead.
|
||||
*/
|
||||
class SqlEscape extends DataFlow::Node {
|
||||
SqlEscape::Range range;
|
||||
|
||||
SqlEscape() { this = range }
|
||||
|
||||
class SqlEscape extends DataFlow::Node instanceof SqlEscape::Range {
|
||||
/**
|
||||
* Gets the argument containing the raw SQL statement.
|
||||
*/
|
||||
DataFlow::Node getAnInput() { result = range.getAnInput() }
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SqlEscape */
|
||||
@@ -287,13 +263,9 @@ deprecated module NoSQLQuery = NoSqlQuery;
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `NoSQLQuery::Range` instead.
|
||||
*/
|
||||
class NoSqlQuery extends DataFlow::Node {
|
||||
NoSqlQuery::Range range;
|
||||
|
||||
NoSqlQuery() { this = range }
|
||||
|
||||
class NoSqlQuery extends DataFlow::Node instanceof NoSqlQuery::Range {
|
||||
/** Gets the argument that specifies the NoSql query to be executed. */
|
||||
DataFlow::Node getQuery() { result = range.getQuery() }
|
||||
DataFlow::Node getQuery() { result = super.getQuery() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NoSqlQuery */
|
||||
@@ -322,13 +294,9 @@ deprecated module NoSQLSanitizer = NoSqlSanitizer;
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `NoSQLSanitizer::Range` instead.
|
||||
*/
|
||||
class NoSqlSanitizer extends DataFlow::Node {
|
||||
NoSqlSanitizer::Range range;
|
||||
|
||||
NoSqlSanitizer() { this = range }
|
||||
|
||||
class NoSqlSanitizer extends DataFlow::Node instanceof NoSqlSanitizer::Range {
|
||||
/** Gets the argument that specifies the NoSql query to be sanitized. */
|
||||
DataFlow::Node getAnInput() { result = range.getAnInput() }
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NoSqlSanitizer */
|
||||
@@ -361,20 +329,16 @@ module HeaderDeclaration {
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `HeaderDeclaration::Range` instead.
|
||||
*/
|
||||
class HeaderDeclaration extends DataFlow::Node {
|
||||
HeaderDeclaration::Range range;
|
||||
|
||||
HeaderDeclaration() { this = range }
|
||||
|
||||
class HeaderDeclaration extends DataFlow::Node instanceof HeaderDeclaration::Range {
|
||||
/**
|
||||
* Gets the argument containing the header name.
|
||||
*/
|
||||
DataFlow::Node getNameArg() { result = range.getNameArg() }
|
||||
DataFlow::Node getNameArg() { result = super.getNameArg() }
|
||||
|
||||
/**
|
||||
* Gets the argument containing the header value.
|
||||
*/
|
||||
DataFlow::Node getValueArg() { result = range.getValueArg() }
|
||||
DataFlow::Node getValueArg() { result = super.getValueArg() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling Csv writer APIs. */
|
||||
@@ -399,15 +363,11 @@ module CsvWriter {
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CsvWriter::Range` instead.
|
||||
*/
|
||||
class CsvWriter extends DataFlow::Node {
|
||||
CsvWriter::Range range;
|
||||
|
||||
CsvWriter() { this = range }
|
||||
|
||||
class CsvWriter extends DataFlow::Node instanceof CsvWriter::Range {
|
||||
/**
|
||||
* Get the parameter value of the csv writer function.
|
||||
*/
|
||||
DataFlow::Node getAnInput() { result = range.getAnInput() }
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,6 @@ import experimental.semmle.python.Concepts
|
||||
*/
|
||||
class CookieHeader extends Cookie::Range instanceof HeaderDeclaration {
|
||||
CookieHeader() {
|
||||
this instanceof HeaderDeclaration and
|
||||
exists(StrConst str |
|
||||
str.getText() = "Set-Cookie" and
|
||||
DataFlow::exprNode(str)
|
||||
|
||||
87
python/ql/src/meta/alerts/TaintSinks.ql
Normal file
87
python/ql/src/meta/alerts/TaintSinks.ql
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @name Taint sinks
|
||||
* @description Sinks from TaintTracking queries.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id py/meta/alerts/taint-sinks
|
||||
* @tags meta
|
||||
* @precision very-low
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import meta.MetaMetrics
|
||||
import semmle.python.security.dataflow.CleartextLoggingCustomizations
|
||||
import semmle.python.security.dataflow.CleartextStorageCustomizations
|
||||
import semmle.python.security.dataflow.CodeInjectionCustomizations
|
||||
import semmle.python.security.dataflow.CommandInjectionCustomizations
|
||||
import semmle.python.security.dataflow.LdapInjectionCustomizations
|
||||
import semmle.python.security.dataflow.LogInjectionCustomizations
|
||||
import semmle.python.security.dataflow.PathInjectionCustomizations
|
||||
import semmle.python.security.dataflow.PolynomialReDoSCustomizations
|
||||
import semmle.python.security.dataflow.ReflectedXSSCustomizations
|
||||
import semmle.python.security.dataflow.RegexInjectionCustomizations
|
||||
import semmle.python.security.dataflow.ServerSideRequestForgeryCustomizations
|
||||
import semmle.python.security.dataflow.SqlInjectionCustomizations
|
||||
import semmle.python.security.dataflow.StackTraceExposureCustomizations
|
||||
import semmle.python.security.dataflow.TarSlipCustomizations
|
||||
import semmle.python.security.dataflow.UnsafeDeserializationCustomizations
|
||||
import semmle.python.security.dataflow.UrlRedirectCustomizations
|
||||
import semmle.python.security.dataflow.WeakSensitiveDataHashingCustomizations
|
||||
import semmle.python.security.dataflow.XmlBombCustomizations
|
||||
import semmle.python.security.dataflow.XpathInjectionCustomizations
|
||||
import semmle.python.security.dataflow.XxeCustomizations
|
||||
|
||||
DataFlow::Node relevantTaintSink(string kind) {
|
||||
not result.getLocation().getFile() instanceof IgnoredFile and
|
||||
(
|
||||
kind = "CleartextLogging" and result instanceof CleartextLogging::Sink
|
||||
or
|
||||
kind = "CleartextStorage" and result instanceof CleartextStorage::Sink
|
||||
or
|
||||
kind = "CodeInjection" and result instanceof CodeInjection::Sink
|
||||
or
|
||||
kind = "CommandInjection" and result instanceof CommandInjection::Sink
|
||||
or
|
||||
kind = "LdapInjection (DN)" and result instanceof LdapInjection::DnSink
|
||||
or
|
||||
kind = "LdapInjection (Filter)" and result instanceof LdapInjection::FilterSink
|
||||
or
|
||||
kind = "LogInjection" and result instanceof LogInjection::Sink
|
||||
or
|
||||
kind = "PathInjection" and result instanceof PathInjection::Sink
|
||||
or
|
||||
kind = "PolynomialReDoS" and result instanceof PolynomialReDoS::Sink
|
||||
or
|
||||
kind = "ReflectedXss" and result instanceof ReflectedXss::Sink
|
||||
or
|
||||
kind = "RegexInjection" and result instanceof RegexInjection::Sink
|
||||
or
|
||||
kind = "ServerSideRequestForgery" and result instanceof ServerSideRequestForgery::Sink
|
||||
or
|
||||
kind = "SqlInjection" and result instanceof SqlInjection::Sink
|
||||
or
|
||||
kind = "StackTraceExposure" and result instanceof StackTraceExposure::Sink
|
||||
or
|
||||
kind = "TarSlip" and result instanceof TarSlip::Sink
|
||||
or
|
||||
kind = "UnsafeDeserialization" and result instanceof UnsafeDeserialization::Sink
|
||||
or
|
||||
kind = "UrlRedirect" and result instanceof UrlRedirect::Sink
|
||||
or
|
||||
kind = "WeakSensitiveDataHashing (NormalHashFunction)" and
|
||||
result instanceof NormalHashFunction::Sink
|
||||
or
|
||||
kind = "WeakSensitiveDataHashing (ComputationallyExpensiveHashFunction)" and
|
||||
result instanceof ComputationallyExpensiveHashFunction::Sink
|
||||
or
|
||||
kind = "XmlBomb" and result instanceof XmlBomb::Sink
|
||||
or
|
||||
kind = "XpathInjection" and result instanceof XpathInjection::Sink
|
||||
or
|
||||
kind = "Xxe" and result instanceof Xxe::Sink
|
||||
)
|
||||
}
|
||||
|
||||
from string kind
|
||||
select relevantTaintSink(kind), kind + " sink"
|
||||
@@ -10,7 +10,11 @@
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||
import meta.MetaMetrics
|
||||
|
||||
from DataFlowCall c, DataFlowCallableValue f
|
||||
where c.getCallable() = f
|
||||
where
|
||||
c.getCallable() = f and
|
||||
not c.getLocation().getFile() instanceof IgnoredFile and
|
||||
not f.getScope().getLocation().getFile() instanceof IgnoredFile
|
||||
select c, "Call to $@", f.getScope(), f.toString()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.5.4-dev
|
||||
version: 0.6.0-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
@@ -16,8 +16,9 @@ class DataFlowTest extends FlowTest {
|
||||
query predicate missingAnnotationOnSink(Location location, string error, string element) {
|
||||
error = "ERROR, you should add `# $ MISSING: flow` annotation" and
|
||||
exists(DataFlow::Node sink |
|
||||
any(TestConfiguration config).isSink(sink) and
|
||||
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
|
||||
call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
|
||||
(sink = call.getArg(_) or sink = call.getArgByName(_))
|
||||
) and
|
||||
|
||||
@@ -6,6 +6,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
@@ -17,3 +19,6 @@ reverseRead
|
||||
argHasPostUpdate
|
||||
postWithInFlow
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
|
||||
@@ -6,6 +6,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
@@ -17,3 +19,6 @@ reverseRead
|
||||
argHasPostUpdate
|
||||
postWithInFlow
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
|
||||
@@ -6,6 +6,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
@@ -17,3 +19,6 @@ reverseRead
|
||||
argHasPostUpdate
|
||||
postWithInFlow
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
| classes.py:14:17:14:60 | ControlFlowNode for Attribute() | classes.py:14:17:14:60 | ControlFlowNode for Attribute() |
|
||||
| classes.py:45:16:45:35 | ControlFlowNode for Attribute() | classes.py:45:16:45:35 | ControlFlowNode for Attribute() |
|
||||
| classes.py:60:17:60:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:54:18:54:21 | ControlFlowNode for self |
|
||||
| classes.py:242:9:242:24 | ControlFlowNode for set() | classes.py:242:9:242:24 | ControlFlowNode for set() |
|
||||
| classes.py:247:9:247:30 | ControlFlowNode for frozenset() | classes.py:247:9:247:30 | ControlFlowNode for frozenset() |
|
||||
| classes.py:252:9:252:28 | ControlFlowNode for dict() | classes.py:252:9:252:28 | ControlFlowNode for dict() |
|
||||
| classes.py:559:16:559:17 | ControlFlowNode for Str | classes.py:565:5:565:22 | ControlFlowNode for Subscript |
|
||||
| classes.py:565:5:565:16 | ControlFlowNode for with_getitem | classes.py:555:21:555:24 | ControlFlowNode for self |
|
||||
| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:555:27:555:29 | ControlFlowNode for key |
|
||||
| classes.py:581:5:581:16 | ControlFlowNode for with_setitem | classes.py:570:21:570:24 | ControlFlowNode for self |
|
||||
| classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:570:27:570:29 | ControlFlowNode for key |
|
||||
| classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:570:32:570:36 | ControlFlowNode for value |
|
||||
| classes.py:595:9:595:20 | ControlFlowNode for with_delitem | classes.py:586:21:586:24 | ControlFlowNode for self |
|
||||
| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:586:27:586:29 | ControlFlowNode for key |
|
||||
| classes.py:618:16:618:28 | ControlFlowNode for Attribute() | classes.py:618:16:618:28 | ControlFlowNode for Attribute() |
|
||||
| classes.py:659:15:659:18 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:661:16:661:19 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:657:17:657:20 | ControlFlowNode for self |
|
||||
| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:667:16:667:19 | ControlFlowNode for arg2 | classes.py:657:23:657:27 | ControlFlowNode for other |
|
||||
| classes.py:674:15:674:18 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:676:16:676:19 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:672:17:672:20 | ControlFlowNode for self |
|
||||
| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:682:16:682:19 | ControlFlowNode for arg2 | classes.py:672:23:672:27 | ControlFlowNode for other |
|
||||
| classes.py:689:15:689:18 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:691:16:691:19 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:687:17:687:20 | ControlFlowNode for self |
|
||||
| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:697:16:697:19 | ControlFlowNode for arg2 | classes.py:687:23:687:27 | ControlFlowNode for other |
|
||||
| classes.py:704:15:704:18 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:706:16:706:19 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:702:20:702:23 | ControlFlowNode for self |
|
||||
| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:712:19:712:22 | ControlFlowNode for arg2 | classes.py:702:26:702:30 | ControlFlowNode for other |
|
||||
| classes.py:719:15:719:18 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:721:16:721:19 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:717:21:717:24 | ControlFlowNode for self |
|
||||
| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:727:20:727:23 | ControlFlowNode for arg2 | classes.py:717:27:717:31 | ControlFlowNode for other |
|
||||
| classes.py:734:15:734:18 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:736:16:736:19 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:732:22:732:25 | ControlFlowNode for self |
|
||||
| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:742:22:742:25 | ControlFlowNode for arg2 | classes.py:732:28:732:32 | ControlFlowNode for other |
|
||||
| classes.py:749:15:749:18 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:751:16:751:19 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:747:17:747:20 | ControlFlowNode for self |
|
||||
| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:757:16:757:19 | ControlFlowNode for arg2 | classes.py:747:23:747:27 | ControlFlowNode for other |
|
||||
| classes.py:779:15:779:18 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:781:16:781:19 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:777:17:777:20 | ControlFlowNode for self |
|
||||
| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:793:17:793:20 | ControlFlowNode for arg2 | classes.py:777:23:777:27 | ControlFlowNode for other |
|
||||
| classes.py:800:15:800:18 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:802:16:802:19 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:798:20:798:23 | ControlFlowNode for self |
|
||||
| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:808:20:808:23 | ControlFlowNode for arg2 | classes.py:798:26:798:30 | ControlFlowNode for other |
|
||||
| classes.py:815:15:815:18 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:817:16:817:19 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:813:20:813:23 | ControlFlowNode for self |
|
||||
| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:823:20:823:23 | ControlFlowNode for arg2 | classes.py:813:26:813:30 | ControlFlowNode for other |
|
||||
| classes.py:830:15:830:18 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:832:16:832:19 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:828:17:828:20 | ControlFlowNode for self |
|
||||
| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:838:16:838:19 | ControlFlowNode for arg2 | classes.py:828:23:828:27 | ControlFlowNode for other |
|
||||
| classes.py:845:15:845:18 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:847:16:847:19 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:843:17:843:20 | ControlFlowNode for self |
|
||||
| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:853:16:853:19 | ControlFlowNode for arg2 | classes.py:843:23:843:27 | ControlFlowNode for other |
|
||||
| classes.py:860:15:860:18 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:862:16:862:19 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:858:16:858:19 | ControlFlowNode for self |
|
||||
| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:868:15:868:18 | ControlFlowNode for arg2 | classes.py:858:22:858:26 | ControlFlowNode for other |
|
||||
@@ -1,37 +0,0 @@
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||
|
||||
/**
|
||||
* A configuration to find the call graph edges.
|
||||
*/
|
||||
class CallGraphConfig extends DataFlow::Configuration {
|
||||
CallGraphConfig() { this = "CallGraphConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
node instanceof DataFlowPrivate::ReturnNode
|
||||
or
|
||||
// These sources should allow for the non-standard call syntax
|
||||
node instanceof DataFlow::ArgumentNode
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node instanceof DataFlowPrivate::OutNode
|
||||
or
|
||||
node instanceof DataFlow::ParameterNode and
|
||||
// exclude parameters to the SINK-functions
|
||||
not exists(DataFlowPrivate::DataFlowCallable c |
|
||||
c.getParameter(_) = node.asCfgNode() and
|
||||
c.getName().matches("SINK_")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where
|
||||
source.getLocation().getFile().getBaseName() = "classes.py" and
|
||||
sink.getLocation().getFile().getBaseName() = "classes.py" and
|
||||
exists(CallGraphConfig cfg | cfg.hasFlow(source, sink))
|
||||
select source, sink
|
||||
// Ideally, we would just have 1-step paths either from argument to parameter
|
||||
// or from return to call. This gives a bit more, so should be rewritten.
|
||||
// We should also consider splitting this into two, one for each direction.
|
||||
@@ -6,6 +6,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
@@ -17,3 +19,6 @@ reverseRead
|
||||
argHasPostUpdate
|
||||
postWithInFlow
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
|
||||
@@ -1,970 +0,0 @@
|
||||
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] |
|
||||
| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] |
|
||||
| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute |
|
||||
| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] |
|
||||
| test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:43:9:43:12 | ControlFlowNode for Subscript |
|
||||
| test.py:43:9:43:12 | ControlFlowNode for Subscript | test.py:44:10:44:10 | ControlFlowNode for y |
|
||||
| test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x |
|
||||
| test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x |
|
||||
| test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x |
|
||||
| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x |
|
||||
| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x |
|
||||
| test.py:87:10:87:15 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x |
|
||||
| test.py:93:9:93:16 | ControlFlowNode for List [List element] | test.py:94:10:94:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:93:9:93:16 | ControlFlowNode for List [List element] |
|
||||
| test.py:94:10:94:10 | ControlFlowNode for x [List element] | test.py:94:10:94:13 | ControlFlowNode for Subscript |
|
||||
| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | test.py:104:10:104:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:104:10:104:10 | ControlFlowNode for x [List element] | test.py:104:10:104:13 | ControlFlowNode for Subscript |
|
||||
| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | test.py:109:10:109:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:108:10:108:10 | ControlFlowNode for y | test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:108:16:108:16 | SSA variable y | test.py:108:10:108:10 | ControlFlowNode for y |
|
||||
| test.py:108:21:108:28 | ControlFlowNode for List [List element] | test.py:108:16:108:16 | SSA variable y |
|
||||
| test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:108:21:108:28 | ControlFlowNode for List [List element] |
|
||||
| test.py:109:10:109:10 | ControlFlowNode for x [List element] | test.py:109:10:109:13 | ControlFlowNode for Subscript |
|
||||
| test.py:113:9:113:16 | ControlFlowNode for List [List element] | test.py:114:21:114:21 | ControlFlowNode for l [List element] |
|
||||
| test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:113:9:113:16 | ControlFlowNode for List [List element] |
|
||||
| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | test.py:115:10:115:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:114:10:114:10 | ControlFlowNode for y | test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:114:16:114:16 | SSA variable y | test.py:114:10:114:10 | ControlFlowNode for y |
|
||||
| test.py:114:21:114:21 | ControlFlowNode for l [List element] | test.py:114:16:114:16 | SSA variable y |
|
||||
| test.py:115:10:115:10 | ControlFlowNode for x [List element] | test.py:115:10:115:13 | ControlFlowNode for Subscript |
|
||||
| test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | test.py:126:10:126:10 | ControlFlowNode for x [Set element] |
|
||||
| test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:125:9:125:16 | ControlFlowNode for Set [Set element] |
|
||||
| test.py:126:10:126:10 | ControlFlowNode for x [Set element] | test.py:126:10:126:16 | ControlFlowNode for Attribute() |
|
||||
| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | test.py:131:10:131:10 | ControlFlowNode for x [Set element] |
|
||||
| test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] |
|
||||
| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | test.py:131:10:131:16 | ControlFlowNode for Attribute() |
|
||||
| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | test.py:136:10:136:10 | ControlFlowNode for x [Set element] |
|
||||
| test.py:135:10:135:10 | ControlFlowNode for y | test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] |
|
||||
| test.py:135:16:135:16 | SSA variable y | test.py:135:10:135:10 | ControlFlowNode for y |
|
||||
| test.py:135:21:135:28 | ControlFlowNode for List [List element] | test.py:135:16:135:16 | SSA variable y |
|
||||
| test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:135:21:135:28 | ControlFlowNode for List [List element] |
|
||||
| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | test.py:136:10:136:16 | ControlFlowNode for Attribute() |
|
||||
| test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | test.py:141:21:141:21 | ControlFlowNode for l [Set element] |
|
||||
| test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:140:9:140:16 | ControlFlowNode for Set [Set element] |
|
||||
| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | test.py:142:10:142:10 | ControlFlowNode for x [Set element] |
|
||||
| test.py:141:10:141:10 | ControlFlowNode for y | test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] |
|
||||
| test.py:141:16:141:16 | SSA variable y | test.py:141:10:141:10 | ControlFlowNode for y |
|
||||
| test.py:141:21:141:21 | ControlFlowNode for l [Set element] | test.py:141:16:141:16 | SSA variable y |
|
||||
| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | test.py:142:10:142:16 | ControlFlowNode for Attribute() |
|
||||
| test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] |
|
||||
| test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] |
|
||||
| test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:153:10:153:15 | ControlFlowNode for Subscript |
|
||||
| test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] |
|
||||
| test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] |
|
||||
| test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:158:10:158:19 | ControlFlowNode for Attribute() |
|
||||
| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | test.py:184:10:184:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:183:10:183:10 | ControlFlowNode for y | test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:183:16:183:16 | SSA variable z [List element] | test.py:183:41:183:41 | ControlFlowNode for z [List element] |
|
||||
| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | test.py:183:16:183:16 | SSA variable z [List element] |
|
||||
| test.py:183:22:183:29 | ControlFlowNode for List [List element] | test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:183:22:183:29 | ControlFlowNode for List [List element] |
|
||||
| test.py:183:36:183:36 | SSA variable y | test.py:183:10:183:10 | ControlFlowNode for y |
|
||||
| test.py:183:41:183:41 | ControlFlowNode for z [List element] | test.py:183:36:183:36 | SSA variable y |
|
||||
| test.py:184:10:184:10 | ControlFlowNode for x [List element] | test.py:184:10:184:13 | ControlFlowNode for Subscript |
|
||||
| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | test.py:189:10:189:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:188:10:188:10 | ControlFlowNode for y | test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] |
|
||||
| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:188:16:188:16 | SSA variable v [List element, List element, List element] |
|
||||
| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] |
|
||||
| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] |
|
||||
| test.py:188:24:188:31 | ControlFlowNode for List [List element] | test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:188:24:188:31 | ControlFlowNode for List [List element] |
|
||||
| test.py:188:40:188:40 | SSA variable u [List element, List element] | test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] |
|
||||
| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | test.py:188:40:188:40 | SSA variable u [List element, List element] |
|
||||
| test.py:188:51:188:51 | SSA variable z [List element] | test.py:188:67:188:67 | ControlFlowNode for z [List element] |
|
||||
| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | test.py:188:51:188:51 | SSA variable z [List element] |
|
||||
| test.py:188:62:188:62 | SSA variable y | test.py:188:10:188:10 | ControlFlowNode for y |
|
||||
| test.py:188:67:188:67 | ControlFlowNode for z [List element] | test.py:188:62:188:62 | SSA variable y |
|
||||
| test.py:189:10:189:10 | ControlFlowNode for x [List element] | test.py:189:10:189:13 | ControlFlowNode for Subscript |
|
||||
| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | test.py:200:10:200:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:199:10:199:10 | ControlFlowNode for y | test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:199:16:199:16 | SSA variable y | test.py:199:10:199:10 | ControlFlowNode for y |
|
||||
| test.py:199:22:199:22 | ControlFlowNode for z | test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] |
|
||||
| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | test.py:199:16:199:16 | SSA variable y |
|
||||
| test.py:199:28:199:28 | SSA variable z | test.py:199:22:199:22 | ControlFlowNode for z |
|
||||
| test.py:199:33:199:40 | ControlFlowNode for List [List element] | test.py:199:28:199:28 | SSA variable z |
|
||||
| test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:199:33:199:40 | ControlFlowNode for List [List element] |
|
||||
| test.py:200:10:200:10 | ControlFlowNode for x [List element] | test.py:200:10:200:13 | ControlFlowNode for Subscript |
|
||||
| test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | test.py:206:10:206:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:205:10:205:10 | ControlFlowNode for a | test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:205:17:205:17 | SSA variable a | test.py:205:10:205:10 | ControlFlowNode for a |
|
||||
| test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:205:17:205:17 | SSA variable a |
|
||||
| test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:205:28:205:33 | ControlFlowNode for SOURCE | test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:206:10:206:10 | ControlFlowNode for x [List element] | test.py:206:10:206:13 | ControlFlowNode for Subscript |
|
||||
| test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | test.py:211:10:211:10 | ControlFlowNode for x [List element] |
|
||||
| test.py:210:10:210:10 | ControlFlowNode for a [List element] | test.py:210:10:210:13 | ControlFlowNode for Subscript |
|
||||
| test.py:210:10:210:13 | ControlFlowNode for Subscript | test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:210:20:210:21 | IterableElement | test.py:210:20:210:21 | SSA variable a [List element] |
|
||||
| test.py:210:20:210:21 | SSA variable a [List element] | test.py:210:10:210:10 | ControlFlowNode for a [List element] |
|
||||
| test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:210:20:210:21 | IterableElement |
|
||||
| test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:210:32:210:37 | ControlFlowNode for SOURCE | test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:211:10:211:10 | ControlFlowNode for x [List element] | test.py:211:10:211:13 | ControlFlowNode for Subscript |
|
||||
| test.py:349:11:349:16 | ControlFlowNode for SOURCE | test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:349:10:349:21 | ControlFlowNode for Subscript |
|
||||
| test.py:353:10:353:17 | ControlFlowNode for List [List element] | test.py:353:10:353:20 | ControlFlowNode for Subscript |
|
||||
| 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:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr |
|
||||
| test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr |
|
||||
| test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp |
|
||||
| test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp |
|
||||
| test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x |
|
||||
| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x |
|
||||
| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() |
|
||||
| test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b |
|
||||
| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b |
|
||||
| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() |
|
||||
| test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b |
|
||||
| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b |
|
||||
| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() |
|
||||
| test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b |
|
||||
| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b |
|
||||
| test.py:512:10:512:43 | KwUnpacked b | test.py:512:10:512:43 | ControlFlowNode for second() |
|
||||
| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:512:10:512:43 | KwUnpacked b |
|
||||
| test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] |
|
||||
| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript |
|
||||
| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
|
||||
| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript |
|
||||
| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() |
|
||||
| test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
|
||||
| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a |
|
||||
| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b |
|
||||
| test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:547:5:547:5 | SSA variable a | test.py:548:10:548:10 | ControlFlowNode for a |
|
||||
| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:5 | SSA variable a |
|
||||
| test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
|
||||
| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
|
||||
| test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:555:5:555:5 | SSA variable a | test.py:556:10:556:10 | ControlFlowNode for a |
|
||||
| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:5 | SSA variable a |
|
||||
| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] |
|
||||
| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:555:12:555:12 | SSA variable c |
|
||||
| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:555:12:555:12 | SSA variable c | test.py:558:10:558:10 | ControlFlowNode for c |
|
||||
| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] |
|
||||
| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] |
|
||||
| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] |
|
||||
| test.py:563:12:563:19 | ControlFlowNode for List [List element] | test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:563:12:563:19 | ControlFlowNode for List [List element] |
|
||||
| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | test.py:564:6:564:10 | IterableSequence [List element, List element] |
|
||||
| test.py:564:5:564:11 | IterableElement [List element, List element] | test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] |
|
||||
| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | test.py:564:5:564:11 | IterableElement [List element, List element] |
|
||||
| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | test.py:564:5:564:11 | IterableSequence [List element, List element, List element] |
|
||||
| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] |
|
||||
| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableElement [List element, List element, List element] |
|
||||
| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:564:7:564:9 | IterableSequence [List element] |
|
||||
| test.py:564:6:564:10 | IterableElement [List element] | test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] |
|
||||
| test.py:564:6:564:10 | IterableSequence [List element, List element] | test.py:564:6:564:10 | IterableElement [List element] |
|
||||
| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | test.py:564:8:564:8 | SSA variable a |
|
||||
| test.py:564:7:564:9 | IterableElement | test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] |
|
||||
| test.py:564:7:564:9 | IterableSequence [List element] | test.py:564:7:564:9 | IterableElement |
|
||||
| test.py:564:8:564:8 | SSA variable a | test.py:565:10:565:10 | ControlFlowNode for a |
|
||||
| test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:572:5:572:5 | SSA variable a | test.py:573:10:573:10 | ControlFlowNode for a |
|
||||
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:5 | SSA variable a |
|
||||
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:8:572:9 | IterableElement |
|
||||
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:12:572:12 | SSA variable c |
|
||||
| test.py:572:8:572:9 | IterableElement | test.py:572:8:572:9 | SSA variable b [List element] |
|
||||
| test.py:572:8:572:9 | SSA variable b [List element] | test.py:575:10:575:10 | ControlFlowNode for b [List element] |
|
||||
| test.py:572:12:572:12 | SSA variable c | test.py:576:12:576:12 | ControlFlowNode for c |
|
||||
| test.py:575:10:575:10 | ControlFlowNode for b [List element] | test.py:575:10:575:13 | ControlFlowNode for Subscript |
|
||||
| test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:582:5:582:5 | SSA variable a | test.py:583:10:583:10 | ControlFlowNode for a |
|
||||
| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:5 | SSA variable a |
|
||||
| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:12:582:12 | SSA variable c |
|
||||
| test.py:582:12:582:12 | SSA variable c | test.py:585:10:585:10 | ControlFlowNode for c |
|
||||
| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:593:6:593:23 | IterableSequence [List element, List element] |
|
||||
| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:601:5:601:24 | IterableSequence [List element, List element] |
|
||||
| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:609:6:609:23 | IterableSequence [List element, List element] |
|
||||
| test.py:590:11:590:37 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] |
|
||||
| test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] |
|
||||
| test.py:590:40:590:47 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:590:40:590:47 | ControlFlowNode for List [List element] |
|
||||
| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:593:7:593:16 | IterableSequence [List element] |
|
||||
| test.py:593:6:593:23 | IterableElement [List element] | test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] |
|
||||
| test.py:593:6:593:23 | IterableSequence [List element, List element] | test.py:593:6:593:23 | IterableElement [List element] |
|
||||
| test.py:593:7:593:8 | SSA variable a1 | test.py:594:10:594:11 | ControlFlowNode for a1 |
|
||||
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:593:7:593:8 | SSA variable a1 |
|
||||
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:593:11:593:12 | SSA variable a2 |
|
||||
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:593:15:593:16 | SSA variable a3 |
|
||||
| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:593:7:593:16 | IterableSequence [List element] | test.py:593:7:593:16 | IterableElement |
|
||||
| test.py:593:11:593:12 | SSA variable a2 | test.py:595:12:595:13 | ControlFlowNode for a2 |
|
||||
| test.py:593:15:593:16 | SSA variable a3 | test.py:596:10:596:11 | ControlFlowNode for a3 |
|
||||
| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:601:7:601:16 | IterableSequence [List element] |
|
||||
| test.py:601:5:601:24 | IterableElement [List element] | test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] |
|
||||
| test.py:601:5:601:24 | IterableSequence [List element, List element] | test.py:601:5:601:24 | IterableElement [List element] |
|
||||
| test.py:601:7:601:8 | SSA variable a1 | test.py:602:10:602:11 | ControlFlowNode for a1 |
|
||||
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:601:7:601:8 | SSA variable a1 |
|
||||
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:601:11:601:12 | SSA variable a2 |
|
||||
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:601:15:601:16 | SSA variable a3 |
|
||||
| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:601:7:601:16 | IterableSequence [List element] | test.py:601:7:601:16 | IterableElement |
|
||||
| test.py:601:11:601:12 | SSA variable a2 | test.py:603:12:603:13 | ControlFlowNode for a2 |
|
||||
| test.py:601:15:601:16 | SSA variable a3 | test.py:604:10:604:11 | ControlFlowNode for a3 |
|
||||
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | test.py:609:7:609:8 | SSA variable a1 |
|
||||
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | test.py:609:11:609:12 | SSA variable a2 |
|
||||
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | test.py:609:15:609:16 | SSA variable a3 |
|
||||
| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] |
|
||||
| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] |
|
||||
| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] |
|
||||
| test.py:609:6:609:17 | IterableSequence [List element] | test.py:609:6:609:17 | IterableElement |
|
||||
| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:609:6:609:17 | IterableSequence [List element] |
|
||||
| test.py:609:6:609:23 | IterableElement [List element] | test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] |
|
||||
| test.py:609:6:609:23 | IterableSequence [List element, List element] | test.py:609:6:609:23 | IterableElement [List element] |
|
||||
| test.py:609:7:609:8 | SSA variable a1 | test.py:610:10:610:11 | ControlFlowNode for a1 |
|
||||
| test.py:609:11:609:12 | SSA variable a2 | test.py:611:12:611:13 | ControlFlowNode for a2 |
|
||||
| test.py:609:15:609:16 | SSA variable a3 | test.py:612:10:612:11 | ControlFlowNode for a3 |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] |
|
||||
| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:621:7:621:8 | SSA variable a1 |
|
||||
| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:621:11:621:13 | IterableElement |
|
||||
| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] |
|
||||
| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] |
|
||||
| test.py:621:7:621:8 | SSA variable a1 | test.py:622:10:622:11 | ControlFlowNode for a1 |
|
||||
| test.py:621:11:621:13 | IterableElement | test.py:621:11:621:13 | SSA variable a2 [List element] |
|
||||
| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:624:12:624:13 | ControlFlowNode for a2 [List element] |
|
||||
| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:625:10:625:11 | ControlFlowNode for a2 [List element] |
|
||||
| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | test.py:624:12:624:16 | ControlFlowNode for Subscript |
|
||||
| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | test.py:625:10:625:14 | ControlFlowNode for Subscript |
|
||||
| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] |
|
||||
| test.py:630:7:630:8 | SSA variable a1 | test.py:631:10:631:11 | ControlFlowNode for a1 |
|
||||
| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:630:7:630:8 | SSA variable a1 |
|
||||
| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:630:11:630:13 | IterableElement |
|
||||
| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:630:11:630:13 | IterableElement | test.py:630:11:630:13 | SSA variable a2 [List element] |
|
||||
| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:633:12:633:13 | ControlFlowNode for a2 [List element] |
|
||||
| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:634:10:634:11 | ControlFlowNode for a2 [List element] |
|
||||
| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | test.py:633:12:633:16 | ControlFlowNode for Subscript |
|
||||
| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | test.py:634:10:634:14 | ControlFlowNode for Subscript |
|
||||
| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] |
|
||||
| test.py:639:7:639:8 | SSA variable a1 | test.py:640:10:640:11 | ControlFlowNode for a1 |
|
||||
| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:639:7:639:8 | SSA variable a1 |
|
||||
| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:639:11:639:13 | IterableElement |
|
||||
| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:639:11:639:13 | IterableElement | test.py:639:11:639:13 | SSA variable a2 [List element] |
|
||||
| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:642:12:642:13 | ControlFlowNode for a2 [List element] |
|
||||
| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:643:10:643:11 | ControlFlowNode for a2 [List element] |
|
||||
| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | test.py:642:12:642:16 | ControlFlowNode for Subscript |
|
||||
| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | test.py:643:10:643:14 | ControlFlowNode for Subscript |
|
||||
| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:648:7:648:8 | SSA variable a1 |
|
||||
| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:648:11:648:13 | IterableElement |
|
||||
| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] |
|
||||
| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] |
|
||||
| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] |
|
||||
| test.py:648:7:648:8 | SSA variable a1 | test.py:649:10:649:11 | ControlFlowNode for a1 |
|
||||
| test.py:648:11:648:13 | IterableElement | test.py:648:11:648:13 | SSA variable a2 [List element] |
|
||||
| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:651:12:651:13 | ControlFlowNode for a2 [List element] |
|
||||
| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:652:10:652:11 | ControlFlowNode for a2 [List element] |
|
||||
| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | test.py:651:12:651:16 | ControlFlowNode for Subscript |
|
||||
| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | test.py:652:10:652:14 | ControlFlowNode for Subscript |
|
||||
| test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a |
|
||||
| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] |
|
||||
| test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:668:9:668:9 | SSA variable x | test.py:669:14:669:14 | ControlFlowNode for x |
|
||||
| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:668:9:668:9 | SSA variable x |
|
||||
| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] |
|
||||
| test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:676:9:676:10 | IterableElement | test.py:676:9:676:10 | SSA variable x [List element] |
|
||||
| test.py:676:9:676:10 | SSA variable x [List element] | test.py:678:14:678:14 | ControlFlowNode for x [List element] |
|
||||
| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:9:676:10 | IterableElement |
|
||||
| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:12:676:12 | SSA variable y |
|
||||
| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:676:12:676:12 | SSA variable y | test.py:679:16:679:16 | ControlFlowNode for y |
|
||||
| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:678:14:678:14 | ControlFlowNode for x [List element] | test.py:678:14:678:17 | ControlFlowNode for Subscript |
|
||||
| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] |
|
||||
| test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:685:9:685:9 | SSA variable x | test.py:686:14:686:14 | ControlFlowNode for x |
|
||||
| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:685:9:685:9 | SSA variable x |
|
||||
| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] |
|
||||
| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] |
|
||||
| test.py:691:7:691:9 | SSA variable arg | test.py:692:10:692:12 | ControlFlowNode for arg |
|
||||
| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:7:691:9 | SSA variable arg |
|
||||
| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:7:691:9 | SSA variable arg |
|
||||
| test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:698:51:698:51 | ControlFlowNode for s |
|
||||
| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] |
|
||||
| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] |
|
||||
| test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] |
|
||||
| test.py:698:51:698:51 | ControlFlowNode for s | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] |
|
||||
| test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() |
|
||||
| test.py:807:35:807:35 | ControlFlowNode for x | test.py:808:10:808:10 | ControlFlowNode for x |
|
||||
| test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:807:35:807:35 | ControlFlowNode for x |
|
||||
| test.py:807:48:807:48 | ControlFlowNode for y | test.py:809:10:809:10 | ControlFlowNode for y |
|
||||
| test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:807:48:807:48 | ControlFlowNode for y |
|
||||
| test.py:807:61:807:61 | ControlFlowNode for z | test.py:810:10:810:10 | ControlFlowNode for z |
|
||||
| test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:807:61:807: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() |
|
||||
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | semmle.label | [post store] ControlFlowNode for self [Attribute b] |
|
||||
| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] |
|
||||
| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] |
|
||||
| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:42:21:42:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] |
|
||||
| test.py:43:9:43:12 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:44:10:44:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:55:9:55:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:56:10:56:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:61:9:61:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
|
||||
| test.py:62:10:62:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:66:9:66:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
|
||||
| test.py:67:10:67:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral |
|
||||
| test.py:72:10:72:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral |
|
||||
| test.py:77:10:77:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:87:10:87:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:88:10:88:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:93:9:93:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:93:10:93:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:94:10:94:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:94:10:94:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:103:10:103:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:104:10:104:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:104:10:104:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:108:10:108:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:108:16:108:16 | SSA variable y | semmle.label | SSA variable y |
|
||||
| test.py:108:21:108:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:108:22:108:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:109:10:109:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:109:10:109:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:113:9:113:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:113:10:113:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:114:10:114:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:114:16:114:16 | SSA variable y | semmle.label | SSA variable y |
|
||||
| test.py:114:21:114:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] |
|
||||
| test.py:115:10:115:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:115:10:115:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | semmle.label | ControlFlowNode for Set [Set element] |
|
||||
| test.py:125:10:125:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:126:10:126:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
|
||||
| test.py:126:10:126:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
|
||||
| test.py:130:10:130:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
|
||||
| test.py:131:10:131:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
|
||||
| test.py:135:10:135:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:135:16:135:16 | SSA variable y | semmle.label | SSA variable y |
|
||||
| test.py:135:21:135:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:135:22:135:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
|
||||
| test.py:136:10:136:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | semmle.label | ControlFlowNode for Set [Set element] |
|
||||
| test.py:140:10:140:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
|
||||
| test.py:141:10:141:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:141:16:141:16 | SSA variable y | semmle.label | SSA variable y |
|
||||
| test.py:141:21:141:21 | ControlFlowNode for l [Set element] | semmle.label | ControlFlowNode for l [Set element] |
|
||||
| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
|
||||
| test.py:142:10:142:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
|
||||
| test.py:152:15:152:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] |
|
||||
| test.py:153:10:153:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
|
||||
| test.py:157:15:157:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] |
|
||||
| test.py:158:10:158:19 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:183:10:183:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:183:16:183:16 | SSA variable z [List element] | semmle.label | SSA variable z [List element] |
|
||||
| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:183:22:183:29 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:183:23:183:28 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:183:36:183:36 | SSA variable y | semmle.label | SSA variable y |
|
||||
| test.py:183:41:183:41 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] |
|
||||
| test.py:184:10:184:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:184:10:184:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:188:10:188:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | semmle.label | SSA variable v [List element, List element, List element] |
|
||||
| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] |
|
||||
| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] |
|
||||
| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:188:24:188:31 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:188:25:188:30 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:188:40:188:40 | SSA variable u [List element, List element] | semmle.label | SSA variable u [List element, List element] |
|
||||
| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | semmle.label | ControlFlowNode for v [List element, List element, List element] |
|
||||
| test.py:188:51:188:51 | SSA variable z [List element] | semmle.label | SSA variable z [List element] |
|
||||
| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | semmle.label | ControlFlowNode for u [List element, List element] |
|
||||
| test.py:188:62:188:62 | SSA variable y | semmle.label | SSA variable y |
|
||||
| test.py:188:67:188:67 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] |
|
||||
| test.py:189:10:189:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:189:10:189:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:199:10:199:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:199:16:199:16 | SSA variable y | semmle.label | SSA variable y |
|
||||
| test.py:199:22:199:22 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
|
||||
| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | semmle.label | ControlFlowNode for GeneratorExp [List element] |
|
||||
| test.py:199:28:199:28 | SSA variable z | semmle.label | SSA variable z |
|
||||
| test.py:199:33:199:40 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:199:34:199:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:200:10:200:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:200:10:200:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:205:10:205:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:205:17:205:17 | SSA variable a | semmle.label | SSA variable a |
|
||||
| test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:205:28:205:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:206:10:206:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:206:10:206:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
|
||||
| test.py:210:10:210:10 | ControlFlowNode for a [List element] | semmle.label | ControlFlowNode for a [List element] |
|
||||
| test.py:210:10:210:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:210:20:210:21 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:210:20:210:21 | SSA variable a [List element] | semmle.label | SSA variable a [List element] |
|
||||
| test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:210:32:210:37 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:211:10:211:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:211:10:211:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:349:10:349:21 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:349:11:349:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:353:10:353:17 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:353:10:353:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:353:11:353:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| 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() |
|
||||
| test.py:388:30:388:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:396:10:396:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
| 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 |
|
||||
| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr |
|
||||
| test.py:429:15:429:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr |
|
||||
| test.py:434:16:434:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:445:10:445:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:445:10:445:38 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp |
|
||||
| test.py:453:10:453:39 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp |
|
||||
| test.py:453:34:453:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:474:11:474:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:475:16:475:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:477:10:477:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
|
||||
| test.py:477:12:477:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:481:19:481:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:482:16:482:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:484:10:484:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
| test.py:484:28:484:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:495:19:495:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:496:16:496:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:498:10:498:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
| test.py:498:30:498:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:509:19:509:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:510:16:510:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:512:10:512:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
|
||||
| test.py:512:10:512:43 | KwUnpacked b | semmle.label | KwUnpacked b |
|
||||
| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
|
||||
| test.py:512:36:512:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
|
||||
| test.py:516:33:516:36 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:517:10:517: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:517:33:517:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
|
||||
| test.py:521:38:521:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
|
||||
| test.py:522:10:522: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:522:39:522:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:534:9:534:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:536:10:536:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:541:10:541:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
|
||||
| test.py:546:10:546:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:547:5:547:5 | SSA variable a | semmle.label | SSA variable a |
|
||||
| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:548:10:548:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:554:10:554:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
|
||||
| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:554:30:554:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:555:5:555:5 | SSA variable a | semmle.label | SSA variable a |
|
||||
| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
|
||||
| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | semmle.label | IterableSequence [Tuple element at index 1] |
|
||||
| test.py:555:12:555:12 | SSA variable c | semmle.label | SSA variable c |
|
||||
| test.py:556:10:556:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:558:10:558:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
|
||||
| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] |
|
||||
| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] |
|
||||
| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:563:12:563:19 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:563:13:563:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element, List element] |
|
||||
| test.py:564:5:564:11 | IterableElement [List element, List element] | semmle.label | IterableElement [List element, List element] |
|
||||
| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element] |
|
||||
| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] |
|
||||
| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | semmle.label | IterableElement [List element, List element, List element] |
|
||||
| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element, List element] |
|
||||
| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] |
|
||||
| test.py:564:6:564:10 | IterableElement [List element] | semmle.label | IterableElement [List element] |
|
||||
| test.py:564:6:564:10 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] |
|
||||
| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] |
|
||||
| test.py:564:7:564:9 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:564:7:564:9 | IterableSequence [List element] | semmle.label | IterableSequence [List element] |
|
||||
| test.py:564:8:564:8 | SSA variable a | semmle.label | SSA variable a |
|
||||
| test.py:565:10:565:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:571:10:571:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:571:18:571:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:572:5:572:5 | SSA variable a | semmle.label | SSA variable a |
|
||||
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:572:8:572:9 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:572:8:572:9 | SSA variable b [List element] | semmle.label | SSA variable b [List element] |
|
||||
| test.py:572:12:572:12 | SSA variable c | semmle.label | SSA variable c |
|
||||
| test.py:573:10:573:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:575:10:575:10 | ControlFlowNode for b [List element] | semmle.label | ControlFlowNode for b [List element] |
|
||||
| test.py:575:10:575:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:576:12:576:12 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
|
||||
| test.py:581:10:581:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:581:18:581:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:582:5:582:5 | SSA variable a | semmle.label | SSA variable a |
|
||||
| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:582:12:582:12 | SSA variable c | semmle.label | SSA variable c |
|
||||
| test.py:583:10:583:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:585:10:585:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
|
||||
| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] |
|
||||
| test.py:590:11:590:37 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:590:12:590:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:590:31:590:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:590:40:590:47 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| test.py:590:41:590:46 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] |
|
||||
| test.py:593:6:593:23 | IterableElement [List element] | semmle.label | IterableElement [List element] |
|
||||
| test.py:593:6:593:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] |
|
||||
| test.py:593:7:593:8 | SSA variable a1 | semmle.label | SSA variable a1 |
|
||||
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:593:7:593:16 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:593:7:593:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] |
|
||||
| test.py:593:11:593:12 | SSA variable a2 | semmle.label | SSA variable a2 |
|
||||
| test.py:593:15:593:16 | SSA variable a3 | semmle.label | SSA variable a3 |
|
||||
| test.py:594:10:594:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
|
||||
| test.py:595:12:595:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 |
|
||||
| test.py:596:10:596:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 |
|
||||
| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] |
|
||||
| test.py:601:5:601:24 | IterableElement [List element] | semmle.label | IterableElement [List element] |
|
||||
| test.py:601:5:601:24 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] |
|
||||
| test.py:601:7:601:8 | SSA variable a1 | semmle.label | SSA variable a1 |
|
||||
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
|
||||
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:601:7:601:16 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:601:7:601:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] |
|
||||
| test.py:601:11:601:12 | SSA variable a2 | semmle.label | SSA variable a2 |
|
||||
| test.py:601:15:601:16 | SSA variable a3 | semmle.label | SSA variable a3 |
|
||||
| test.py:602:10:602:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
|
||||
| test.py:603:12:603:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 |
|
||||
| test.py:604:10:604:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 |
|
||||
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] |
|
||||
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | semmle.label | ControlFlowNode for List [Tuple element at index 1] |
|
||||
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] |
|
||||
| test.py:609:6:609:17 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:609:6:609:17 | IterableSequence [List element] | semmle.label | IterableSequence [List element] |
|
||||
| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] |
|
||||
| test.py:609:6:609:23 | IterableElement [List element] | semmle.label | IterableElement [List element] |
|
||||
| test.py:609:6:609:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] |
|
||||
| test.py:609:7:609:8 | SSA variable a1 | semmle.label | SSA variable a1 |
|
||||
| test.py:609:11:609:12 | SSA variable a2 | semmle.label | SSA variable a2 |
|
||||
| test.py:609:15:609:16 | SSA variable a3 | semmle.label | SSA variable a3 |
|
||||
| test.py:610:10:610:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
|
||||
| test.py:611:12:611:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 |
|
||||
| test.py:612:10:612:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:618:12:618:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:618:31:618:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] |
|
||||
| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] |
|
||||
| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] |
|
||||
| test.py:621:7:621:8 | SSA variable a1 | semmle.label | SSA variable a1 |
|
||||
| test.py:621:11:621:13 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:621:11:621:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] |
|
||||
| test.py:622:10:622:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
|
||||
| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
|
||||
| test.py:624:12:624:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
|
||||
| test.py:625:10:625:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:630:7:630:8 | SSA variable a1 | semmle.label | SSA variable a1 |
|
||||
| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] |
|
||||
| test.py:630:11:630:13 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:630:11:630:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] |
|
||||
| test.py:631:10:631:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
|
||||
| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
|
||||
| test.py:633:12:633:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
|
||||
| test.py:634:10:634:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:639:7:639:8 | SSA variable a1 | semmle.label | SSA variable a1 |
|
||||
| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
|
||||
| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] |
|
||||
| test.py:639:11:639:13 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:639:11:639:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] |
|
||||
| test.py:640:10:640:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
|
||||
| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
|
||||
| test.py:642:12:642:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
|
||||
| test.py:643:10:643:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] |
|
||||
| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] |
|
||||
| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] |
|
||||
| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
|
||||
| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
|
||||
| test.py:648:7:648:8 | SSA variable a1 | semmle.label | SSA variable a1 |
|
||||
| test.py:648:11:648:13 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:648:11:648:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] |
|
||||
| test.py:649:10:649:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
|
||||
| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
|
||||
| test.py:651:12:651:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
|
||||
| test.py:652:10:652:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:659:19:659:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:660:10:660:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
|
||||
| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:667:12:667:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:667:33:667:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:668:9:668:9 | SSA variable x | semmle.label | SSA variable x |
|
||||
| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] |
|
||||
| test.py:669:14:669:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:675:12:675:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:675:33:675:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:676:9:676:10 | IterableElement | semmle.label | IterableElement |
|
||||
| test.py:676:9:676:10 | SSA variable x [List element] | semmle.label | SSA variable x [List element] |
|
||||
| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:676:12:676:12 | SSA variable y | semmle.label | SSA variable y |
|
||||
| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] |
|
||||
| test.py:678:14:678:14 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
|
||||
| test.py:678:14:678:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| test.py:679:16:679:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
|
||||
| test.py:684:12:684:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:684:33:684:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:685:9:685:9 | SSA variable x | semmle.label | SSA variable x |
|
||||
| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
|
||||
| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
|
||||
| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] |
|
||||
| test.py:686:14:686:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] |
|
||||
| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] |
|
||||
| test.py:691:7:691:9 | SSA variable arg | semmle.label | SSA variable arg |
|
||||
| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] |
|
||||
| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] |
|
||||
| test.py:692:10:692:12 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg |
|
||||
| test.py:697:7:697:12 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 0] |
|
||||
| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 1] |
|
||||
| test.py:698:43:698:48 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:698:51:698:51 | ControlFlowNode for s | semmle.label | ControlFlowNode for s |
|
||||
| test.py:769:16:769:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | semmle.label | ControlFlowNode for return_from_inner_scope() |
|
||||
| test.py:807:35:807:35 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:807:37:807:42 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:807:48:807:48 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:807:50:807:55 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:807:61:807:61 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
|
||||
| test.py:807:63:807:68 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| test.py:808:10:808:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| test.py:809:10:809:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| test.py:810:10:810: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:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x | test.py:477:10:477:18 | ControlFlowNode for f() |
|
||||
| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b | test.py:484:10:484:34 | ControlFlowNode for second() |
|
||||
| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b | test.py:498:10:498:36 | ControlFlowNode for second() |
|
||||
| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b | test.py:512:10:512:43 | ControlFlowNode for second() |
|
||||
| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() |
|
||||
| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript | test.py:522:10:522: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 |
|
||||
| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found |
|
||||
| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found |
|
||||
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found |
|
||||
| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | Flow found |
|
||||
| test.py:44:10:44:10 | ControlFlowNode for y | test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for y | Flow found |
|
||||
| test.py:56:10:56:10 | ControlFlowNode for x | test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | Flow found |
|
||||
| test.py:62:10:62:10 | ControlFlowNode for x | test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | Flow found |
|
||||
| test.py:67:10:67:10 | ControlFlowNode for x | test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x | Flow found |
|
||||
| test.py:72:10:72:10 | ControlFlowNode for x | test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x | Flow found |
|
||||
| test.py:77:10:77:10 | ControlFlowNode for x | test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x | Flow found |
|
||||
| test.py:88:10:88:10 | ControlFlowNode for x | test.py:87:10:87:15 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x | Flow found |
|
||||
| test.py:94:10:94:13 | ControlFlowNode for Subscript | test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:94:10:94:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:104:10:104:13 | ControlFlowNode for Subscript | test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:104:10:104:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:109:10:109:13 | ControlFlowNode for Subscript | test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:109:10:109:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:115:10:115:13 | ControlFlowNode for Subscript | test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:115:10:115:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:126:10:126:16 | ControlFlowNode for Attribute() | test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:126:10:126:16 | ControlFlowNode for Attribute() | Flow found |
|
||||
| test.py:131:10:131:16 | ControlFlowNode for Attribute() | test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:131:10:131:16 | ControlFlowNode for Attribute() | Flow found |
|
||||
| test.py:136:10:136:16 | ControlFlowNode for Attribute() | test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:136:10:136:16 | ControlFlowNode for Attribute() | Flow found |
|
||||
| test.py:142:10:142:16 | ControlFlowNode for Attribute() | test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:142:10:142:16 | ControlFlowNode for Attribute() | Flow found |
|
||||
| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found |
|
||||
| test.py:184:10:184:13 | ControlFlowNode for Subscript | test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:184:10:184:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:189:10:189:13 | ControlFlowNode for Subscript | test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:189:10:189:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:200:10:200:13 | ControlFlowNode for Subscript | test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:200:10:200:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:206:10:206:13 | ControlFlowNode for Subscript | test.py:205:28:205:33 | ControlFlowNode for SOURCE | test.py:206:10:206:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:211:10:211:13 | ControlFlowNode for Subscript | test.py:210:32:210:37 | ControlFlowNode for SOURCE | test.py:211:10:211:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:349:10:349:21 | ControlFlowNode for Subscript | test.py:349:11:349:16 | ControlFlowNode for SOURCE | test.py:349:10:349:21 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:353:10:353:20 | ControlFlowNode for Subscript | test.py:353:11:353:16 | ControlFlowNode for SOURCE | test.py:353:10:353:20 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:357:10:357:27 | ControlFlowNode for Subscript | test.py:357:16:357:21 | ControlFlowNode for SOURCE | test.py:357:10:357:27 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:380:10:380:34 | ControlFlowNode for second() | test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:380:10:380:34 | ControlFlowNode for second() | Flow found |
|
||||
| test.py:388:10:388:36 | ControlFlowNode for second() | test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:388:10:388:36 | ControlFlowNode for second() | Flow found |
|
||||
| test.py:396:10:396:43 | ControlFlowNode for second() | test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:10:396:43 | ControlFlowNode for second() | Flow found |
|
||||
| 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 | ControlFlowNode for f_extra_pos() | Flow found |
|
||||
| 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 | ControlFlowNode for f_extra_keyword() | Flow found |
|
||||
| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr | Flow found |
|
||||
| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr | Flow found |
|
||||
| test.py:445:10:445:38 | ControlFlowNode for IfExp | test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp | Flow found |
|
||||
| test.py:453:10:453:39 | ControlFlowNode for IfExp | test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp | Flow found |
|
||||
| test.py:477:10:477:18 | ControlFlowNode for f() | test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() | Flow found |
|
||||
| test.py:484:10:484:34 | ControlFlowNode for second() | test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() | Flow found |
|
||||
| test.py:498:10:498:36 | ControlFlowNode for second() | test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() | Flow found |
|
||||
| test.py:512:10:512:43 | ControlFlowNode for second() | test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:10:512:43 | ControlFlowNode for second() | Flow found |
|
||||
| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | Flow found |
|
||||
| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | Flow found |
|
||||
| test.py:536:10:536:10 | ControlFlowNode for a | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | Flow found |
|
||||
| test.py:541:10:541:10 | ControlFlowNode for b | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b | Flow found |
|
||||
| test.py:548:10:548:10 | ControlFlowNode for a | test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:548:10:548:10 | ControlFlowNode for a | Flow found |
|
||||
| test.py:556:10:556:10 | ControlFlowNode for a | test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:556:10:556:10 | ControlFlowNode for a | Flow found |
|
||||
| test.py:558:10:558:10 | ControlFlowNode for c | test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:558:10:558:10 | ControlFlowNode for c | Flow found |
|
||||
| test.py:565:10:565:10 | ControlFlowNode for a | test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:565:10:565:10 | ControlFlowNode for a | Flow found |
|
||||
| test.py:573:10:573:10 | ControlFlowNode for a | test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:573:10:573:10 | ControlFlowNode for a | Flow found |
|
||||
| test.py:575:10:575:13 | ControlFlowNode for Subscript | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:575:10:575:13 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:576:12:576:12 | ControlFlowNode for c | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:576:12:576:12 | ControlFlowNode for c | Flow found |
|
||||
| test.py:583:10:583:10 | ControlFlowNode for a | test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:583:10:583:10 | ControlFlowNode for a | Flow found |
|
||||
| test.py:585:10:585:10 | ControlFlowNode for c | test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:585:10:585:10 | ControlFlowNode for c | Flow found |
|
||||
| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found |
|
||||
| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found |
|
||||
| test.py:622:10:622:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:622:10:622:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:624:12:624:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:624:12:624:16 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:625:10:625:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:625:10:625:14 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:631:10:631:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:631:10:631:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:633:12:633:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:633:12:633:16 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:634:10:634:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:634:10:634:14 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:640:10:640:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:640:10:640:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:642:12:642:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:642:12:642:16 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:643:10:643:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:643:10:643:14 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:649:10:649:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:649:10:649:11 | ControlFlowNode for a1 | Flow found |
|
||||
| test.py:651:12:651:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:651:12:651:16 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:652:10:652:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:652:10:652:14 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:660:10:660:10 | ControlFlowNode for a | test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a | Flow found |
|
||||
| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found |
|
||||
| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found |
|
||||
| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found |
|
||||
| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found |
|
||||
| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found |
|
||||
| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found |
|
||||
| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found |
|
||||
| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found |
|
||||
| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found |
|
||||
| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | Flow found |
|
||||
| test.py:808:10:808:10 | ControlFlowNode for x | test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:808:10:808:10 | ControlFlowNode for x | Flow found |
|
||||
| test.py:809:10:809:10 | ControlFlowNode for y | test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:809:10:809:10 | ControlFlowNode for y | Flow found |
|
||||
| test.py:810:10:810:10 | ControlFlowNode for z | test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:810:10:810:10 | ControlFlowNode for z | Flow found |
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.dataflow.testConfig
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Flow found"
|
||||
@@ -8,15 +8,30 @@
|
||||
# Intended sources should be the variable `SOURCE` and intended sinks should be
|
||||
# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll).
|
||||
|
||||
import sys
|
||||
import os
|
||||
import functools
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from testlib import expects
|
||||
|
||||
# These are defined so that we can evaluate the test code.
|
||||
NONSOURCE = "not a source"
|
||||
SOURCE = "source"
|
||||
|
||||
arg1 = "source1"
|
||||
arg2 = "source2"
|
||||
arg3 = "source3"
|
||||
arg4 = "source4"
|
||||
arg5 = "source5"
|
||||
arg6 = "source6"
|
||||
arg7 = "source7"
|
||||
|
||||
def is_source(x):
|
||||
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
|
||||
|
||||
def SINK(x):
|
||||
if is_source(x):
|
||||
def SINK(x, expected=SOURCE):
|
||||
if is_source(x) or x == expected:
|
||||
print("OK")
|
||||
else:
|
||||
print("Unexpected flow", x)
|
||||
@@ -27,6 +42,14 @@ def SINK_F(x):
|
||||
else:
|
||||
print("OK")
|
||||
|
||||
SINK1 = functools.partial(SINK, expected=arg1)
|
||||
SINK2 = functools.partial(SINK, expected=arg2)
|
||||
SINK3 = functools.partial(SINK, expected=arg3)
|
||||
SINK4 = functools.partial(SINK, expected=arg4)
|
||||
SINK5 = functools.partial(SINK, expected=arg5)
|
||||
SINK6 = functools.partial(SINK, expected=arg6)
|
||||
SINK7 = functools.partial(SINK, expected=arg7)
|
||||
|
||||
# Callable types
|
||||
# These are the types to which the function call operation (see section Calls) can be applied:
|
||||
|
||||
@@ -41,17 +64,19 @@ SINK(f(SOURCE, 3)) #$ flow="SOURCE -> f(..)"
|
||||
# An instance method object combines a class, a class instance and any callable object (normally a user-defined function).
|
||||
class C(object):
|
||||
|
||||
def method(self, x, cls):
|
||||
assert cls is self.__class__
|
||||
return x
|
||||
def method(self, x, y):
|
||||
SINK1(x)
|
||||
SINK2(y)
|
||||
|
||||
@classmethod
|
||||
def classmethod(cls, x):
|
||||
return x
|
||||
def classmethod(cls, x, y):
|
||||
SINK1(x)
|
||||
SINK2(y)
|
||||
|
||||
@staticmethod
|
||||
def staticmethod(x):
|
||||
return x
|
||||
def staticmethod(x, y):
|
||||
SINK1(x)
|
||||
SINK2(y)
|
||||
|
||||
def gen(self, x, count):
|
||||
n = count
|
||||
@@ -64,22 +89,40 @@ class C(object):
|
||||
|
||||
c = C()
|
||||
|
||||
# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object.
|
||||
func_obj = c.method.__func__
|
||||
@expects(6)
|
||||
def test_method_call():
|
||||
# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object.
|
||||
func_obj = c.method.__func__
|
||||
|
||||
# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1).
|
||||
SINK(c.method(SOURCE, C)) #$ flow="SOURCE -> c.method(..)"
|
||||
SINK(C.method(c, SOURCE, C)) #$ flow="SOURCE -> C.method(..)"
|
||||
SINK(func_obj(c, SOURCE, C)) #$ MISSING: flow="SOURCE -> func_obj(..)"
|
||||
# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1).
|
||||
c.method(arg1, arg2) # $ func=C.method arg1 arg2
|
||||
C.method(c, arg1, arg2) # $ func=C.method arg1 arg2
|
||||
func_obj(c, arg1, arg2) # $ MISSING: func=C.method arg1 arg2
|
||||
|
||||
|
||||
# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method.
|
||||
c_func_obj = C.classmethod.__func__
|
||||
@expects(6)
|
||||
def test_classmethod_call():
|
||||
# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method.
|
||||
c_func_obj = C.classmethod.__func__
|
||||
|
||||
# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.
|
||||
c.classmethod(arg1, arg2) # $ func=C.classmethod arg1 arg2
|
||||
C.classmethod(arg1, arg2) # $ func=C.classmethod arg1 arg2
|
||||
c_func_obj(C, arg1, arg2) # $ MISSING: func=C.classmethod arg1 arg2
|
||||
|
||||
|
||||
@expects(5)
|
||||
def test_staticmethod_call():
|
||||
# staticmethods does not have a __func__ attribute
|
||||
try:
|
||||
C.staticmethod.__func__
|
||||
except AttributeError:
|
||||
print("OK")
|
||||
|
||||
# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.
|
||||
c.staticmethod(arg1, arg2) # $ func=C.staticmethod arg1 arg2
|
||||
C.staticmethod(arg1, arg2) # $ func=C.staticmethod arg1 arg2
|
||||
|
||||
# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.
|
||||
SINK(c.classmethod(SOURCE)) #$ flow="SOURCE -> c.classmethod(..)"
|
||||
SINK(C.classmethod(SOURCE)) #$ flow="SOURCE -> C.classmethod(..)"
|
||||
SINK(c_func_obj(C, SOURCE)) #$ MISSING: flow="SOURCE -> c_func_obj(..)"
|
||||
|
||||
# Generator functions
|
||||
# A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
| file://:0:0:0:0 | Function generator_func | generator.py:1:20:1:21 | ControlFlowNode for xs |
|
||||
| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 |
|
||||
| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 |
|
||||
| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for ListComp |
|
||||
| file://:0:0:0:0 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for Yield |
|
||||
| file://:0:0:0:0 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for x |
|
||||
| file://:0:0:0:0 | Function generator_func | generator.py:2:19:2:19 | ControlFlowNode for x |
|
||||
| file://:0:0:0:0 | Function generator_func | generator.py:2:24:2:25 | ControlFlowNode for xs |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:1:1:1:3 | ControlFlowNode for wat |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:1:7:1:7 | ControlFlowNode for IntegerLiteral |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:3:1:3:10 | ControlFlowNode for ClassExpr |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:3:7:3:9 | ControlFlowNode for Wat |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:4:5:4:7 | ControlFlowNode for wat |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:4:11:4:11 | ControlFlowNode for IntegerLiteral |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:5:5:5:9 | ControlFlowNode for print |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:5:5:5:26 | ControlFlowNode for print() |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:5:11:5:20 | ControlFlowNode for Str |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:5:23:5:25 | ControlFlowNode for wat |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:7:1:7:5 | ControlFlowNode for print |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:7:1:7:23 | ControlFlowNode for print() |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:7:7:7:17 | ControlFlowNode for Str |
|
||||
| file://:0:0:0:0 | Module class_example | class_example.py:7:20:7:22 | ControlFlowNode for wat |
|
||||
| file://:0:0:0:0 | Module generator | generator.py:1:1:1:23 | ControlFlowNode for FunctionExpr |
|
||||
| file://:0:0:0:0 | Module generator | generator.py:1:5:1:18 | ControlFlowNode for generator_func |
|
||||
@@ -0,0 +1,6 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
from DataFlow::CfgNode node
|
||||
where exists(node.getLocation().getFile().getRelativePath())
|
||||
select node.getEnclosingCallable() as enclosingCallable, node
|
||||
@@ -0,0 +1,7 @@
|
||||
wat = 1
|
||||
|
||||
class Wat:
|
||||
wat = 2
|
||||
print("in class", wat) # prints 2
|
||||
|
||||
print("in module", wat) # prints 1
|
||||
@@ -0,0 +1,2 @@
|
||||
def generator_func(xs):
|
||||
return [x for x in xs]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user