mirror of
https://github.com/github/codeql.git
synced 2026-06-05 21:47:10 +02:00
Compare commits
61 Commits
yoff/pytho
...
codeql-spa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e947b5ef68 | ||
|
|
cd2398aeea | ||
|
|
d6892eaf0d | ||
|
|
d2972cb53f | ||
|
|
5576d30780 | ||
|
|
3da195f50f | ||
|
|
0430c71318 | ||
|
|
52f2a5825a | ||
|
|
d55ff83568 | ||
|
|
f34275636c | ||
|
|
0a801440b9 | ||
|
|
7edf0100cc | ||
|
|
167c837088 | ||
|
|
af45e53e77 | ||
|
|
d11fc3a00e | ||
|
|
9d5dfea5c5 | ||
|
|
dc0c7d7ec2 | ||
|
|
61a5cece56 | ||
|
|
566a92e555 | ||
|
|
be9c785cb2 | ||
|
|
1fd31d0ddd | ||
|
|
c4e3720d8a | ||
|
|
0547e9c98d | ||
|
|
2a3cff382c | ||
|
|
c610af88d3 | ||
|
|
fa63dad1d1 | ||
|
|
019a5c01ad | ||
|
|
c1c9287535 | ||
|
|
d1226b71de | ||
|
|
71a363545a | ||
|
|
3f3bed62d3 | ||
|
|
21f216af8c | ||
|
|
1751d70c62 | ||
|
|
ac8eb50c26 | ||
|
|
1ecdc3614f | ||
|
|
e3b3888bee | ||
|
|
ef9306d82c | ||
|
|
56822f8ee1 | ||
|
|
62207f152c | ||
|
|
d5f94475b5 | ||
|
|
00e95a0757 | ||
|
|
c695c151ea | ||
|
|
5e5a0437e1 | ||
|
|
d95d99848c | ||
|
|
8937e22735 | ||
|
|
37589dd8a0 | ||
|
|
a159dc1c66 | ||
|
|
cc12740c0e | ||
|
|
acb5c0e70f | ||
|
|
6042adebae | ||
|
|
dc864762c3 | ||
|
|
dd35bc0722 | ||
|
|
043ec857ab | ||
|
|
f5b17b0b48 | ||
|
|
26dca558c7 | ||
|
|
57ce0b3d51 | ||
|
|
408ba2e139 | ||
|
|
7632bdba88 | ||
|
|
4b830c1864 | ||
|
|
d6c8767647 | ||
|
|
ec815397a2 |
@@ -11,10 +11,6 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
|
||||
],
|
||||
"Bound Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/Bound.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll"
|
||||
],
|
||||
"ModulusAnalysis Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/ModulusAnalysis.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/ModulusAnalysis.qll"
|
||||
|
||||
@@ -9,6 +9,7 @@ dependencies:
|
||||
codeql/controlflow: ${workspace}
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/rangeanalysis: ${workspace}
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/threat-models: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
|
||||
@@ -4,67 +4,31 @@
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import internal.rangeanalysis.BoundSpecific
|
||||
private import csharp as CS
|
||||
private import semmle.code.csharp.dataflow.SSA::Ssa
|
||||
private import semmle.code.csharp.dataflow.internal.rangeanalysis.ConstantUtils as CU
|
||||
private import semmle.code.csharp.dataflow.internal.rangeanalysis.RangeUtils as RU
|
||||
private import semmle.code.csharp.dataflow.internal.rangeanalysis.SsaUtils as SU
|
||||
private import codeql.rangeanalysis.Bound as SharedBound
|
||||
|
||||
private newtype TBound =
|
||||
TBoundZero() or
|
||||
TBoundSsa(SsaVariable v) { v.getSourceVariable().getType() instanceof IntegralType } or
|
||||
TBoundExpr(Expr e) {
|
||||
interestingExprBound(e) and
|
||||
not exists(SsaVariable v | e = v.getAUse())
|
||||
}
|
||||
/** Provides C#-specific definitions for bounds. */
|
||||
private module BoundDefs implements SharedBound::BoundDefinitions<CS::Location> {
|
||||
class Type = CS::Type;
|
||||
|
||||
/**
|
||||
* A bound that may be inferred for an expression plus/minus an integer delta.
|
||||
*/
|
||||
abstract class Bound extends TBound {
|
||||
/** Gets a textual representation of this bound. */
|
||||
abstract string toString();
|
||||
class SsaVariable = SU::SsaVariable;
|
||||
|
||||
/** Gets an expression that equals this bound plus `delta`. */
|
||||
abstract Expr getExpr(int delta);
|
||||
class SsaSourceVariable = SourceVariable;
|
||||
|
||||
/** Gets an expression that equals this bound. */
|
||||
Expr getExpr() { result = this.getExpr(0) }
|
||||
class Expr = CS::ControlFlowNodes::ExprNode;
|
||||
|
||||
/** Gets the location of this bound. */
|
||||
abstract Location getLocation();
|
||||
class IntegralType = CS::IntegralType;
|
||||
|
||||
class ConstantIntegerExpr = CU::ConstantIntegerExpr;
|
||||
|
||||
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
|
||||
predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.getExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The bound that corresponds to the integer 0. This is used to represent all
|
||||
* integer bounds as bounds are always accompanied by an added integer delta.
|
||||
*/
|
||||
class ZeroBound extends Bound, TBoundZero {
|
||||
override string toString() { result = "0" }
|
||||
module BoundImpl = SharedBound::Bound<CS::Location, BoundDefs>;
|
||||
|
||||
override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta }
|
||||
|
||||
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound corresponding to the value of an SSA variable.
|
||||
*/
|
||||
class SsaBound extends Bound, TBoundSsa {
|
||||
/** Gets the SSA variable that equals this bound. */
|
||||
SsaVariable getSsa() { this = TBoundSsa(result) }
|
||||
|
||||
override string toString() { result = this.getSsa().toString() }
|
||||
|
||||
override Expr getExpr(int delta) { result = this.getSsa().getAUse() and delta = 0 }
|
||||
|
||||
override Location getLocation() { result = this.getSsa().getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound that corresponds to the value of a specific expression that might be
|
||||
* interesting, but isn't otherwise represented by the value of an SSA variable.
|
||||
*/
|
||||
class ExprBound extends Bound, TBoundExpr {
|
||||
override string toString() { result = this.getExpr().toString() }
|
||||
|
||||
override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 }
|
||||
|
||||
override Location getLocation() { result = this.getExpr().getLocation() }
|
||||
}
|
||||
import BoundImpl
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Provides C#-specific definitions for bounds.
|
||||
*/
|
||||
|
||||
private import csharp as CS
|
||||
private import semmle.code.csharp.dataflow.SSA::Ssa as Ssa
|
||||
private import semmle.code.csharp.dataflow.internal.rangeanalysis.ConstantUtils as CU
|
||||
private import semmle.code.csharp.dataflow.internal.rangeanalysis.RangeUtils as RU
|
||||
private import semmle.code.csharp.dataflow.internal.rangeanalysis.SsaUtils as SU
|
||||
|
||||
class SsaVariable = SU::SsaVariable;
|
||||
|
||||
class Expr = CS::ControlFlowNodes::ExprNode;
|
||||
|
||||
class Location = CS::Location;
|
||||
|
||||
class IntegralType = CS::IntegralType;
|
||||
|
||||
class ConstantIntegerExpr = CU::ConstantIntegerExpr;
|
||||
|
||||
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
|
||||
predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.getExpr()) }
|
||||
@@ -0,0 +1,139 @@
|
||||
.. _codeql-cli-2.25.6:
|
||||
|
||||
==========================
|
||||
CodeQL 2.25.6 (2026-06-04)
|
||||
==========================
|
||||
|
||||
.. contents:: Contents
|
||||
:depth: 2
|
||||
:local:
|
||||
:backlinks: none
|
||||
|
||||
This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog <https://github.blog/tag/code-scanning/>`__, `relevant GitHub Changelog updates <https://github.blog/changelog/label/application-security/>`__, `changes in the CodeQL extension for Visual Studio Code <https://marketplace.visualstudio.com/items/GitHub.vscode-codeql/changelog>`__, and the `CodeQL Action changelog <https://github.com/github/codeql-action/blob/main/CHANGELOG.md>`__.
|
||||
|
||||
Security Coverage
|
||||
-----------------
|
||||
|
||||
CodeQL 2.25.6 runs a total of 496 security queries when configured with the Default suite (covering 169 CWE). The Extended suite enables an additional 131 queries (covering 32 more CWE).
|
||||
|
||||
CodeQL CLI
|
||||
----------
|
||||
|
||||
Improvements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* When the :code:`git` executable is available, CodeQL can now obtain configuration and queries from SHA-256 Git repositories, and infer Git metadata about them.
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* The build of Eclipse Temurin OpenJDK that is used to run the CodeQL CLI has been updated to version 21.0.11.
|
||||
|
||||
Query Packs
|
||||
-----------
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
|
||||
GitHub Actions
|
||||
""""""""""""""
|
||||
|
||||
* Adjusted (minor) help file descriptions for queries: :code:`actions/untrusted-checkout/critical`, :code:`actions/untrusted-checkout/high`, :code:`actions/untrusted-checkout/medium`. Clarified wording on in minor point, added one more listed resource and added one more recommendation for things to check.
|
||||
|
||||
Major Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
GitHub Actions
|
||||
""""""""""""""
|
||||
|
||||
* Adjusted :code:`actions/untrusted-checkout/critical` to align more with other untrusted resource queries, where the alert location is the location where the artifact is obtained from (the checkout point). This aligns with the other 2 related queries. This will cause the same alerts to re-open for closed alerts of this query.
|
||||
|
||||
Minor Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
GitHub Actions
|
||||
""""""""""""""
|
||||
|
||||
* Altered the alert message for clarity for queries: :code:`actions/untrusted-checkout/critical`, :code:`actions/untrusted-checkout/high`.
|
||||
* The :code:`actions/unpinned-tag` query now recognizes 64-character SHA-256 commit hashes as properly pinned references, in addition to 40-character SHA-1 hashes.
|
||||
|
||||
Query Metadata Changes
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
GitHub Actions
|
||||
""""""""""""""
|
||||
|
||||
* Reversed adjustment of the name of :code:`actions/untrusted-checkout/high`, but kept the portion of the previous change for the word "trusted" to "privileged". Added a missing "a" to phrasing in :code:`actions/untrusted-checkout/high` and :code:`actions/untrusted-checkout/medium`.
|
||||
|
||||
Language Libraries
|
||||
------------------
|
||||
|
||||
Major Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Swift
|
||||
"""""
|
||||
|
||||
* Upgraded to allow analysis of Swift 6.3.2.
|
||||
|
||||
Minor Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C/C++
|
||||
"""""
|
||||
|
||||
* Added flow source models for :code:`scanf_s` and related functions.
|
||||
* Added a :code:`Call` column to :code:`LocalFlowSourceFunction::hasLocalFlowSource` and :code:`RemoteFlowSourceFunction::hasRemoteFlowSource`. The old predicates without a :code:`Call` column continue to be supported.
|
||||
|
||||
C#
|
||||
""
|
||||
|
||||
* Full support for C# 14 / .NET 10. All new language features are now supported by the extractor. The QL library and data flow analysis now support the new C# 14 language constructs and include generated Models as Data (MaD) models for the .NET 10 runtime.
|
||||
* C# 14: Added support for user-defined instance increment/decrement operators.
|
||||
|
||||
Java/Kotlin
|
||||
"""""""""""
|
||||
|
||||
* Added LLM-generated source and sink models for :code:`org.apache.avro`.
|
||||
|
||||
JavaScript/TypeScript
|
||||
"""""""""""""""""""""
|
||||
|
||||
* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example :code:`js/clear-text-logging`) may find more correct results and fewer false positive results after these changes.
|
||||
|
||||
Python
|
||||
""""""
|
||||
|
||||
* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example :code:`py/clear-text-logging-sensitive-data`) may find more correct results and less fewer positive results after these changes.
|
||||
|
||||
Swift
|
||||
"""""
|
||||
|
||||
* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example :code:`swift/cleartext-logging`) may find more correct results and fewer false positive results after these changes.
|
||||
|
||||
GitHub Actions
|
||||
""""""""""""""
|
||||
|
||||
* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, include regexes like :code:`^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a sha1 or sha256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used.
|
||||
|
||||
Rust
|
||||
""""
|
||||
|
||||
* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example :code:`rust/cleartext-logging`) may find more correct results and fewer false positive results after these changes.
|
||||
|
||||
Deprecated APIs
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
C/C++
|
||||
"""""
|
||||
|
||||
* The :code:`UsingAliasTypedefType` class has been deprecated. Use :code:`TypeAliasType` instead.
|
||||
|
||||
New Features
|
||||
~~~~~~~~~~~~
|
||||
|
||||
C/C++
|
||||
"""""
|
||||
|
||||
* Added a :code:`getOriginalTemplate` predicate to :code:`TemplateClass`, :code:`TemplateFunction`, :code:`TemplateVariable`, and :code:`AliasTemplateType`, which yields the class member template the template was generated from. The predicates only have results for templates that are members of class template instantiations.
|
||||
* Added :code:`AliasTemplateType` and :code:`AliasTemplateInstantiationType` classes, representing C++ alias templates and their instantiations.
|
||||
@@ -11,6 +11,7 @@ A list of queries for each suite and language `is available here <https://docs.g
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
codeql-cli-2.25.6
|
||||
codeql-cli-2.25.5
|
||||
codeql-cli-2.25.4
|
||||
codeql-cli-2.25.3
|
||||
|
||||
@@ -9,9 +9,9 @@ import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import utils.test.InlineFlowTest
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { sourceNode(src, "qltest") }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node src) { sinkNode(src, "qltest") }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
import ValueFlowTest<Config>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
reverseRead
|
||||
| main.go:23:3:23:5 | out | Origin of readStep is missing a PostUpdateNode. |
|
||||
@@ -4,7 +4,7 @@ func source() string {
|
||||
return "untrusted data"
|
||||
}
|
||||
|
||||
func sink(string) {
|
||||
func sink(any) {
|
||||
}
|
||||
|
||||
type A struct {
|
||||
@@ -19,6 +19,10 @@ func functionWithVarArgsParameter(s ...string) string {
|
||||
return s[1]
|
||||
}
|
||||
|
||||
func functionWithVarArgsOutParameter(in string, out ...*string) {
|
||||
*out[0] = in
|
||||
}
|
||||
|
||||
func functionWithSliceOfStructsParameter(s []A) string {
|
||||
return s[1].f
|
||||
}
|
||||
@@ -38,6 +42,12 @@ func main() {
|
||||
sink(functionWithVarArgsParameter(sSlice...)) // $ hasValueFlow="call to functionWithVarArgsParameter"
|
||||
sink(functionWithVarArgsParameter(s0, s1)) // $ hasValueFlow="call to functionWithVarArgsParameter"
|
||||
|
||||
var out1 *string
|
||||
var out2 *string
|
||||
functionWithVarArgsOutParameter(source(), out1, out2)
|
||||
sink(out1) // $ MISSING: hasValueFlow="out1"
|
||||
sink(out2) // $ MISSING: hasValueFlow="out2"
|
||||
|
||||
sliceOfStructs := []A{{f: source()}}
|
||||
sink(sliceOfStructs[0].f) // $ hasValueFlow="selection of f"
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
invalidModelRow
|
||||
testFailures
|
||||
@@ -0,0 +1,21 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "", False, "FunctionWithParameter", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- ["github.com/nonexistent/test", "", False, "FunctionWithSliceParameter", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
|
||||
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsParameter", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
|
||||
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsOutParameter", "", "", "Argument[0]", "Argument[1].ArrayElement", "value", "manual"]
|
||||
- ["github.com/nonexistent/test", "", False, "FunctionWithSliceOfStructsParameter", "", "", "Argument[0].ArrayElement.Field[github.com/nonexistent/test.A.Field]", "ReturnValue", "value", "manual"]
|
||||
- ["github.com/nonexistent/test", "", False, "FunctionWithVarArgsOfStructsParameter", "", "", "Argument[0].ArrayElement.Field[github.com/nonexistent/test.A.Field]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "", False, "VariadicSource", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "", False, "VariadicSink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -0,0 +1,22 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import utils.test.InlineFlowTest
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
sourceNode(source, "qltest")
|
||||
or
|
||||
exists(Function fn | fn.hasQualifiedName(_, ["source", "taint"]) |
|
||||
source = fn.getACall().getResult()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sinkNode(sink, "qltest")
|
||||
or
|
||||
exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument())
|
||||
}
|
||||
}
|
||||
|
||||
import FlowTest<Config, Config>
|
||||
@@ -0,0 +1,5 @@
|
||||
module semmle.go.Packages
|
||||
|
||||
go 1.25
|
||||
|
||||
require github.com/nonexistent/test v0.0.0-20200203000000-0000000000000
|
||||
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/nonexistent/test"
|
||||
)
|
||||
|
||||
func source() string {
|
||||
return "untrusted data"
|
||||
}
|
||||
|
||||
func sink(any) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := source()
|
||||
sink(test.FunctionWithParameter(s)) // $ hasValueFlow="call to FunctionWithParameter"
|
||||
|
||||
stringSlice := []string{source()}
|
||||
sink(stringSlice[0]) // $ hasValueFlow="index expression"
|
||||
|
||||
s0 := ""
|
||||
s1 := source()
|
||||
sSlice := []string{s0, s1}
|
||||
sink(test.FunctionWithParameter(sSlice[1])) // $ hasValueFlow="call to FunctionWithParameter"
|
||||
sink(test.FunctionWithSliceParameter(sSlice)) // $ hasValueFlow="call to FunctionWithSliceParameter"
|
||||
sink(test.FunctionWithVarArgsParameter(sSlice...)) // $ hasValueFlow="call to FunctionWithVarArgsParameter"
|
||||
sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ hasValueFlow="call to FunctionWithVarArgsParameter"
|
||||
|
||||
var out1 *string
|
||||
var out2 *string
|
||||
test.FunctionWithVarArgsOutParameter(source(), out1, out2)
|
||||
sink(out1) // $ MISSING: hasValueFlow="out1"
|
||||
sink(out2) // $ MISSING: hasValueFlow="out2"
|
||||
|
||||
sliceOfStructs := []test.A{{Field: source()}}
|
||||
sink(sliceOfStructs[0].Field) // $ hasValueFlow="selection of Field"
|
||||
|
||||
a0 := test.A{Field: ""}
|
||||
a1 := test.A{Field: source()}
|
||||
aSlice := []test.A{a0, a1}
|
||||
sink(test.FunctionWithSliceOfStructsParameter(aSlice)) // $ hasValueFlow="call to FunctionWithSliceOfStructsParameter"
|
||||
sink(test.FunctionWithVarArgsOfStructsParameter(aSlice...)) // $ hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
|
||||
sink(test.FunctionWithVarArgsOfStructsParameter(a0, a1)) // $ hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
|
||||
|
||||
var variadicSource string
|
||||
test.VariadicSource(&variadicSource)
|
||||
sink(variadicSource) // $ MISSING: hasTaintFlow="variadicSource"
|
||||
sink(&variadicSource) // $ MISSING: hasTaintFlow="&..."
|
||||
|
||||
var variadicSourcePtr *string
|
||||
test.VariadicSource(variadicSourcePtr)
|
||||
sink(variadicSourcePtr) // $ MISSING: hasTaintFlow="variadicSourcePtr"
|
||||
sink(*variadicSourcePtr) // $ MISSING: hasTaintFlow="star expression"
|
||||
|
||||
test.VariadicSink(source()) // $ hasTaintFlow="[]type{args}"
|
||||
}
|
||||
32
go/ql/test/library-tests/semmle/go/dataflow/VarArgsWithExternalFlow/vendor/github.com/nonexistent/test/stub.go
generated
vendored
Normal file
32
go/ql/test/library-tests/semmle/go/dataflow/VarArgsWithExternalFlow/vendor/github.com/nonexistent/test/stub.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package test
|
||||
|
||||
type A struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
func FunctionWithParameter(s string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func FunctionWithSliceParameter(s []string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func FunctionWithVarArgsParameter(s ...string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func FunctionWithVarArgsOutParameter(in string, out ...*string) {
|
||||
}
|
||||
|
||||
func FunctionWithSliceOfStructsParameter(s []A) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func FunctionWithVarArgsOfStructsParameter(s ...A) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func VariadicSource(s ...*string) {}
|
||||
|
||||
func VariadicSink(s ...string) {}
|
||||
3
go/ql/test/library-tests/semmle/go/dataflow/VarArgsWithExternalFlow/vendor/modules.txt
vendored
Normal file
3
go/ql/test/library-tests/semmle/go/dataflow/VarArgsWithExternalFlow/vendor/modules.txt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# github.com/nonexistent/test v0.0.0-20200203000000-0000000000000
|
||||
## explicit
|
||||
github.com/nonexistent/test
|
||||
@@ -20,6 +20,9 @@ class SummaryModelTest extends DataFlow::FunctionModel {
|
||||
this.hasQualifiedName("github.com/nonexistent/test", "FunctionWithVarArgsParameter") and
|
||||
(inp.isParameter(_) and outp.isResult())
|
||||
or
|
||||
this.hasQualifiedName("github.com/nonexistent/test", "FunctionWithVarArgsOutParameter") and
|
||||
(inp.isParameter(0) and outp.isParameter(any(int i | i >= 1)))
|
||||
or
|
||||
this.hasQualifiedName("github.com/nonexistent/test", "FunctionWithSliceOfStructsParameter") and
|
||||
(inp.isParameter(0) and outp.isResult())
|
||||
or
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module semmle.go.Packages
|
||||
|
||||
go 1.17
|
||||
go 1.25
|
||||
|
||||
require github.com/nonexistent/test v0.0.0-20200203000000-0000000000000
|
||||
|
||||
@@ -8,7 +8,7 @@ func source() string {
|
||||
return "untrusted data"
|
||||
}
|
||||
|
||||
func sink(string) {
|
||||
func sink(any) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -21,10 +21,17 @@ func main() {
|
||||
s0 := ""
|
||||
s1 := source()
|
||||
sSlice := []string{s0, s1}
|
||||
sink(test.FunctionWithParameter(sSlice[1])) // $ hasValueFlow="call to FunctionWithParameter"
|
||||
sink(test.FunctionWithSliceParameter(sSlice)) // $ hasTaintFlow="call to FunctionWithSliceParameter" MISSING: hasValueFlow="call to FunctionWithSliceParameter"
|
||||
sink(test.FunctionWithVarArgsParameter(sSlice...)) // $ hasTaintFlow="call to FunctionWithVarArgsParameter" MISSING: hasValueFlow="call to FunctionWithVarArgsParameter"
|
||||
sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ MISSING: hasValueFlow="call to FunctionWithVarArgsParameter"
|
||||
sink(test.FunctionWithParameter(sSlice[1])) // $ hasValueFlow="call to FunctionWithParameter"
|
||||
sink(test.FunctionWithSliceParameter(sSlice)) // $ hasTaintFlow="call to FunctionWithSliceParameter" MISSING: hasValueFlow="call to FunctionWithSliceParameter"
|
||||
sink(test.FunctionWithVarArgsParameter(sSlice...)) // $ hasTaintFlow="call to FunctionWithVarArgsParameter" MISSING: hasValueFlow="call to FunctionWithVarArgsParameter"
|
||||
randomFunctionWithMoreThanOneParameter(1, 2, 3, 4, 5) // This is needed to make the next line pass, because we need to have seen a call to a function with at least 2 parameters for ParameterInput to exist with index 1.
|
||||
sink(test.FunctionWithVarArgsParameter(s0, s1)) // $ hasValueFlow="call to FunctionWithVarArgsParameter"
|
||||
|
||||
var out1 *string
|
||||
var out2 *string
|
||||
test.FunctionWithVarArgsOutParameter(source(), out1, out2)
|
||||
sink(out1) // $ hasValueFlow="out1"
|
||||
sink(out2) // $ hasValueFlow="out2"
|
||||
|
||||
sliceOfStructs := []test.A{{Field: source()}}
|
||||
sink(sliceOfStructs[0].Field) // $ hasValueFlow="selection of Field"
|
||||
@@ -37,3 +44,6 @@ func main() {
|
||||
sink(test.FunctionWithVarArgsOfStructsParameter(aSlice...)) // $ MISSING: hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
|
||||
sink(test.FunctionWithVarArgsOfStructsParameter(a0, a1)) // $ MISSING: hasValueFlow="call to FunctionWithVarArgsOfStructsParameter"
|
||||
}
|
||||
|
||||
func randomFunctionWithMoreThanOneParameter(i1, i2, i3, i4, i5 int) {
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -16,6 +16,9 @@ func FunctionWithVarArgsParameter(s ...string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func FunctionWithVarArgsOutParameter(in string, out ...*string) {
|
||||
}
|
||||
|
||||
func FunctionWithSliceOfStructsParameter(s []A) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ org.apache.hc.core5.http,73,2,45,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,72,,,,,,,,,,,
|
||||
org.apache.hc.core5.net,,,18,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18,
|
||||
org.apache.hc.core5.util,,,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18,6
|
||||
org.apache.hive.hcatalog.templeton,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,
|
||||
org.apache.http,48,3,95,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,46,,,,,,,,,,,,,,,,3,86,9
|
||||
org.apache.http,53,3,117,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,51,,,,,,,,,,,,,,,,3,108,9
|
||||
org.apache.ibatis.jdbc,6,,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,57,
|
||||
org.apache.ibatis.mapping,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.log4j,11,,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
|
||||
|
@@ -13,7 +13,7 @@ Java framework & library support
|
||||
`Apache Commons IO <https://commons.apache.org/proper/commons-io/>`_,``org.apache.commons.io``,,570,124,105,,,,,15
|
||||
`Apache Commons Lang <https://commons.apache.org/proper/commons-lang/>`_,``org.apache.commons.lang3``,,425,7,,,,,,
|
||||
`Apache Commons Text <https://commons.apache.org/proper/commons-text/>`_,``org.apache.commons.text``,,272,,,,,,,
|
||||
`Apache HttpComponents <https://hc.apache.org/>`_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,183,122,,3,,,,119
|
||||
`Apache HttpComponents <https://hc.apache.org/>`_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,205,127,,3,,,,124
|
||||
`Apache Log4j 2 <https://logging.apache.org/log4j/2.0/>`_,``org.apache.logging.log4j``,,8,359,,,,,,
|
||||
`Apache Struts <https://struts.apache.org/>`_,"``org.apache.struts2``, ``org.apache.struts.beanvalidation.validation.interceptor``",,3877,14,,,,,,
|
||||
`Apache Velocity <https://velocity.apache.org/>`_,"``org.apache.velocity.app``, ``org.apache.velocity.runtime``",,,8,,,,,,
|
||||
@@ -41,5 +41,5 @@ Java framework & library support
|
||||
`Thymeleaf <https://www.thymeleaf.org/>`_,``org.thymeleaf``,,2,2,,,,,,
|
||||
`jOOQ <https://www.jooq.org/>`_,``org.jooq``,,,1,,,1,,,
|
||||
Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.com.caucho.hessian.io``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.caucho.burlap.io``, ``com.caucho.hessian.io``, ``com.cedarsoftware.util.io``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.esotericsoftware.yamlbeans``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``hudson``, ``io.jsonwebtoken``, ``io.undertow.server.handlers.resource``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``liquibase.database.jvm``, ``liquibase.statement.core``, ``net.lingala.zip4j``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.avro``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.fileupload``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hadoop.hive.ql.exec``, ``org.apache.hadoop.hive.ql.metadata``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.ibatis.mapping``, ``org.apache.log4j``, ``org.apache.shiro.authc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.codehaus.cargo.container.installer``, ``org.dom4j``, ``org.exolab.castor.xml``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.ho.yaml``, ``org.influxdb``, ``org.jabsorb``, ``org.jboss.vfs``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.keycloak.models.map.storage``, ``org.kohsuke.stapler``, ``org.lastaflute.web``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.pac4j.jwt.config.encryption``, ``org.pac4j.jwt.config.signature``, ``org.scijava.log``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``software.amazon.awssdk.transfer.s3.model``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",127,6034,775,148,6,14,18,,186
|
||||
Totals,,382,26381,2702,421,16,137,33,1,410
|
||||
Totals,,382,26403,2707,421,16,137,33,1,415
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved modeling of Apache HttpClient `execute` method sinks for `java/ssrf` and `java/non-https-url`.
|
||||
@@ -11,7 +11,7 @@ extensions:
|
||||
- ["org.apache.http.client.methods", "HttpPost", False, "HttpPost", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["org.apache.http.client.methods", "HttpPut", False, "HttpPut", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["org.apache.http.client.methods", "HttpRequestBase", True, "setURI", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["org.apache.http.client.methods", "HttpRequestWrapper", True, "setURI", "(URI)", "", "Argument[0]", "request-forgery", "hq-manual"]
|
||||
- ["org.apache.http.client.methods", "HttpRequestWrapper", True, "setURI", "(URI)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "HttpTrace", False, "HttpTrace", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", False, "delete", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", False, "get", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
@@ -22,3 +22,29 @@ extensions:
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", False, "put", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", False, "setUri", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", False, "trace", "", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "delete", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "delete", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "get", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "get", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "getUri", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "head", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "head", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "options", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "options", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "patch", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "patch", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "post", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "post", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "put", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "put", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "setUri", "(String)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "setUri", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "setUri", "(URI)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "setUri", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "trace", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
- ["org.apache.http.client.methods", "RequestBuilder", True, "trace", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
|
||||
|
||||
@@ -3,6 +3,11 @@ extensions:
|
||||
pack: codeql/java-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,ResponseHandler,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,ResponseHandler)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,ResponseHandler,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,ResponseHandler)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,ResponseHandler,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
|
||||
|
||||
@@ -4,67 +4,33 @@
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import internal.rangeanalysis.BoundSpecific
|
||||
private import java as J
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.RangeUtils as RU
|
||||
private import codeql.rangeanalysis.Bound as SharedBound
|
||||
|
||||
private newtype TBound =
|
||||
TBoundZero() or
|
||||
TBoundSsa(SsaVariable v) { v.getSourceVariable().getType() instanceof IntegralType } or
|
||||
TBoundExpr(Expr e) {
|
||||
interestingExprBound(e) and
|
||||
not exists(SsaVariable v | e = v.getAUse())
|
||||
private module BoundDefs implements SharedBound::BoundDefinitions<J::Location> {
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
/** Gets a use of this variable. */
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound that may be inferred for an expression plus/minus an integer delta.
|
||||
*/
|
||||
abstract class Bound extends TBound {
|
||||
/** Gets a textual representation of this bound. */
|
||||
abstract string toString();
|
||||
class SsaSourceVariable = Ssa::SourceVariable;
|
||||
|
||||
/** Gets an expression that equals this bound plus `delta`. */
|
||||
abstract Expr getExpr(int delta);
|
||||
class Type = J::Type;
|
||||
|
||||
/** Gets an expression that equals this bound. */
|
||||
Expr getExpr() { result = this.getExpr(0) }
|
||||
class Expr = J::Expr;
|
||||
|
||||
/** Gets the location of this bound. */
|
||||
abstract Location getLocation();
|
||||
class IntegralType = J::IntegralType;
|
||||
|
||||
class ConstantIntegerExpr = RU::ConstantIntegerExpr;
|
||||
|
||||
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
|
||||
predicate interestingExprBound(Expr e) {
|
||||
e.(J::FieldRead).getField() instanceof J::ArrayLengthField
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The bound that corresponds to the integer 0. This is used to represent all
|
||||
* integer bounds as bounds are always accompanied by an added integer delta.
|
||||
*/
|
||||
class ZeroBound extends Bound, TBoundZero {
|
||||
override string toString() { result = "0" }
|
||||
module BoundImpl = SharedBound::Bound<J::Location, BoundDefs>;
|
||||
|
||||
override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta }
|
||||
|
||||
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound corresponding to the value of an SSA variable.
|
||||
*/
|
||||
class SsaBound extends Bound, TBoundSsa {
|
||||
/** Gets the SSA variable that equals this bound. */
|
||||
SsaVariable getSsa() { this = TBoundSsa(result) }
|
||||
|
||||
override string toString() { result = this.getSsa().toString() }
|
||||
|
||||
override Expr getExpr(int delta) { result = this.getSsa().getAUse() and delta = 0 }
|
||||
|
||||
override Location getLocation() { result = this.getSsa().getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound that corresponds to the value of a specific expression that might be
|
||||
* interesting, but isn't otherwise represented by the value of an SSA variable.
|
||||
*/
|
||||
class ExprBound extends Bound, TBoundExpr {
|
||||
override string toString() { result = this.getExpr().toString() }
|
||||
|
||||
override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 }
|
||||
|
||||
override Location getLocation() { result = this.getExpr().getLocation() }
|
||||
}
|
||||
import BoundImpl
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Provides Java-specific definitions for bounds.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import java as J
|
||||
private import semmle.code.java.dataflow.SSA as Ssa
|
||||
private import semmle.code.java.dataflow.RangeUtils as RU
|
||||
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
/** Gets a use of this variable. */
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
class Location = J::Location;
|
||||
|
||||
class IntegralType = J::IntegralType;
|
||||
|
||||
class ConstantIntegerExpr = RU::ConstantIntegerExpr;
|
||||
|
||||
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
|
||||
predicate interestingExprBound(Expr e) {
|
||||
e.(J::FieldRead).getField() instanceof J::ArrayLengthField
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.ResponseHandler;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicHttpRequest;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class ApacheHttpClientExecuteSSRF extends HttpServlet {
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
try {
|
||||
|
||||
String source = request.getParameter("host"); // $ Source
|
||||
|
||||
HttpHost host = new HttpHost(source);
|
||||
HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
HttpUriRequest uriReq = RequestBuilder.get(source).build(); // $ Alert
|
||||
HttpContext context = null;
|
||||
HttpClient client = HttpClients.createDefault();
|
||||
ResponseHandler<Object> handler = null;
|
||||
|
||||
client.execute(host, req); // $ Alert
|
||||
client.execute(host, req, context); // $ Alert
|
||||
client.execute(host, req, handler); // $ Alert
|
||||
client.execute(host, req, handler, context); // $ Alert
|
||||
client.execute(uriReq); // $ Alert
|
||||
client.execute(uriReq, context); // $ Alert
|
||||
client.execute(uriReq, handler); // $ Alert
|
||||
client.execute(uriReq, handler, context); // $ Alert
|
||||
|
||||
} catch (Exception e) {
|
||||
// TODO: handle exception
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/javax-validation-constraints:${testdir}/../../../stubs/springframework-5.8.x:${testdir}/../../../stubs/javax-ws-rs-api-2.1.1:${testdir}/../../../stubs/javax-ws-rs-api-3.0.0:${testdir}/../../../stubs/apache-http-4.4.13/:${testdir}/../../../stubs/projectreactor-3.4.3/:${testdir}/../../../stubs/postgresql-42.3.3/:${testdir}/../../../stubs/HikariCP-3.4.5/:${testdir}/../../../stubs/spring-jdbc-5.3.8/:${testdir}/../../../stubs/jdbi3-core-3.27.2/:${testdir}/../../../stubs/cargo:${testdir}/../../../stubs/javafx-web:${testdir}/../../../stubs/apache-commons-jelly-1.0.1:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/stapler-1.263:${testdir}/../../../stubs/javax-servlet-2.5:${testdir}/../../../stubs/apache-commons-fileupload-1.4:${testdir}/../../../stubs/saxon-xqj-9.x:${testdir}/../../../stubs/apache-commons-beanutils:${testdir}/../../../stubs/apache-commons-lang:${testdir}/../../../stubs/apache-http-5:${testdir}/../../../stubs/playframework-2.6.x:${testdir}/../../../stubs/jaxws-api-2.0:${testdir}/../../../stubs/apache-cxf
|
||||
//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/javax-validation-constraints:${testdir}/../../../stubs/springframework-5.8.x:${testdir}/../../../stubs/javax-ws-rs-api-2.1.1:${testdir}/../../../stubs/javax-ws-rs-api-3.0.0:${testdir}/../../../stubs/apache-http-4.4.13/:${testdir}/../../../stubs/apache-http-client-4.4.13:${testdir}/../../../stubs/projectreactor-3.4.3/:${testdir}/../../../stubs/postgresql-42.3.3/:${testdir}/../../../stubs/HikariCP-3.4.5/:${testdir}/../../../stubs/spring-jdbc-5.3.8/:${testdir}/../../../stubs/jdbi3-core-3.27.2/:${testdir}/../../../stubs/cargo:${testdir}/../../../stubs/javafx-web:${testdir}/../../../stubs/apache-commons-jelly-1.0.1:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/stapler-1.263:${testdir}/../../../stubs/javax-servlet-2.5:${testdir}/../../../stubs/apache-commons-fileupload-1.4:${testdir}/../../../stubs/saxon-xqj-9.x:${testdir}/../../../stubs/apache-commons-beanutils:${testdir}/../../../stubs/apache-commons-lang:${testdir}/../../../stubs/apache-http-5:${testdir}/../../../stubs/playframework-2.6.x:${testdir}/../../../stubs/jaxws-api-2.0:${testdir}/../../../stubs/apache-cxf
|
||||
|
||||
23
java/ql/test/stubs/apache-http-client-4.4.13/org/apache/http/client/HttpClient.java
generated
Normal file
23
java/ql/test/stubs/apache-http-client-4.4.13/org/apache/http/client/HttpClient.java
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
// Generated automatically from org.apache.http.client.HttpClient for testing purposes
|
||||
|
||||
package org.apache.http.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
||||
public interface HttpClient {
|
||||
HttpResponse execute(HttpHost target, HttpRequest request) throws IOException;
|
||||
HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException;
|
||||
<T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler) throws IOException;
|
||||
<T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context)
|
||||
throws IOException;
|
||||
HttpResponse execute(HttpUriRequest request) throws IOException;
|
||||
HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException;
|
||||
<T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException;
|
||||
<T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context)
|
||||
throws IOException;
|
||||
}
|
||||
9
java/ql/test/stubs/apache-http-client-4.4.13/org/apache/http/client/ResponseHandler.java
generated
Normal file
9
java/ql/test/stubs/apache-http-client-4.4.13/org/apache/http/client/ResponseHandler.java
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
// Generated automatically from org.apache.http.client.ResponseHandler for testing purposes
|
||||
|
||||
package org.apache.http.client;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
|
||||
public interface ResponseHandler<T> {
|
||||
T handleResponse(HttpResponse response);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.apache.http.impl.client;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
|
||||
public abstract class CloseableHttpClient implements HttpClient {
|
||||
|
||||
}
|
||||
10
java/ql/test/stubs/apache-http-client-4.4.13/org/apache/http/impl/client/HttpClients.java
generated
Normal file
10
java/ql/test/stubs/apache-http-client-4.4.13/org/apache/http/impl/client/HttpClients.java
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
// Generated automatically from org.apache.http.client.HttpClient for testing purposes
|
||||
|
||||
package org.apache.http.impl.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
|
||||
public final class HttpClients {
|
||||
public static CloseableHttpClient createDefault() { return null; }
|
||||
}
|
||||
@@ -21,13 +21,19 @@ file_coverage_languages:
|
||||
scc_languages:
|
||||
- TypeScript
|
||||
- TypeScript Typings
|
||||
- name: vue
|
||||
display_name: Vue.js component
|
||||
scc_languages:
|
||||
- Vue
|
||||
github_api_languages:
|
||||
- JavaScript
|
||||
- TypeScript
|
||||
- Vue
|
||||
scc_languages:
|
||||
- JavaScript
|
||||
- TypeScript
|
||||
- TypeScript Typings
|
||||
- Vue
|
||||
file_types:
|
||||
- name: javascript
|
||||
display_name: JavaScript
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
import semmle.python.controlflow.internal.AstNodeImpl
|
||||
import ControlFlow::Consistency
|
||||
@@ -213,11 +213,9 @@ class ExprWithPointsTo extends Expr {
|
||||
* Gets what this expression might "refer-to" in the given `context`.
|
||||
*/
|
||||
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).refersTo(context, obj, cls, origin_)
|
||||
)
|
||||
this.getAFlowNode()
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.refersTo(context, obj, cls, origin.getAFlowNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,11 +226,7 @@ class ExprWithPointsTo extends Expr {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate refersTo(Object obj, AstNode origin) {
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).refersTo(obj, origin_)
|
||||
)
|
||||
this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,22 +240,16 @@ class ExprWithPointsTo extends Expr {
|
||||
* in the given `context`.
|
||||
*/
|
||||
predicate pointsTo(Context context, Value value, AstNode origin) {
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).pointsTo(context, value, origin_)
|
||||
)
|
||||
this.getAFlowNode()
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.pointsTo(context, value, origin.getAFlowNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression might "point-to" to `value` which is from `origin`.
|
||||
*/
|
||||
predicate pointsTo(Value value, AstNode origin) {
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).pointsTo(value, origin_)
|
||||
)
|
||||
this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -487,10 +475,7 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics {
|
||||
not non_coupling_method(result) and
|
||||
exists(Call call | call.getScope() = this |
|
||||
exists(FunctionObject callee | callee.getFunction() = result |
|
||||
exists(CallNode call_ |
|
||||
call_.getNode() = call and
|
||||
call_.getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
|
||||
)
|
||||
call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
|
||||
)
|
||||
or
|
||||
exists(Attribute a | call.getFunc() = a |
|
||||
|
||||
@@ -64,7 +64,7 @@ private predicate jump_to_defn(ControlFlowNode use, Definition defn) {
|
||||
private predicate preferred_jump_to_defn(Expr use, Definition def) {
|
||||
not use instanceof ClassExpr and
|
||||
not use instanceof FunctionExpr and
|
||||
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, def))
|
||||
jump_to_defn(use.getAFlowNode(), def)
|
||||
}
|
||||
|
||||
private predicate unique_jump_to_defn(Expr use, Definition def) {
|
||||
@@ -452,7 +452,7 @@ private predicate self_parameter_jump_to_defn_attribute(
|
||||
* This exists primarily for testing use `getPreferredDefinition()` instead.
|
||||
*/
|
||||
Definition getADefinition(Expr use) {
|
||||
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, result)) and
|
||||
jump_to_defn(use.getAFlowNode(), result) and
|
||||
not use instanceof Call and
|
||||
not use.isArtificial() and
|
||||
// Not the use itself
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* A new Python control flow graph implementation has been added under `semmle.python.controlflow.internal.Cfg` (backed by `AstNodeImpl.qll`), built on the shared `codeql.controlflow.ControlFlowGraph` library. It is not yet used by the dataflow library or any production query; the legacy CFG in `semmle/python/Flow.qll` remains the default. The new library is exposed for tests and for upcoming migrations.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* A new SSA adapter has been added under `semmle.python.dataflow.new.internal.SsaImpl`, built on the shared `codeql.ssa.Ssa` library and the new shared CFG (`semmle.python.controlflow.internal.Cfg`). It is not yet used by the dataflow library or any production query; the legacy ESSA SSA in `semmle/python/essa/*` remains the default. The new SSA adapter is exposed for tests and for the upcoming dataflow migration.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* @name Print CFG (New)
|
||||
* @description Produces a representation of a file's Control Flow Graph
|
||||
* using the new shared control flow library.
|
||||
* This query is used by the VS Code extension.
|
||||
* @id python/print-cfg
|
||||
* @kind graph
|
||||
* @tags ide-contextual-queries/print-cfg
|
||||
*/
|
||||
|
||||
private import python as Py
|
||||
import semmle.python.controlflow.internal.AstNodeImpl
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
private predicate selectedSourceFileAlias = selectedSourceFile/0;
|
||||
|
||||
external int selectedSourceLine();
|
||||
|
||||
private predicate selectedSourceLineAlias = selectedSourceLine/0;
|
||||
|
||||
external int selectedSourceColumn();
|
||||
|
||||
private predicate selectedSourceColumnAlias = selectedSourceColumn/0;
|
||||
|
||||
module ViewCfgQueryInput implements ControlFlow::ViewCfgQueryInputSig<Py::File> {
|
||||
predicate selectedSourceFile = selectedSourceFileAlias/0;
|
||||
|
||||
predicate selectedSourceLine = selectedSourceLineAlias/0;
|
||||
|
||||
predicate selectedSourceColumn = selectedSourceColumnAlias/0;
|
||||
|
||||
predicate cfgScopeSpan(
|
||||
Ast::Callable callable, Py::File file, int startLine, int startColumn, int endLine,
|
||||
int endColumn
|
||||
) {
|
||||
exists(Py::Scope scope |
|
||||
scope = callable.asScope() and
|
||||
file = scope.getLocation().getFile() and
|
||||
scope.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import ControlFlow::ViewCfgQuery<Py::File, ViewCfgQueryInput>
|
||||
@@ -16,26 +16,21 @@ abstract class AstNode extends AstNode_ {
|
||||
/** Gets the scope that this node occurs in */
|
||||
abstract Scope getScope();
|
||||
|
||||
/** Gets the location for this AST node */
|
||||
cached
|
||||
Location getLocation() { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead;
|
||||
* that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is
|
||||
* being removed to untangle the AST and CFG hierarchies in preparation for
|
||||
* migrating the dataflow library off the legacy CFG.
|
||||
*
|
||||
* Gets a flow node corresponding directly to this node.
|
||||
* NOTE: For some statements and other purely syntactic elements,
|
||||
* there may not be a `ControlFlowNode`.
|
||||
* there may not be a `ControlFlowNode`
|
||||
*/
|
||||
cached
|
||||
deprecated ControlFlowNode getAFlowNode() {
|
||||
ControlFlowNode getAFlowNode() {
|
||||
Stages::AST::ref() and
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
/** Gets the location for this AST node */
|
||||
cached
|
||||
Location getLocation() { none() }
|
||||
|
||||
/**
|
||||
* Whether this syntactic element is artificial, that is it is generated
|
||||
* by the compiler and is not present in the source
|
||||
|
||||
@@ -28,9 +28,7 @@ class Expr extends Expr_, AstNode {
|
||||
/** Whether this expression may have a side effect (as determined purely from its syntax) */
|
||||
predicate hasSideEffects() {
|
||||
/* If an exception raised by this expression handled, count that as a side effect */
|
||||
exists(ControlFlowNode n | n.getNode() = this |
|
||||
n.getASuccessor().getNode() instanceof ExceptStmt
|
||||
)
|
||||
this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt
|
||||
or
|
||||
this.getASubExpression().hasSideEffects()
|
||||
}
|
||||
@@ -70,6 +68,8 @@ class Attribute extends Attribute_ {
|
||||
/* syntax: Expr.name */
|
||||
override Expr getASubExpression() { result = this.getObject() }
|
||||
|
||||
override AttrNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
/** Gets the name of this attribute. That is the `name` in `obj.name` */
|
||||
string getName() { result = Attribute_.super.getAttr() }
|
||||
|
||||
@@ -96,6 +96,8 @@ class Subscript extends Subscript_ {
|
||||
}
|
||||
|
||||
Expr getObject() { result = Subscript_.super.getValue() }
|
||||
|
||||
override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A call expression, such as `func(...)` */
|
||||
@@ -111,6 +113,8 @@ class Call extends Call_ {
|
||||
|
||||
override string toString() { result = this.getFunc().toString() + "()" }
|
||||
|
||||
override CallNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
/** Gets a tuple (*) argument of this call. */
|
||||
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
|
||||
|
||||
@@ -196,6 +200,8 @@ class IfExp extends IfExp_ {
|
||||
override Expr getASubExpression() {
|
||||
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
|
||||
}
|
||||
|
||||
override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
|
||||
@@ -404,6 +410,8 @@ class PlaceHolder extends PlaceHolder_ {
|
||||
override Expr getASubExpression() { none() }
|
||||
|
||||
override string toString() { result = "$" + this.getId() }
|
||||
|
||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
||||
@@ -470,6 +478,8 @@ class Name extends Name_ {
|
||||
|
||||
override string toString() { result = this.getId() }
|
||||
|
||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
override predicate isArtificial() {
|
||||
/* Artificial variable names in comprehensions all start with "." */
|
||||
this.getId().charAt(0) = "."
|
||||
@@ -575,6 +585,8 @@ abstract class NameConstant extends Name, ImmutableLiteral {
|
||||
|
||||
override predicate isConstant() { any() }
|
||||
|
||||
override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
|
||||
|
||||
override predicate isArtificial() { none() }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import python as Py
|
||||
import python
|
||||
private import semmle.python.internal.CachedStages
|
||||
private import codeql.controlflow.BasicBlock as BB
|
||||
|
||||
@@ -17,7 +17,7 @@ private import codeql.controlflow.BasicBlock as BB
|
||||
*/
|
||||
|
||||
private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
|
||||
exists(Py::Expr load_store | exists(Py::AugAssign aa | aa.getTarget() = load_store) |
|
||||
exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) |
|
||||
toAst(load) = load_store and
|
||||
toAst(store) = load_store and
|
||||
load.strictlyDominates(store)
|
||||
@@ -25,7 +25,7 @@ private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
|
||||
}
|
||||
|
||||
/** A non-dispatched getNode() to avoid negative recursion issues */
|
||||
private Py::AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
|
||||
private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
|
||||
|
||||
/**
|
||||
* A control flow node. Control flow nodes have a many-to-one relation with syntactic nodes,
|
||||
@@ -35,19 +35,19 @@ private Py::AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _)
|
||||
class ControlFlowNode extends @py_flow_node {
|
||||
/** Whether this control flow node is a load (including those in augmented assignments) */
|
||||
predicate isLoad() {
|
||||
exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
|
||||
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
|
||||
}
|
||||
|
||||
/** Whether this control flow node is a store (including those in augmented assignments) */
|
||||
predicate isStore() {
|
||||
exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
|
||||
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
|
||||
}
|
||||
|
||||
/** Whether this control flow node is a delete */
|
||||
predicate isDelete() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
|
||||
predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
|
||||
|
||||
/** Whether this control flow node is a parameter */
|
||||
predicate isParameter() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
|
||||
predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
|
||||
|
||||
/** Whether this control flow node is a store in an augmented assignment */
|
||||
predicate isAugStore() { augstore(_, this) }
|
||||
@@ -57,61 +57,61 @@ class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
/** Whether this flow node corresponds to a literal */
|
||||
predicate isLiteral() {
|
||||
toAst(this) instanceof Py::Bytes
|
||||
toAst(this) instanceof Bytes
|
||||
or
|
||||
toAst(this) instanceof Py::Dict
|
||||
toAst(this) instanceof Dict
|
||||
or
|
||||
toAst(this) instanceof Py::DictComp
|
||||
toAst(this) instanceof DictComp
|
||||
or
|
||||
toAst(this) instanceof Py::Set
|
||||
toAst(this) instanceof Set
|
||||
or
|
||||
toAst(this) instanceof Py::SetComp
|
||||
toAst(this) instanceof SetComp
|
||||
or
|
||||
toAst(this) instanceof Py::Ellipsis
|
||||
toAst(this) instanceof Ellipsis
|
||||
or
|
||||
toAst(this) instanceof Py::GeneratorExp
|
||||
toAst(this) instanceof GeneratorExp
|
||||
or
|
||||
toAst(this) instanceof Py::Lambda
|
||||
toAst(this) instanceof Lambda
|
||||
or
|
||||
toAst(this) instanceof Py::ListComp
|
||||
toAst(this) instanceof ListComp
|
||||
or
|
||||
toAst(this) instanceof Py::List
|
||||
toAst(this) instanceof List
|
||||
or
|
||||
toAst(this) instanceof Py::Num
|
||||
toAst(this) instanceof Num
|
||||
or
|
||||
toAst(this) instanceof Py::Tuple
|
||||
toAst(this) instanceof Tuple
|
||||
or
|
||||
toAst(this) instanceof Py::Unicode
|
||||
toAst(this) instanceof Unicode
|
||||
or
|
||||
toAst(this) instanceof Py::NameConstant
|
||||
toAst(this) instanceof NameConstant
|
||||
}
|
||||
|
||||
/** Whether this flow node corresponds to an attribute expression */
|
||||
predicate isAttribute() { toAst(this) instanceof Py::Attribute }
|
||||
predicate isAttribute() { toAst(this) instanceof Attribute }
|
||||
|
||||
/** Whether this flow node corresponds to an subscript expression */
|
||||
predicate isSubscript() { toAst(this) instanceof Py::Subscript }
|
||||
predicate isSubscript() { toAst(this) instanceof Subscript }
|
||||
|
||||
/** Whether this flow node corresponds to an import member */
|
||||
predicate isImportMember() { toAst(this) instanceof Py::ImportMember }
|
||||
predicate isImportMember() { toAst(this) instanceof ImportMember }
|
||||
|
||||
/** Whether this flow node corresponds to a call */
|
||||
predicate isCall() { toAst(this) instanceof Py::Call }
|
||||
predicate isCall() { toAst(this) instanceof Call }
|
||||
|
||||
/** Whether this flow node is the first in a module */
|
||||
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Py::Module }
|
||||
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module }
|
||||
|
||||
/** Whether this flow node corresponds to an import */
|
||||
predicate isImport() { toAst(this) instanceof Py::ImportExpr }
|
||||
predicate isImport() { toAst(this) instanceof ImportExpr }
|
||||
|
||||
/** Whether this flow node corresponds to a conditional expression */
|
||||
predicate isIfExp() { toAst(this) instanceof Py::IfExp }
|
||||
predicate isIfExp() { toAst(this) instanceof IfExp }
|
||||
|
||||
/** Whether this flow node corresponds to a function definition expression */
|
||||
predicate isFunction() { toAst(this) instanceof Py::FunctionExpr }
|
||||
predicate isFunction() { toAst(this) instanceof FunctionExpr }
|
||||
|
||||
/** Whether this flow node corresponds to a class definition expression */
|
||||
predicate isClass() { toAst(this) instanceof Py::ClassExpr }
|
||||
predicate isClass() { toAst(this) instanceof ClassExpr }
|
||||
|
||||
/** Gets a predecessor of this flow node */
|
||||
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
|
||||
@@ -123,25 +123,25 @@ class ControlFlowNode extends @py_flow_node {
|
||||
ControlFlowNode getImmediateDominator() { py_idoms(this, result) }
|
||||
|
||||
/** Gets the syntactic element corresponding to this flow node */
|
||||
Py::AstNode getNode() { py_flow_bb_node(this, result, _, _) }
|
||||
AstNode getNode() { py_flow_bb_node(this, result, _, _) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
cached
|
||||
string toString() {
|
||||
Stages::AST::ref() and
|
||||
// Since modules can have ambigous names, entry nodes can too, if we do not collate them.
|
||||
exists(Py::Scope s | s.getEntryNode() = this |
|
||||
exists(Scope s | s.getEntryNode() = this |
|
||||
result = "Entry node for " + concat( | | s.toString(), ",")
|
||||
)
|
||||
or
|
||||
exists(Py::Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
|
||||
exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
|
||||
or
|
||||
not exists(Py::Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
|
||||
not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
|
||||
result = "ControlFlowNode for " + this.getNode().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this ControlFlowNode */
|
||||
Py::Location getLocation() { result = this.getNode().getLocation() }
|
||||
Location getLocation() { result = this.getNode().getLocation() }
|
||||
|
||||
/** Whether this flow node is the first in its scope */
|
||||
predicate isEntryNode() { py_scope_flow(this, _, -1) }
|
||||
@@ -151,9 +151,9 @@ class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
/** Gets the scope containing this flow node */
|
||||
cached
|
||||
Py::Scope getScope() {
|
||||
Scope getScope() {
|
||||
Stages::AST::ref() and
|
||||
if this.getNode() instanceof Py::Scope
|
||||
if this.getNode() instanceof Scope
|
||||
then
|
||||
/* Entry or exit node */
|
||||
result = this.getNode()
|
||||
@@ -161,7 +161,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
}
|
||||
|
||||
/** Gets the enclosing module */
|
||||
Py::Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
|
||||
Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
|
||||
|
||||
/** Gets a successor for this node if the relevant condition is True. */
|
||||
ControlFlowNode getATrueSuccessor() {
|
||||
@@ -188,7 +188,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
}
|
||||
|
||||
/** Whether the scope may be exited as a result of this node raising an exception */
|
||||
predicate isExceptionalExit(Py::Scope s) { py_scope_flow(this, s, 1) }
|
||||
predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) }
|
||||
|
||||
/** Whether this node is a normal (non-exceptional) exit */
|
||||
predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) }
|
||||
@@ -236,7 +236,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
/* join-ordering helper for `getAChild() */
|
||||
pragma[noinline]
|
||||
private ControlFlowNode getExprChild(BasicBlock dom) {
|
||||
this.getNode().(Py::Expr).getAChildNode() = result.getNode() and
|
||||
this.getNode().(Expr).getAChildNode() = result.getNode() and
|
||||
result.getBasicBlock().dominates(dom) and
|
||||
not this instanceof UnaryExprNode
|
||||
}
|
||||
@@ -249,16 +249,16 @@ class ControlFlowNode extends @py_flow_node {
|
||||
*/
|
||||
|
||||
private class AnyNode extends ControlFlowNode {
|
||||
override Py::AstNode getNode() { result = super.getNode() }
|
||||
override AstNode getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a call expression, such as `func(...)` */
|
||||
class CallNode extends ControlFlowNode {
|
||||
CallNode() { toAst(this) instanceof Py::Call }
|
||||
CallNode() { toAst(this) instanceof Call }
|
||||
|
||||
/** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */
|
||||
ControlFlowNode getFunction() {
|
||||
exists(Py::Call c |
|
||||
exists(Call c |
|
||||
this.getNode() = c and
|
||||
c.getFunc() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -267,7 +267,7 @@ class CallNode extends ControlFlowNode {
|
||||
|
||||
/** Gets the flow node corresponding to the n'th positional argument of the call corresponding to this flow node */
|
||||
ControlFlowNode getArg(int n) {
|
||||
exists(Py::Call c |
|
||||
exists(Call c |
|
||||
this.getNode() = c and
|
||||
c.getArg(n) = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -276,7 +276,7 @@ class CallNode extends ControlFlowNode {
|
||||
|
||||
/** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */
|
||||
ControlFlowNode getArgByName(string name) {
|
||||
exists(Py::Call c, Py::Keyword k |
|
||||
exists(Call c, Keyword k |
|
||||
this.getNode() = c and
|
||||
k = c.getANamedArg() and
|
||||
k.getValue() = result.getNode() and
|
||||
@@ -292,7 +292,7 @@ class CallNode extends ControlFlowNode {
|
||||
result = this.getArgByName(_)
|
||||
}
|
||||
|
||||
override Py::Call getNode() { result = super.getNode() }
|
||||
override Call getNode() { result = super.getNode() }
|
||||
|
||||
predicate isDecoratorCall() {
|
||||
this.isClassDecoratorCall()
|
||||
@@ -301,11 +301,11 @@ class CallNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
predicate isClassDecoratorCall() {
|
||||
exists(Py::ClassExpr cls | this.getNode() = cls.getADecoratorCall())
|
||||
exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall())
|
||||
}
|
||||
|
||||
predicate isFunctionDecoratorCall() {
|
||||
exists(Py::FunctionExpr func | this.getNode() = func.getADecoratorCall())
|
||||
exists(FunctionExpr func | this.getNode() = func.getADecoratorCall())
|
||||
}
|
||||
|
||||
/** Gets the first tuple (*) argument of this call, if any. */
|
||||
@@ -323,11 +323,11 @@ class CallNode extends ControlFlowNode {
|
||||
|
||||
/** A control flow corresponding to an attribute expression, such as `value.attr` */
|
||||
class AttrNode extends ControlFlowNode {
|
||||
AttrNode() { toAst(this) instanceof Py::Attribute }
|
||||
AttrNode() { toAst(this) instanceof Attribute }
|
||||
|
||||
/** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */
|
||||
ControlFlowNode getObject() {
|
||||
exists(Py::Attribute a |
|
||||
exists(Attribute a |
|
||||
this.getNode() = a and
|
||||
a.getObject() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -339,7 +339,7 @@ class AttrNode extends ControlFlowNode {
|
||||
* with the matching name
|
||||
*/
|
||||
ControlFlowNode getObject(string name) {
|
||||
exists(Py::Attribute a |
|
||||
exists(Attribute a |
|
||||
this.getNode() = a and
|
||||
a.getObject() = result.getNode() and
|
||||
a.getName() = name and
|
||||
@@ -348,57 +348,57 @@ class AttrNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
/** Gets the attribute name of the attribute expression corresponding to this flow node */
|
||||
string getName() { exists(Py::Attribute a | this.getNode() = a and a.getName() = result) }
|
||||
string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) }
|
||||
|
||||
override Py::Attribute getNode() { result = super.getNode() }
|
||||
override Attribute getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a `from ... import ...` expression */
|
||||
class ImportMemberNode extends ControlFlowNode {
|
||||
ImportMemberNode() { toAst(this) instanceof Py::ImportMember }
|
||||
ImportMemberNode() { toAst(this) instanceof ImportMember }
|
||||
|
||||
/**
|
||||
* Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node,
|
||||
* with the matching name
|
||||
*/
|
||||
ControlFlowNode getModule(string name) {
|
||||
exists(Py::ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
|
||||
exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
|
||||
i.getName() = name and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override Py::ImportMember getNode() { result = super.getNode() }
|
||||
override ImportMember getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to an artificial expression representing an import */
|
||||
class ImportExprNode extends ControlFlowNode {
|
||||
ImportExprNode() { toAst(this) instanceof Py::ImportExpr }
|
||||
ImportExprNode() { toAst(this) instanceof ImportExpr }
|
||||
|
||||
override Py::ImportExpr getNode() { result = super.getNode() }
|
||||
override ImportExpr getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a `from ... import *` statement */
|
||||
class ImportStarNode extends ControlFlowNode {
|
||||
ImportStarNode() { toAst(this) instanceof Py::ImportStar }
|
||||
ImportStarNode() { toAst(this) instanceof ImportStar }
|
||||
|
||||
/** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */
|
||||
ControlFlowNode getModule() {
|
||||
exists(Py::ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
|
||||
exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override Py::ImportStar getNode() { result = super.getNode() }
|
||||
override ImportStar getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a subscript expression, such as `value[slice]` */
|
||||
class SubscriptNode extends ControlFlowNode {
|
||||
SubscriptNode() { toAst(this) instanceof Py::Subscript }
|
||||
SubscriptNode() { toAst(this) instanceof Subscript }
|
||||
|
||||
/** flow node corresponding to the value of the sequence in a subscript operation */
|
||||
ControlFlowNode getObject() {
|
||||
exists(Py::Subscript s |
|
||||
exists(Subscript s |
|
||||
this.getNode() = s and
|
||||
s.getObject() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -407,23 +407,23 @@ class SubscriptNode extends ControlFlowNode {
|
||||
|
||||
/** flow node corresponding to the index in a subscript operation */
|
||||
ControlFlowNode getIndex() {
|
||||
exists(Py::Subscript s |
|
||||
exists(Subscript s |
|
||||
this.getNode() = s and
|
||||
s.getIndex() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override Py::Subscript getNode() { result = super.getNode() }
|
||||
override Subscript getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a comparison operation, such as `x<y` */
|
||||
class CompareNode extends ControlFlowNode {
|
||||
CompareNode() { toAst(this) instanceof Py::Compare }
|
||||
CompareNode() { toAst(this) instanceof Compare }
|
||||
|
||||
/** Whether left and right are a pair of operands for this comparison */
|
||||
predicate operands(ControlFlowNode left, Py::Cmpop op, ControlFlowNode right) {
|
||||
exists(Py::Compare c, Py::Expr eleft, Py::Expr eright |
|
||||
predicate operands(ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(Compare c, Expr eleft, Expr eright |
|
||||
this.getNode() = c and left.getNode() = eleft and right.getNode() = eright
|
||||
|
|
||||
eleft = c.getLeft() and eright = c.getComparator(0) and op = c.getOp(0)
|
||||
@@ -436,26 +436,26 @@ class CompareNode extends ControlFlowNode {
|
||||
right.getBasicBlock().dominates(this.getBasicBlock())
|
||||
}
|
||||
|
||||
override Py::Compare getNode() { result = super.getNode() }
|
||||
override Compare getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a conditional expression such as, `body if test else orelse` */
|
||||
class IfExprNode extends ControlFlowNode {
|
||||
IfExprNode() { toAst(this) instanceof Py::IfExp }
|
||||
IfExprNode() { toAst(this) instanceof IfExp }
|
||||
|
||||
/** flow node corresponding to one of the operands of an if-expression */
|
||||
ControlFlowNode getAnOperand() { result = this.getAPredecessor() }
|
||||
|
||||
override Py::IfExp getNode() { result = super.getNode() }
|
||||
override IfExp getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */
|
||||
class AssignmentExprNode extends ControlFlowNode {
|
||||
AssignmentExprNode() { toAst(this) instanceof Py::AssignExpr }
|
||||
AssignmentExprNode() { toAst(this) instanceof AssignExpr }
|
||||
|
||||
/** Gets the flow node corresponding to the left-hand side of the assignment expression */
|
||||
ControlFlowNode getTarget() {
|
||||
exists(Py::AssignExpr a |
|
||||
exists(AssignExpr a |
|
||||
this.getNode() = a and
|
||||
a.getTarget() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -464,27 +464,27 @@ class AssignmentExprNode extends ControlFlowNode {
|
||||
|
||||
/** Gets the flow node corresponding to the right-hand side of the assignment expression */
|
||||
ControlFlowNode getValue() {
|
||||
exists(Py::AssignExpr a |
|
||||
exists(AssignExpr a |
|
||||
this.getNode() = a and
|
||||
a.getValue() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override Py::AssignExpr getNode() { result = super.getNode() }
|
||||
override AssignExpr getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a binary expression, such as `x + y` */
|
||||
class BinaryExprNode extends ControlFlowNode {
|
||||
BinaryExprNode() { toAst(this) instanceof Py::BinaryExpr }
|
||||
BinaryExprNode() { toAst(this) instanceof BinaryExpr }
|
||||
|
||||
/** flow node corresponding to one of the operands of a binary expression */
|
||||
ControlFlowNode getAnOperand() { result = this.getLeft() or result = this.getRight() }
|
||||
|
||||
override Py::BinaryExpr getNode() { result = super.getNode() }
|
||||
override BinaryExpr getNode() { result = super.getNode() }
|
||||
|
||||
ControlFlowNode getLeft() {
|
||||
exists(Py::BinaryExpr b |
|
||||
exists(BinaryExpr b |
|
||||
this.getNode() = b and
|
||||
result.getNode() = b.getLeft() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -492,7 +492,7 @@ class BinaryExprNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
ControlFlowNode getRight() {
|
||||
exists(Py::BinaryExpr b |
|
||||
exists(BinaryExpr b |
|
||||
this.getNode() = b and
|
||||
result.getNode() = b.getRight() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -500,11 +500,11 @@ class BinaryExprNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
/** Gets the operator of this binary expression node. */
|
||||
Py::Operator getOp() { result = this.getNode().getOp() }
|
||||
Operator getOp() { result = this.getNode().getOp() }
|
||||
|
||||
/** Whether left and right are a pair of operands for this binary expression */
|
||||
predicate operands(ControlFlowNode left, Py::Operator op, ControlFlowNode right) {
|
||||
exists(Py::BinaryExpr b, Py::Expr eleft, Py::Expr eright |
|
||||
predicate operands(ControlFlowNode left, Operator op, ControlFlowNode right) {
|
||||
exists(BinaryExpr b, Expr eleft, Expr eright |
|
||||
this.getNode() = b and left.getNode() = eleft and right.getNode() = eright
|
||||
|
|
||||
eleft = b.getLeft() and eright = b.getRight() and op = b.getOp()
|
||||
@@ -516,20 +516,20 @@ class BinaryExprNode extends ControlFlowNode {
|
||||
|
||||
/** A control flow node corresponding to a boolean shortcut (and/or) operation */
|
||||
class BoolExprNode extends ControlFlowNode {
|
||||
BoolExprNode() { toAst(this) instanceof Py::BoolExpr }
|
||||
BoolExprNode() { toAst(this) instanceof BoolExpr }
|
||||
|
||||
/** flow node corresponding to one of the operands of a boolean expression */
|
||||
ControlFlowNode getAnOperand() {
|
||||
exists(Py::BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
|
||||
exists(BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
|
||||
this.getBasicBlock().dominates(result.getBasicBlock())
|
||||
}
|
||||
|
||||
override Py::BoolExpr getNode() { result = super.getNode() }
|
||||
override BoolExpr getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a unary expression: (`+x`), (`-x`) or (`~x`) */
|
||||
class UnaryExprNode extends ControlFlowNode {
|
||||
UnaryExprNode() { toAst(this) instanceof Py::UnaryExpr }
|
||||
UnaryExprNode() { toAst(this) instanceof UnaryExpr }
|
||||
|
||||
/**
|
||||
* Gets flow node corresponding to the operand of a unary expression.
|
||||
@@ -540,7 +540,7 @@ class UnaryExprNode extends ControlFlowNode {
|
||||
*/
|
||||
ControlFlowNode getOperand() { result = this.getAPredecessor() }
|
||||
|
||||
override Py::UnaryExpr getNode() { result = super.getNode() }
|
||||
override UnaryExpr getNode() { result = super.getNode() }
|
||||
|
||||
override ControlFlowNode getAChild() { result = this.getAPredecessor() }
|
||||
}
|
||||
@@ -555,27 +555,27 @@ class DefinitionNode extends ControlFlowNode {
|
||||
cached
|
||||
DefinitionNode() {
|
||||
Stages::AST::ref() and
|
||||
exists(Py::Assign a | this.getNode() = a.getATarget())
|
||||
exists(Assign a | a.getATarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(Py::AssignExpr a | this.getNode() = a.getTarget())
|
||||
exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(Py::AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue()))
|
||||
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
|
||||
or
|
||||
exists(Py::Alias a | this.getNode() = a.getAsname())
|
||||
exists(Alias a | a.getAsname().getAFlowNode() = this)
|
||||
or
|
||||
augstore(_, this)
|
||||
or
|
||||
// `x, y = 1, 2` where LHS is a combination of list or tuples
|
||||
exists(Py::Assign a | this.getNode() = list_or_tuple_nested_element(a.getATarget()))
|
||||
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
|
||||
or
|
||||
exists(Py::For for | this.getNode() = for.getTarget())
|
||||
exists(For for | for.getTarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(Py::Parameter param | this.getNode() = param.asName() and exists(param.getDefault()))
|
||||
exists(Parameter param | this = param.asName().getAFlowNode() and exists(param.getDefault()))
|
||||
}
|
||||
|
||||
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
|
||||
ControlFlowNode getValue() {
|
||||
result.getNode() = assigned_value(this.getNode()) and
|
||||
result = assigned_value(this.getNode()).getAFlowNode() and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -584,16 +584,16 @@ class DefinitionNode extends ControlFlowNode {
|
||||
// since the default value for a parameter is evaluated in the same basic block as
|
||||
// the function definition, but the parameter belongs to the basic block of the function,
|
||||
// there is no dominance relationship between the two.
|
||||
exists(Py::Parameter param | this.getNode() = param.asName())
|
||||
exists(Parameter param | this = param.asName().getAFlowNode())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private Py::Expr list_or_tuple_nested_element(Py::Expr list_or_tuple) {
|
||||
exists(Py::Expr elt |
|
||||
elt = list_or_tuple.(Py::Tuple).getAnElt()
|
||||
private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
|
||||
exists(Expr elt |
|
||||
elt = list_or_tuple.(Tuple).getAnElt()
|
||||
or
|
||||
elt = list_or_tuple.(Py::List).getAnElt()
|
||||
elt = list_or_tuple.(List).getAnElt()
|
||||
|
|
||||
result = elt
|
||||
or
|
||||
@@ -603,12 +603,12 @@ private Py::Expr list_or_tuple_nested_element(Py::Expr list_or_tuple) {
|
||||
|
||||
/**
|
||||
* A control flow node corresponding to a deletion statement, such as `del x`.
|
||||
* There can be multiple `DeletionNode`s for each `Py::Delete` such that each
|
||||
* There can be multiple `DeletionNode`s for each `Delete` such that each
|
||||
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
|
||||
* `NameNode('a') -> DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`.
|
||||
*/
|
||||
class DeletionNode extends ControlFlowNode {
|
||||
DeletionNode() { toAst(this) instanceof Py::Delete }
|
||||
DeletionNode() { toAst(this) instanceof Delete }
|
||||
|
||||
/** Gets the unique target of this deletion node. */
|
||||
ControlFlowNode getTarget() { result.getASuccessor() = this }
|
||||
@@ -617,9 +617,9 @@ class DeletionNode extends ControlFlowNode {
|
||||
/** A control flow node corresponding to a sequence (tuple or list) literal */
|
||||
abstract class SequenceNode extends ControlFlowNode {
|
||||
SequenceNode() {
|
||||
toAst(this) instanceof Py::Tuple
|
||||
toAst(this) instanceof Tuple
|
||||
or
|
||||
toAst(this) instanceof Py::List
|
||||
toAst(this) instanceof List
|
||||
}
|
||||
|
||||
/** Gets the control flow node for an element of this sequence */
|
||||
@@ -632,11 +632,11 @@ abstract class SequenceNode extends ControlFlowNode {
|
||||
|
||||
/** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
||||
class TupleNode extends SequenceNode {
|
||||
TupleNode() { toAst(this) instanceof Py::Tuple }
|
||||
TupleNode() { toAst(this) instanceof Tuple }
|
||||
|
||||
override ControlFlowNode getElement(int n) {
|
||||
Stages::AST::ref() and
|
||||
exists(Py::Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
|
||||
exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -647,10 +647,10 @@ class TupleNode extends SequenceNode {
|
||||
|
||||
/** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */
|
||||
class ListNode extends SequenceNode {
|
||||
ListNode() { toAst(this) instanceof Py::List }
|
||||
ListNode() { toAst(this) instanceof List }
|
||||
|
||||
override ControlFlowNode getElement(int n) {
|
||||
exists(Py::List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
|
||||
exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -661,10 +661,10 @@ class ListNode extends SequenceNode {
|
||||
|
||||
/** A control flow node corresponding to a set expression, such as `{ 1, 3, 5, 7, 9 }` */
|
||||
class SetNode extends ControlFlowNode {
|
||||
SetNode() { toAst(this) instanceof Py::Set }
|
||||
SetNode() { toAst(this) instanceof Set }
|
||||
|
||||
ControlFlowNode getAnElement() {
|
||||
exists(Py::Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
|
||||
exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -675,20 +675,20 @@ class SetNode extends ControlFlowNode {
|
||||
|
||||
/** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */
|
||||
class DictNode extends ControlFlowNode {
|
||||
DictNode() { toAst(this) instanceof Py::Dict }
|
||||
DictNode() { toAst(this) instanceof Dict }
|
||||
|
||||
/**
|
||||
* Gets a key of this dictionary literal node, for those items that have keys
|
||||
* E.g, in {'a':1, **b} this returns only 'a'
|
||||
*/
|
||||
ControlFlowNode getAKey() {
|
||||
exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
|
||||
exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
}
|
||||
|
||||
/** Gets a value of this dictionary literal node */
|
||||
ControlFlowNode getAValue() {
|
||||
exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
|
||||
exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
}
|
||||
}
|
||||
@@ -712,23 +712,21 @@ class IterableNode extends ControlFlowNode {
|
||||
}
|
||||
}
|
||||
|
||||
private Py::AstNode assigned_value(Py::Expr lhs) {
|
||||
private AstNode assigned_value(Expr lhs) {
|
||||
/* lhs = result */
|
||||
exists(Py::Assign a | a.getATarget() = lhs and result = a.getValue())
|
||||
exists(Assign a | a.getATarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs := result */
|
||||
exists(Py::AssignExpr a | a.getTarget() = lhs and result = a.getValue())
|
||||
exists(AssignExpr a | a.getTarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs : annotation = result */
|
||||
exists(Py::AnnAssign a | a.getTarget() = lhs and result = a.getValue())
|
||||
exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* import result as lhs */
|
||||
exists(Py::Alias a | a.getAsname() = lhs and result = a.getValue())
|
||||
exists(Alias a | a.getAsname() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs += x => result = (lhs + x) */
|
||||
exists(Py::AugAssign a, Py::BinaryExpr b |
|
||||
b = a.getOperation() and result = b and lhs = b.getLeft()
|
||||
)
|
||||
exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
|
||||
or
|
||||
/*
|
||||
* ..., lhs, ... = ..., result, ...
|
||||
@@ -736,31 +734,31 @@ private Py::AstNode assigned_value(Py::Expr lhs) {
|
||||
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
|
||||
*/
|
||||
|
||||
exists(Py::Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
|
||||
exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
|
||||
or
|
||||
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
|
||||
result.(Py::For).getTarget() = lhs
|
||||
result.(For).getTarget() = lhs
|
||||
or
|
||||
exists(Py::Parameter param | lhs = param.asName() and result = param.getDefault())
|
||||
exists(Parameter param | lhs = param.asName() and result = param.getDefault())
|
||||
}
|
||||
|
||||
predicate nested_sequence_assign(
|
||||
Py::Expr left_parent, Py::Expr right_parent, Py::Expr left_result, Py::Expr right_result
|
||||
Expr left_parent, Expr right_parent, Expr left_result, Expr right_result
|
||||
) {
|
||||
exists(Py::Assign a |
|
||||
exists(Assign a |
|
||||
a.getATarget().getASubExpression*() = left_parent and
|
||||
a.getValue().getASubExpression*() = right_parent
|
||||
) and
|
||||
exists(int i, Py::Expr left_elem, Py::Expr right_elem |
|
||||
exists(int i, Expr left_elem, Expr right_elem |
|
||||
(
|
||||
left_elem = left_parent.(Py::Tuple).getElt(i)
|
||||
left_elem = left_parent.(Tuple).getElt(i)
|
||||
or
|
||||
left_elem = left_parent.(Py::List).getElt(i)
|
||||
left_elem = left_parent.(List).getElt(i)
|
||||
) and
|
||||
(
|
||||
right_elem = right_parent.(Py::Tuple).getElt(i)
|
||||
right_elem = right_parent.(Tuple).getElt(i)
|
||||
or
|
||||
right_elem = right_parent.(Py::List).getElt(i)
|
||||
right_elem = right_parent.(List).getElt(i)
|
||||
)
|
||||
|
|
||||
left_result = left_elem and right_result = right_elem
|
||||
@@ -771,9 +769,9 @@ predicate nested_sequence_assign(
|
||||
|
||||
/** A flow node for a `for` statement. */
|
||||
class ForNode extends ControlFlowNode {
|
||||
ForNode() { toAst(this) instanceof Py::For }
|
||||
ForNode() { toAst(this) instanceof For }
|
||||
|
||||
override Py::For getNode() { result = super.getNode() }
|
||||
override For getNode() { result = super.getNode() }
|
||||
|
||||
/** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */
|
||||
predicate iterates(ControlFlowNode target, ControlFlowNode sequence) {
|
||||
@@ -784,7 +782,7 @@ class ForNode extends ControlFlowNode {
|
||||
|
||||
/** Gets the sequence node for this `for` statement. */
|
||||
ControlFlowNode getSequence() {
|
||||
exists(Py::For for |
|
||||
exists(For for |
|
||||
toAst(this) = for and
|
||||
for.getIter() = result.getNode()
|
||||
|
|
||||
@@ -794,7 +792,7 @@ class ForNode extends ControlFlowNode {
|
||||
|
||||
/** A possible `target` for this `for` statement, not accounting for loop unrolling */
|
||||
private ControlFlowNode possibleTarget() {
|
||||
exists(Py::For for |
|
||||
exists(For for |
|
||||
toAst(this) = for and
|
||||
for.getTarget() = result.getNode() and
|
||||
this.getBasicBlock().dominates(result.getBasicBlock())
|
||||
@@ -811,11 +809,11 @@ class ForNode extends ControlFlowNode {
|
||||
|
||||
/** A flow node for a `raise` statement */
|
||||
class RaiseStmtNode extends ControlFlowNode {
|
||||
RaiseStmtNode() { toAst(this) instanceof Py::Raise }
|
||||
RaiseStmtNode() { toAst(this) instanceof Raise }
|
||||
|
||||
/** Gets the control flow node for the exception raised by this raise statement */
|
||||
ControlFlowNode getException() {
|
||||
exists(Py::Raise r |
|
||||
exists(Raise r |
|
||||
r = toAst(this) and
|
||||
r.getException() = toAst(result) and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -829,36 +827,36 @@ class RaiseStmtNode extends ControlFlowNode {
|
||||
*/
|
||||
class NameNode extends ControlFlowNode {
|
||||
NameNode() {
|
||||
exists(Py::Name n | py_flow_bb_node(this, n, _, _))
|
||||
exists(Name n | py_flow_bb_node(this, n, _, _))
|
||||
or
|
||||
exists(Py::PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||
}
|
||||
|
||||
/** Whether this flow node defines the variable `v`. */
|
||||
predicate defines(Py::Variable v) {
|
||||
exists(Py::Name d | this.getNode() = d and d.defines(v)) and
|
||||
predicate defines(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.defines(v)) and
|
||||
not this.isLoad()
|
||||
}
|
||||
|
||||
/** Whether this flow node deletes the variable `v`. */
|
||||
predicate deletes(Py::Variable v) { exists(Py::Name d | this.getNode() = d and d.deletes(v)) }
|
||||
predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) }
|
||||
|
||||
/** Whether this flow node uses the variable `v`. */
|
||||
predicate uses(Py::Variable v) {
|
||||
predicate uses(Variable v) {
|
||||
this.isLoad() and
|
||||
exists(Py::Name u | this.getNode() = u and u.uses(v))
|
||||
exists(Name u | this.getNode() = u and u.uses(v))
|
||||
or
|
||||
exists(Py::PlaceHolder u |
|
||||
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Py::Load
|
||||
exists(PlaceHolder u |
|
||||
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load
|
||||
)
|
||||
or
|
||||
Scopes::use_of_global_variable(this, v.getScope(), v.getId())
|
||||
}
|
||||
|
||||
string getId() {
|
||||
result = this.getNode().(Py::Name).getId()
|
||||
result = this.getNode().(Name).getId()
|
||||
or
|
||||
result = this.getNode().(Py::PlaceHolder).getId()
|
||||
result = this.getNode().(PlaceHolder).getId()
|
||||
}
|
||||
|
||||
/** Whether this is a use of a local variable. */
|
||||
@@ -870,84 +868,82 @@ class NameNode extends ControlFlowNode {
|
||||
/** Whether this is a use of a global (including builtin) variable. */
|
||||
predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) }
|
||||
|
||||
predicate isSelf() {
|
||||
exists(Py::SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this)
|
||||
}
|
||||
predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
||||
class NameConstantNode extends NameNode {
|
||||
NameConstantNode() { exists(Py::NameConstant n | py_flow_bb_node(this, n, _, _)) }
|
||||
NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) }
|
||||
/*
|
||||
* We ought to override uses as well, but that has
|
||||
* a serious performance impact.
|
||||
* deprecated predicate uses(Py::Variable v) { none() }
|
||||
* deprecated predicate uses(Variable v) { none() }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a starred expression, `*a`. */
|
||||
class StarredNode extends ControlFlowNode {
|
||||
StarredNode() { toAst(this) instanceof Py::Starred }
|
||||
StarredNode() { toAst(this) instanceof Starred }
|
||||
|
||||
ControlFlowNode getValue() { toAst(result) = toAst(this).(Py::Starred).getValue() }
|
||||
ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() }
|
||||
}
|
||||
|
||||
/** The ControlFlowNode for an 'except' statement. */
|
||||
class ExceptFlowNode extends ControlFlowNode {
|
||||
ExceptFlowNode() { this.getNode() instanceof Py::ExceptStmt }
|
||||
ExceptFlowNode() { this.getNode() instanceof ExceptStmt }
|
||||
|
||||
/**
|
||||
* Gets the type handled by this exception handler.
|
||||
* `Py::ExceptionType` in `except Py::ExceptionType as e:`
|
||||
* `ExceptionType` in `except ExceptionType as e:`
|
||||
*/
|
||||
ControlFlowNode getType() {
|
||||
exists(Py::ExceptStmt ex |
|
||||
exists(ExceptStmt ex |
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
ex = this.getNode() and
|
||||
result.getNode() = ex.getType()
|
||||
result = ex.getType().getAFlowNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name assigned to the handled exception, if any.
|
||||
* `e` in `except Py::ExceptionType as e:`
|
||||
* `e` in `except ExceptionType as e:`
|
||||
*/
|
||||
ControlFlowNode getName() {
|
||||
exists(Py::ExceptStmt ex |
|
||||
exists(ExceptStmt ex |
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
ex = this.getNode() and
|
||||
result.getNode() = ex.getName()
|
||||
result = ex.getName().getAFlowNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** The ControlFlowNode for an 'except*' statement. */
|
||||
class ExceptGroupFlowNode extends ControlFlowNode {
|
||||
ExceptGroupFlowNode() { this.getNode() instanceof Py::ExceptGroupStmt }
|
||||
ExceptGroupFlowNode() { this.getNode() instanceof ExceptGroupStmt }
|
||||
|
||||
/**
|
||||
* Gets the type handled by this exception handler.
|
||||
* `Py::ExceptionType` in `except* Py::ExceptionType as e:`
|
||||
* `ExceptionType` in `except* ExceptionType as e:`
|
||||
*/
|
||||
ControlFlowNode getType() {
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getType()
|
||||
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name assigned to the handled exception, if any.
|
||||
* `e` in `except* Py::ExceptionType as e:`
|
||||
* `e` in `except* ExceptionType as e:`
|
||||
*/
|
||||
ControlFlowNode getName() {
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getName()
|
||||
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
|
||||
}
|
||||
}
|
||||
|
||||
private module Scopes {
|
||||
private predicate fast_local(NameNode n) {
|
||||
exists(Py::FastLocalVariable v |
|
||||
exists(FastLocalVariable v |
|
||||
n.uses(v) and
|
||||
v.getScope() = n.getScope()
|
||||
)
|
||||
@@ -956,15 +952,15 @@ private module Scopes {
|
||||
predicate local(NameNode n) {
|
||||
fast_local(n)
|
||||
or
|
||||
exists(Py::SsaVariable var |
|
||||
exists(SsaVariable var |
|
||||
var.getAUse() = n and
|
||||
n.getScope() instanceof Py::Class and
|
||||
n.getScope() instanceof Class and
|
||||
exists(var.getDefinition())
|
||||
)
|
||||
}
|
||||
|
||||
predicate non_local(NameNode n) {
|
||||
exists(Py::FastLocalVariable flv |
|
||||
exists(FastLocalVariable flv |
|
||||
flv.getALoad() = n.getNode() and
|
||||
not flv.getScope() = n.getScope()
|
||||
)
|
||||
@@ -972,20 +968,20 @@ private module Scopes {
|
||||
|
||||
// magic is fine, but we get questionable join-ordering of it
|
||||
pragma[nomagic]
|
||||
predicate use_of_global_variable(NameNode n, Py::Module scope, string name) {
|
||||
predicate use_of_global_variable(NameNode n, Module scope, string name) {
|
||||
n.isLoad() and
|
||||
not non_local(n) and
|
||||
not exists(Py::SsaVariable var | var.getAUse() = n |
|
||||
var.getVariable() instanceof Py::FastLocalVariable
|
||||
not exists(SsaVariable var | var.getAUse() = n |
|
||||
var.getVariable() instanceof FastLocalVariable
|
||||
or
|
||||
n.getScope() instanceof Py::Class and
|
||||
n.getScope() instanceof Class and
|
||||
not maybe_undefined(var)
|
||||
) and
|
||||
name = n.getId() and
|
||||
scope = n.getEnclosingModule()
|
||||
}
|
||||
|
||||
private predicate maybe_undefined(Py::SsaVariable var) {
|
||||
private predicate maybe_undefined(SsaVariable var) {
|
||||
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
||||
or
|
||||
var.getDefinition().isDelete()
|
||||
@@ -1062,13 +1058,13 @@ class BasicBlock extends @py_flow_node {
|
||||
private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() }
|
||||
|
||||
private predicate startLocationInfo(string file, int line, int col) {
|
||||
if this.firstNode().getNode() instanceof Py::Scope
|
||||
if this.firstNode().getNode() instanceof Scope
|
||||
then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _)
|
||||
else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _)
|
||||
}
|
||||
|
||||
private predicate endLocationInfo(int endl, int endc) {
|
||||
if this.getLastNode().getNode() instanceof Py::Scope and not this.oneNodeBlock()
|
||||
if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock()
|
||||
then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc)
|
||||
else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc)
|
||||
}
|
||||
@@ -1085,7 +1081,7 @@ class BasicBlock extends @py_flow_node {
|
||||
|
||||
/** Whether flow from this basic block reaches a normal exit from its scope */
|
||||
predicate reachesExit() {
|
||||
exists(Py::Scope s | s.getANormalExit().getBasicBlock() = this)
|
||||
exists(Scope s | s.getANormalExit().getBasicBlock() = this)
|
||||
or
|
||||
this.getASuccessor().reachesExit()
|
||||
}
|
||||
@@ -1126,7 +1122,7 @@ class BasicBlock extends @py_flow_node {
|
||||
|
||||
/** Gets the scope of this block */
|
||||
pragma[nomagic]
|
||||
Py::Scope getScope() {
|
||||
Scope getScope() {
|
||||
exists(ControlFlowNode n | n.getBasicBlock() = this |
|
||||
/* Take care not to use an entry or exit node as that node's scope will be the outer scope */
|
||||
not py_scope_flow(n, _, -1) and
|
||||
@@ -1149,17 +1145,17 @@ class BasicBlock extends @py_flow_node {
|
||||
predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) }
|
||||
|
||||
/**
|
||||
* Gets the `Py::ConditionBlock`, if any, that controls this block and
|
||||
* does not control any other `Py::ConditionBlock`s that control this block.
|
||||
* That is the `Py::ConditionBlock` that is closest dominator.
|
||||
* Gets the `ConditionBlock`, if any, that controls this block and
|
||||
* does not control any other `ConditionBlock`s that control this block.
|
||||
* That is the `ConditionBlock` that is closest dominator.
|
||||
*/
|
||||
Py::ConditionBlock getImmediatelyControllingBlock() {
|
||||
ConditionBlock getImmediatelyControllingBlock() {
|
||||
result = this.nonControllingImmediateDominator*().getImmediateDominator()
|
||||
}
|
||||
|
||||
private BasicBlock nonControllingImmediateDominator() {
|
||||
result = this.getImmediateDominator() and
|
||||
not result.(Py::ConditionBlock).controls(this, _)
|
||||
not result.(ConditionBlock).controls(this, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1179,7 +1175,7 @@ private class ControlFlowNodeAlias = ControlFlowNode;
|
||||
|
||||
final private class FinalBasicBlock = BasicBlock;
|
||||
|
||||
module Cfg implements BB::CfgSig<Py::Location> {
|
||||
module Cfg implements BB::CfgSig<Location> {
|
||||
private import codeql.controlflow.SuccessorType
|
||||
|
||||
class ControlFlowNode = ControlFlowNodeAlias;
|
||||
@@ -1190,7 +1186,7 @@ module Cfg implements BB::CfgSig<Py::Location> {
|
||||
// Using the location of the first node is simple
|
||||
// and we just need a way to identify the basic block
|
||||
// during debugging, so this will be serviceable.
|
||||
Py::Location getLocation() { result = super.getNode(0).getLocation() }
|
||||
Location getLocation() { result = super.getNode(0).getLocation() }
|
||||
|
||||
int length() { result = count(int i | exists(this.getNode(i))) }
|
||||
|
||||
|
||||
@@ -153,16 +153,8 @@ class Function extends Function_, Scope, AstNode {
|
||||
|
||||
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: bind a `Return` node explicitly instead, e.g.
|
||||
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
|
||||
* This API is being phased out together with `AstNode.getAFlowNode()` to
|
||||
* untangle the AST and CFG hierarchies in preparation for migrating the
|
||||
* dataflow library off the legacy CFG.
|
||||
*
|
||||
* Gets a control flow node for a return value of this function.
|
||||
*/
|
||||
deprecated ControlFlowNode getAReturnValueFlowNode() {
|
||||
/** Gets a control flow node for a return value of this function */
|
||||
ControlFlowNode getAReturnValueFlowNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this and
|
||||
ret.getValue() = result.getNode()
|
||||
|
||||
@@ -162,6 +162,8 @@ class ImportMember extends ImportMember_ {
|
||||
string getImportedModuleName() {
|
||||
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
|
||||
}
|
||||
|
||||
override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** An import statement */
|
||||
|
||||
@@ -46,23 +46,20 @@ class SelfAttributeRead extends SelfAttribute {
|
||||
}
|
||||
|
||||
predicate guardedByHasattr() {
|
||||
exists(Variable var, ControlFlowNode n, ControlFlowNode this_, ControlFlowNode obj_ |
|
||||
this_.getNode() = this and obj_.getNode() = this.getObject()
|
||||
|
|
||||
var.getAUse() = obj_ and
|
||||
exists(Variable var, ControlFlowNode n |
|
||||
var.getAUse() = this.getObject().getAFlowNode() and
|
||||
hasattr(n, var.getAUse(), this.getName()) and
|
||||
n.strictlyDominates(this_)
|
||||
n.strictlyDominates(this.getAFlowNode())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate locallyDefined() {
|
||||
exists(SelfAttributeStore store, ControlFlowNode store_, ControlFlowNode this_ |
|
||||
store_.getNode() = store and this_.getNode() = this
|
||||
|
|
||||
exists(SelfAttributeStore store |
|
||||
this.getName() = store.getName() and
|
||||
this.getScope() = store.getScope() and
|
||||
store_.strictlyDominates(this_)
|
||||
this.getScope() = store.getScope()
|
||||
|
|
||||
store.getAFlowNode().strictlyDominates(this.getAFlowNode())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -5,30 +5,24 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
exists(CompareNode cn | cn = g |
|
||||
exists(ImmutableLiteral const, Cmpop op, ControlFlowNode c |
|
||||
c.getNode() = const and
|
||||
(
|
||||
op = any(Eq eq) and branch = true
|
||||
or
|
||||
op = any(NotEq ne) and branch = false
|
||||
)
|
||||
|
|
||||
cn.operands(c, op, node)
|
||||
exists(ImmutableLiteral const, Cmpop op |
|
||||
op = any(Eq eq) and branch = true
|
||||
or
|
||||
cn.operands(node, op, c)
|
||||
op = any(NotEq ne) and branch = false
|
||||
|
|
||||
cn.operands(const.getAFlowNode(), op, node)
|
||||
or
|
||||
cn.operands(node, op, const.getAFlowNode())
|
||||
)
|
||||
or
|
||||
exists(NameConstant const, Cmpop op, ControlFlowNode c |
|
||||
c.getNode() = const and
|
||||
(
|
||||
op = any(Is is_) and branch = true
|
||||
or
|
||||
op = any(IsNot isn) and branch = false
|
||||
)
|
||||
|
|
||||
cn.operands(c, op, node)
|
||||
exists(NameConstant const, Cmpop op |
|
||||
op = any(Is is_) and branch = true
|
||||
or
|
||||
cn.operands(node, op, c)
|
||||
op = any(IsNot isn) and branch = false
|
||||
|
|
||||
cn.operands(const.getAFlowNode(), op, node)
|
||||
or
|
||||
cn.operands(node, op, const.getAFlowNode())
|
||||
)
|
||||
or
|
||||
exists(IterableNode const_iterable, Cmpop op |
|
||||
|
||||
@@ -228,7 +228,7 @@ private class ClassDefinitionAsAttrWrite extends AttrWrite, CfgNode {
|
||||
|
||||
override Node getValue() { result.asCfgNode() = node.getValue() }
|
||||
|
||||
override Node getObject() { result.asCfgNode().getNode() = cls }
|
||||
override Node getObject() { result.asCfgNode() = cls.getAFlowNode() }
|
||||
|
||||
override ExprNode getAttributeNameExpr() { none() }
|
||||
|
||||
|
||||
@@ -256,12 +256,9 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
|
||||
*/
|
||||
overlay[local]
|
||||
predicate isStaticmethod(Function func) {
|
||||
// The decorator is *syntactically* a `Name` "staticmethod" — we don't
|
||||
// care which variable it resolves to. `staticmethod` is a builtin and
|
||||
// is almost never shadowed in a module-level scope; even if a class
|
||||
// redefines `staticmethod` in its body, the class body has not started
|
||||
// executing yet at the decorator position, so Python uses the builtin.
|
||||
func.getADecorator().(Name).getId() = "staticmethod"
|
||||
exists(NameNode id | id.getId() = "staticmethod" and id.isGlobal() |
|
||||
func.getADecorator() = id.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,9 +268,9 @@ predicate isStaticmethod(Function func) {
|
||||
*/
|
||||
overlay[local]
|
||||
predicate isClassmethod(Function func) {
|
||||
// See `isStaticmethod` for the rationale for matching on the AST `Name`
|
||||
// rather than going via the CFG and `isGlobal()`.
|
||||
func.getADecorator().(Name).getId() = "classmethod"
|
||||
exists(NameNode id | id.getId() = "classmethod" and id.isGlobal() |
|
||||
func.getADecorator() = id.getNode()
|
||||
)
|
||||
or
|
||||
exists(Class cls |
|
||||
cls.getAMethod() = func and
|
||||
@@ -288,8 +285,9 @@ predicate isClassmethod(Function func) {
|
||||
/** Holds if the function `func` has a `property` decorator. */
|
||||
overlay[local]
|
||||
predicate hasPropertyDecorator(Function func) {
|
||||
// See `isStaticmethod` for the rationale for matching on the AST `Name`.
|
||||
func.getADecorator().(Name).getId() = "property"
|
||||
exists(NameNode id | id.getId() = "property" and id.isGlobal() |
|
||||
func.getADecorator() = id.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1913,8 +1911,8 @@ abstract class ReturnNode extends Node {
|
||||
class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||
// See `TaintTrackingImplementation::returnFlowStep`
|
||||
ExtractedReturnNode() {
|
||||
node.getNode() = any(Return ret).getValue() or
|
||||
node.getNode() = any(Yield yield)
|
||||
node = any(Return ret).getValue().getAFlowNode() or
|
||||
node = any(Yield yield).getAFlowNode()
|
||||
}
|
||||
|
||||
override ReturnKind getKind() { any() }
|
||||
@@ -1932,7 +1930,7 @@ class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||
class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode {
|
||||
YieldNodeInContextManagerFunction() {
|
||||
hasContextmanagerDecorator(node.getScope()) and
|
||||
node.getNode() = any(Yield yield).getValue()
|
||||
node = any(Yield yield).getValue().getAFlowNode()
|
||||
}
|
||||
|
||||
override ReturnKind getKind() { any() }
|
||||
|
||||
@@ -185,8 +185,8 @@ private predicate synthDictSplatArgumentNodeStoreStep(
|
||||
*/
|
||||
predicate yieldStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
exists(Yield yield |
|
||||
nodeTo.asCfgNode().getNode() = yield and
|
||||
nodeFrom.asCfgNode().getNode() = yield.getValue() and
|
||||
nodeTo.asCfgNode() = yield.getAFlowNode() and
|
||||
nodeFrom.asCfgNode() = yield.getValue().getAFlowNode() and
|
||||
// TODO: Consider if this will also need to transfer dictionary content
|
||||
// once dictionary comprehensions are supported.
|
||||
c instanceof ListElementContent
|
||||
|
||||
@@ -485,7 +485,7 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
|
||||
|
||||
/** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */
|
||||
Node getALocalRead() {
|
||||
result.asCfgNode().getNode() = var.getALoad() and
|
||||
result.asCfgNode() = var.getALoad().getAFlowNode() and
|
||||
not result.getScope() = mod
|
||||
}
|
||||
|
||||
|
||||
@@ -9,19 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
private import semmle.python.dataflow.new.TypeTracking
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Holds if `init` is a package's `__init__.py` and `var` is a global variable in
|
||||
* `init` whose name matches a submodule of the package.
|
||||
*
|
||||
* Inlined from `SsaSource::init_module_submodule_defn` to avoid pulling
|
||||
* `semmle.python.essa.SsaDefinitions` into the new dataflow stack.
|
||||
*/
|
||||
private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) {
|
||||
init.isPackageInit() and
|
||||
exists(init.getPackage().getSubModule(var.getId())) and
|
||||
var.getScope() = init
|
||||
}
|
||||
private import semmle.python.essa.SsaDefinitions
|
||||
|
||||
/**
|
||||
* Python modules and the way imports are resolved are... complicated. Here's a crash course in how
|
||||
@@ -338,7 +326,7 @@ module ImportResolution {
|
||||
// imported yet.
|
||||
exists(string submodule, Module package, EssaVariable var |
|
||||
submodule = var.getName() and
|
||||
initModuleSubmoduleDefn(var.getSourceVariable(), package) and
|
||||
SsaSource::init_module_submodule_defn(var.getSourceVariable(), package.getEntryNode()) and
|
||||
m = getModuleFromName(package.getPackageName() + "." + submodule) and
|
||||
result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode()
|
||||
)
|
||||
|
||||
@@ -1,547 +0,0 @@
|
||||
/**
|
||||
* Provides the Python SSA implementation built on the new (shared) CFG.
|
||||
*
|
||||
* Mirrors the Java SSA adapter at
|
||||
* `java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll`:
|
||||
* an `InputSig` is defined in terms of positional `(BasicBlock, int)`
|
||||
* variable references, and the shared
|
||||
* `codeql.ssa.Ssa::Make<Location, Cfg, Input>` module is then
|
||||
* instantiated.
|
||||
*
|
||||
* `SourceVariable` is the AST-level `Py::Variable`. Variable references
|
||||
* are looked up via the CFG facade's `NameNode.defines`/`uses`/`deletes`
|
||||
* predicates, which themselves are one-line bridges to AST-level
|
||||
* `Name.defines`/`uses`/`deletes`.
|
||||
*
|
||||
* Implicit-entry definitions are inserted for:
|
||||
* - non-local / global / builtin variables that are read in the scope
|
||||
* but never assigned (no enclosing CFG node defines them),
|
||||
* - captured variables (variables defined in an enclosing scope that
|
||||
* are read inside the scope), and
|
||||
* - parameters, but only if the corresponding parameter name is *not*
|
||||
* itself a CFG node. With the C#-style parameter wiring already
|
||||
* installed in `AstNodeImpl.qll`, parameter names *are* CFG nodes,
|
||||
* so the regular `variableWrite` path handles them — no `i = -1`
|
||||
* entry is needed for ordinary parameters.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import python as Py
|
||||
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import codeql.ssa.Ssa as SsaImplCommon
|
||||
private import codeql.controlflow.BasicBlock as BB
|
||||
|
||||
/**
|
||||
* Adapts the Python `Cfg` facade to the shared SSA library's `CfgSig`.
|
||||
* All members are inherited from `Cfg::ControlFlowNode` and
|
||||
* `Cfg::BasicBlock`.
|
||||
*/
|
||||
private module CfgForSsa implements BB::CfgSig<Py::Location> {
|
||||
class ControlFlowNode = CfgImpl::ControlFlowNode;
|
||||
|
||||
class BasicBlock = CfgImpl::BasicBlock;
|
||||
|
||||
class EntryBasicBlock = CfgImpl::Cfg::EntryBasicBlock;
|
||||
|
||||
predicate dominatingEdge = CfgImpl::Cfg::dominatingEdge/2;
|
||||
}
|
||||
|
||||
/**
|
||||
* A source variable for SSA, wrapping a Python AST `Variable`.
|
||||
*
|
||||
* We only track variables that are read at least once in their scope —
|
||||
* tracking write-only variables would be unnecessary work — *except*
|
||||
* for module-scope globals, where the "read" can be external (e.g.
|
||||
* `import mymodule; mymodule.x`). Such globals are tracked
|
||||
* unconditionally so that import-resolution can find their defining
|
||||
* write.
|
||||
*/
|
||||
private newtype TSsaSourceVariable =
|
||||
TPyVar(Py::Variable v) {
|
||||
// Has a use somewhere — read-relevant for SSA.
|
||||
exists(Cfg::NameNode n | n.uses(v))
|
||||
or
|
||||
// Or has a deletion (treated as a write that destroys the value).
|
||||
exists(Cfg::NameNode n | n.deletes(v))
|
||||
or
|
||||
// Or is a module-scope global written in this module — must be
|
||||
// tracked even if never read locally, because importers may read
|
||||
// it as an attribute on the module object.
|
||||
v.getScope() instanceof Py::Module and
|
||||
exists(Cfg::NameNode n | n.defines(v))
|
||||
or
|
||||
// Or is a parameter — parameters must always have a
|
||||
// `ParameterDefinition` for dataflow argument-routing to work,
|
||||
// even if the parameter is never read in its scope. Mirrors
|
||||
// legacy ESSA's `ParameterDefinition` (which fired for every
|
||||
// parameter binding regardless of liveness).
|
||||
exists(Py::Parameter p | p.asName() = v.getAStore())
|
||||
}
|
||||
|
||||
/**
|
||||
* A source variable for SSA, wrapping a Python AST `Variable`.
|
||||
*/
|
||||
class SsaSourceVariable extends TSsaSourceVariable {
|
||||
/** Gets the underlying Python AST variable. */
|
||||
Py::Variable getVariable() { this = TPyVar(result) }
|
||||
|
||||
/** Gets the (textual) name of this variable. */
|
||||
string getName() { result = this.getVariable().getId() }
|
||||
|
||||
/** Gets a textual representation of this source variable. */
|
||||
string toString() { result = this.getVariable().toString() }
|
||||
|
||||
/** Gets the location of this source variable. */
|
||||
Py::Location getLocation() { result = this.getVariable().getScope().getLocation() }
|
||||
|
||||
/** Gets the scope in which this variable lives. */
|
||||
Py::Scope getScope() { result = this.getVariable().getScope() }
|
||||
|
||||
/**
|
||||
* Gets a use of this variable as it appears in the source — a `NameNode`
|
||||
* that loads or deletes the variable. Mirrors legacy
|
||||
* `SsaSourceVariable.getASourceUse()`.
|
||||
*/
|
||||
Cfg::ControlFlowNode getASourceUse() {
|
||||
exists(Cfg::NameNode n | result = n |
|
||||
n.uses(this.getVariable()) or n.deletes(this.getVariable())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an implicit use of this variable. The new SSA does not have
|
||||
* implicit-use refinements, but we keep this for API parity — every
|
||||
* normal-exit of the variable's scope counts as a sink, ensuring
|
||||
* variables stay live to scope exit for taint-tracking.
|
||||
*/
|
||||
Cfg::ControlFlowNode getAnImplicitUse() {
|
||||
result.isNormalExit() and result.getScope() = this.getScope()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a use of this variable — either an explicit source use or an
|
||||
* implicit use at scope exit. Mirrors legacy `SsaSourceVariable.getAUse()`.
|
||||
*/
|
||||
Cfg::ControlFlowNode getAUse() {
|
||||
result = this.getASourceUse() or result = this.getAnImplicitUse()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is a non-local read in scope `s`, in the sense that `s`
|
||||
* uses `v` but does not write it within `s`. This includes globals,
|
||||
* builtins, and variables captured from an enclosing function scope.
|
||||
*
|
||||
* The `Py::Variable` `v` lives in some defining scope (the module for
|
||||
* globals, an outer function for closures, etc.); the reading scope
|
||||
* `s` is the scope where the use of `v` occurs.
|
||||
*/
|
||||
private predicate nonLocalReadIn(Py::Variable v, Py::Scope s) {
|
||||
exists(Cfg::NameNode n |
|
||||
n.uses(v) and
|
||||
n.getScope() = s and
|
||||
not exists(Cfg::NameNode def | def.defines(v) and def.getScope() = s)
|
||||
) and
|
||||
// Match legacy ESSA: only create entry defs for variables that have
|
||||
// at least one defining store somewhere — otherwise the entry def
|
||||
// represents "nothing reaches here", which is the default anyway and
|
||||
// introduces no useful flow. (Legacy's `ModuleVariable` required a
|
||||
// store; this is the closure-aware generalisation.)
|
||||
exists(Cfg::NameNode store | store.defines(v))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is the entry basic block of a scope where `v` should
|
||||
* have an implicit entry definition. This covers:
|
||||
* - non-local / global / builtin variables read in `s`, and
|
||||
* - captured variables (defined in an enclosing scope but read in `s`).
|
||||
*
|
||||
* Each reading scope gets its own entry def, so a closure variable can
|
||||
* have multiple entry defs across all functions/methods that read it.
|
||||
*
|
||||
* Parameters are *not* included: their bound `Name` is itself a CFG
|
||||
* node (per the C#-style parameter wiring), so `variableWrite` fires at
|
||||
* the parameter's natural CFG index.
|
||||
*/
|
||||
private predicate hasEntryDefIn(SsaSourceVariable v, CfgImpl::BasicBlock bb) {
|
||||
exists(Py::Scope s |
|
||||
nonLocalReadIn(v.getVariable(), s) and
|
||||
bb = entryBlock(s)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry basic block of scope `s`, where implicit entry
|
||||
* definitions are placed (at synthetic index `-1`).
|
||||
*/
|
||||
private CfgImpl::BasicBlock entryBlock(Py::Scope s) {
|
||||
exists(CfgImpl::ControlFlowNode entry |
|
||||
entry instanceof CfgImpl::ControlFlow::EntryNode and
|
||||
entry.getEnclosingCallable().asScope() = s and
|
||||
result = entry.getBasicBlock()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The SSA `InputSig` for Python. References are positional
|
||||
* `(BasicBlock, int)` pairs into the new CFG.
|
||||
*/
|
||||
private module SsaImplInput implements SsaImplCommon::InputSig<Py::Location, CfgImpl::BasicBlock> {
|
||||
class SourceVariable = SsaSourceVariable;
|
||||
|
||||
predicate variableWrite(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
// Explicit binding at a CFG node — includes assignments,
|
||||
// parameter Names (wired in via the C# pattern), exception-handler
|
||||
// `as`-bindings, import aliases, and match-pattern captures.
|
||||
exists(Cfg::NameNode n |
|
||||
bb.getNode(i) = n and
|
||||
n.defines(v.getVariable()) and
|
||||
certain = true
|
||||
)
|
||||
or
|
||||
// `del x` — removes the binding. Modelled as a certain write that
|
||||
// makes any subsequent read invalid.
|
||||
exists(Cfg::NameNode n |
|
||||
bb.getNode(i) = n and
|
||||
n.deletes(v.getVariable()) and
|
||||
certain = true
|
||||
)
|
||||
or
|
||||
// Implicit entry definition for non-local / captured / global /
|
||||
// builtin variables read in some scope. Each reading scope's entry
|
||||
// block gets one such write, allowing closures: e.g. when `x` is a
|
||||
// parameter of an outer function and read inside a nested
|
||||
// function, both scopes get entry defs for `x`.
|
||||
hasEntryDefIn(v, bb) and
|
||||
i = -1 and
|
||||
certain = true
|
||||
or
|
||||
// `from X import *` — possibly rebinds every name in the importing
|
||||
// scope. Modelled as an uncertain write at the import-star's CFG
|
||||
// position for every variable that lives in (or is referenced
|
||||
// from) the same scope as the import-star. Mirrors legacy ESSA's
|
||||
// `ImportStarRefinement` (see `essa/SsaDefinitions.qll`'s
|
||||
// `import_star_refinement` predicate). The write is uncertain so
|
||||
// that prior definitions of the variable remain available — the
|
||||
// shared-SSA `SsaUncertainWrite` merges the new value with the
|
||||
// immediately preceding definition.
|
||||
exists(Cfg::ImportStarNode imp |
|
||||
bb.getNode(i) = imp and
|
||||
certain = false and
|
||||
(
|
||||
v.getVariable().getScope() = imp.getScope()
|
||||
or
|
||||
// Variable is defined in some other scope but referenced in
|
||||
// the same scope as the import-star (matches legacy clause 2:
|
||||
// `other.uses(v) and def.getScope() = other.getScope()`).
|
||||
exists(Cfg::NameNode other |
|
||||
other.uses(v.getVariable()) and
|
||||
imp.getScope() = other.getScope()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
// Explicit source use — a `Name` load or a `del x` of the variable.
|
||||
exists(Cfg::NameNode n |
|
||||
bb.getNode(i) = n and
|
||||
n.uses(v.getVariable()) and
|
||||
certain = true
|
||||
)
|
||||
or
|
||||
// Synthetic use at the normal exit of the variable's defining scope.
|
||||
// This keeps every variable live to scope exit so that callers (e.g.
|
||||
// `module_export` in ImportResolution.qll, or taint-tracking pass-through
|
||||
// through unread locals) can ask "which definition reaches end of
|
||||
// scope?". Mirrors legacy ESSA's `SsaSourceVariable.getAUse()` which
|
||||
// included `getScope().getANormalExit()`.
|
||||
exists(Cfg::ControlFlowNode exit |
|
||||
exit.isNormalExit() and
|
||||
exit.getScope() = v.getVariable().getScope() and
|
||||
bb.getNode(i) = exit and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The shared SSA instantiation for Python.
|
||||
*
|
||||
* Members:
|
||||
* - `Definition` — the union of explicit, uncertain, and phi definitions
|
||||
* - `WriteDefinition`, `UncertainWriteDefinition`, `PhiNode`
|
||||
* - the standard SSA predicates (`getAUse`, `getAnUltimateDefinition`, ...).
|
||||
*/
|
||||
module Ssa = SsaImplCommon::Make<Py::Location, CfgForSsa, SsaImplInput>;
|
||||
|
||||
final class Definition = Ssa::Definition;
|
||||
|
||||
final class WriteDefinition = Ssa::WriteDefinition;
|
||||
|
||||
final class UncertainWriteDefinition = Ssa::UncertainWriteDefinition;
|
||||
|
||||
final class PhiNode = Ssa::PhiNode;
|
||||
|
||||
// ===========================================================================
|
||||
// ESSA-shaped adapter layer
|
||||
//
|
||||
// The dataflow library (`python/ql/lib/semmle/python/dataflow/new/`) and
|
||||
// related modules (`ApiGraphs.qll`, etc.) consume the legacy ESSA API
|
||||
// (`EssaVariable`, `EssaDefinition`, `AssignmentDefinition`,
|
||||
// `ScopeEntryDefinition`, `ParameterDefinition`, `WithDefinition`,
|
||||
// `PhiFunction`, plus the `AdjacentUses` module). To migrate them off
|
||||
// the legacy CFG, we expose the same API surface on top of the
|
||||
// shared SSA built above.
|
||||
//
|
||||
// This adapter is intentionally narrow: it covers only the predicates
|
||||
// that new dataflow consumes. The richer legacy ESSA — refinement
|
||||
// nodes, attribute refinements, edge refinements — stays available
|
||||
// via `semmle.python.essa.Essa` for points-to / legacy code.
|
||||
// ===========================================================================
|
||||
/**
|
||||
* Gets the CFG node at which a write definition's binding takes place.
|
||||
*
|
||||
* For ordinary writes (assignment, deletion, parameter) this is the
|
||||
* canonical CFG node of the bound Name. For implicit entry definitions
|
||||
* (synthesised at position `-1` of a scope's entry BB) this is the
|
||||
* scope's entry node.
|
||||
*/
|
||||
private Cfg::ControlFlowNode writeDefNode(Ssa::WriteDefinition def) {
|
||||
exists(CfgImpl::BasicBlock bb, int i | def.definesAt(_, bb, i) |
|
||||
i >= 0 and result = bb.getNode(i)
|
||||
or
|
||||
i = -1 and result = bb.getNode(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A write definition whose binding has a corresponding CFG node — i.e.
|
||||
* everything that's not a phi node. Mirrors legacy ESSA's
|
||||
* `EssaNodeDefinition`.
|
||||
*/
|
||||
class EssaNodeDefinition extends Ssa::WriteDefinition {
|
||||
/** Gets the CFG node where this definition's binding takes place. */
|
||||
Cfg::ControlFlowNode getDefiningNode() { result = writeDefNode(this) }
|
||||
|
||||
/** Gets the variable defined here (legacy name). */
|
||||
SsaSourceVariable getVariable() { result = this.getSourceVariable() }
|
||||
|
||||
/** Gets the enclosing scope. */
|
||||
Py::Scope getScope() {
|
||||
exists(Cfg::ControlFlowNode n | n = this.getDefiningNode() | result = n.getScope())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this definition defines source variable `v` at CFG node
|
||||
* `defNode`. Flatter form of `getSourceVariable()` +
|
||||
* `getDefiningNode()`, matching legacy ESSA's `definedBy`.
|
||||
*/
|
||||
predicate definedBy(SsaSourceVariable v, Cfg::ControlFlowNode defNode) {
|
||||
v = this.getSourceVariable() and defNode = this.getDefiningNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignment definition: any binding where the value being assigned
|
||||
* is statically known via `Cfg::DefinitionNode.getValue()`. Includes
|
||||
* plain assignments, walrus, annotated assignments, augmented
|
||||
* assignments, import aliases (`import x` / `from m import x [as y]`),
|
||||
* `with ... as x`, and for-target bindings (where `getValue()` returns
|
||||
* the iter expression's CFG node). Excludes parameter bindings —
|
||||
* those are modelled by `ParameterDefinition`.
|
||||
*/
|
||||
class AssignmentDefinition extends EssaNodeDefinition {
|
||||
AssignmentDefinition() {
|
||||
exists(Cfg::NameNode n | n = this.getDefiningNode() |
|
||||
exists(n.(Cfg::DefinitionNode).getValue()) and
|
||||
not n.(Cfg::ControlFlowNode).isParameter()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the CFG node for the value being assigned, if statically known. */
|
||||
Cfg::ControlFlowNode getValue() {
|
||||
result = this.getDefiningNode().(Cfg::DefinitionNode).getValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter definition — the binding of a parameter name in a
|
||||
* function's scope.
|
||||
*/
|
||||
class ParameterDefinition extends EssaNodeDefinition {
|
||||
ParameterDefinition() { this.getDefiningNode().isParameter() }
|
||||
|
||||
/** Gets the AST `Parameter` (a `Py::Name` in param context). */
|
||||
Py::Name getParameter() { result = this.getDefiningNode().getNode() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A definition introduced by a `with ... as x:` clause.
|
||||
*/
|
||||
class WithDefinition extends EssaNodeDefinition {
|
||||
WithDefinition() {
|
||||
exists(Cfg::NameNode n, Py::With w |
|
||||
n = this.getDefiningNode() and
|
||||
w.getOptionalVars() = n.getNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignment where the LHS is a tuple/list and the RHS is unpacked:
|
||||
* `a, b = (1, 2)` or `a, *rest = xs`. The SSA def lives at the inner
|
||||
* `Name` CFG node, but for IterableUnpacking integration we expose
|
||||
* the enclosing `StarredNode` as the `getDefiningNode()` for `*rest`
|
||||
* patterns — mirroring legacy ESSA's `multi_assignment_definition`,
|
||||
* which placed the def at the StarredNode CFG node.
|
||||
*/
|
||||
class MultiAssignmentDefinition extends EssaNodeDefinition {
|
||||
MultiAssignmentDefinition() {
|
||||
exists(Cfg::NameNode n | n = super.getDefiningNode() |
|
||||
exists(Py::Assign a, Py::Expr lhs |
|
||||
a.getATarget() = lhs and
|
||||
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
|
||||
lhs.getASubExpression+() = n.getNode()
|
||||
)
|
||||
or
|
||||
// For-loop with tuple/list target: `for a, b in xs:` —
|
||||
// tuple-unpacking semantics applies to the for-target.
|
||||
exists(Py::For f, Py::Expr lhs |
|
||||
f.getTarget() = lhs and
|
||||
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
|
||||
lhs.getASubExpression+() = n.getNode()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override Cfg::ControlFlowNode getDefiningNode() {
|
||||
// Default: the underlying `Name` CFG node (where the SSA def lives).
|
||||
not exists(Cfg::StarredNode s |
|
||||
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
|
||||
) and
|
||||
result = super.getDefiningNode()
|
||||
or
|
||||
// Exception: for `*rest`, expose the enclosing `Starred` CFG node
|
||||
// so that `IterableUnpacking::iterableUnpackingStarredElementStoreStep`
|
||||
// can attach the rest-list to it.
|
||||
exists(Cfg::StarredNode s |
|
||||
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
|
||||
|
|
||||
result = s
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An implicit entry definition for a non-local / captured / global /
|
||||
* builtin variable read in a scope but not defined there.
|
||||
*
|
||||
* Inherits from `EssaNodeDefinition` and exposes the scope's entry node
|
||||
* as its defining node (matching legacy ESSA semantics).
|
||||
*/
|
||||
class ScopeEntryDefinition extends EssaNodeDefinition {
|
||||
ScopeEntryDefinition() {
|
||||
exists(CfgImpl::BasicBlock bb |
|
||||
this.definesAt(_, bb, -1) and
|
||||
bb instanceof CfgImpl::Cfg::EntryBasicBlock
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the enclosing scope (the scope whose entry block this def is in). */
|
||||
override Py::Scope getScope() {
|
||||
exists(CfgImpl::BasicBlock bb |
|
||||
this.definesAt(_, bb, -1) and
|
||||
result = bb.getNode(0).(Cfg::ControlFlowNode).getScope()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A phi node (alias matching legacy naming). */
|
||||
class PhiFunction extends PhiNode {
|
||||
/**
|
||||
* Gets an input to this phi function (a definition that flows into
|
||||
* the phi from one of its predecessor blocks). Mirrors legacy
|
||||
* ESSA's `PhiFunction.getAnInput()`.
|
||||
*/
|
||||
Ssa::Definition getAnInput() { Ssa::phiHasInputFromBlock(this, result, _) }
|
||||
}
|
||||
|
||||
/** Base class for all ESSA definitions (legacy-shaped). */
|
||||
class EssaDefinition = Ssa::Definition;
|
||||
|
||||
/**
|
||||
* An adapter representing a single SSA-defined "variable" — wrapping
|
||||
* one `Ssa::Definition`. Mirrors legacy `EssaVariable` API.
|
||||
*/
|
||||
class EssaVariable extends Ssa::Definition {
|
||||
/** Gets the underlying SSA definition (legacy name). */
|
||||
Ssa::Definition getDefinition() { result = this }
|
||||
|
||||
/**
|
||||
* Gets a CFG node where this definition is used. Includes regular
|
||||
* `Name` reads as well as the synthetic scope-exit "use" registered
|
||||
* via `SsaImplInput::variableRead` — mirrors legacy ESSA's
|
||||
* `EssaVariable.getAUse()` which inherited the synthetic exit-use
|
||||
* from `SsaSourceVariable`.
|
||||
*/
|
||||
Cfg::ControlFlowNode getAUse() {
|
||||
exists(CfgImpl::BasicBlock bb, int i |
|
||||
Ssa::ssaDefReachesRead(this.getSourceVariable(), this, bb, i) and
|
||||
bb.getNode(i) = result
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the (textual) name of the underlying variable. */
|
||||
string getName() { result = this.getSourceVariable().getVariable().getId() }
|
||||
|
||||
/** Gets the scope in which this variable lives. */
|
||||
Py::Scope getScope() { result = this.getSourceVariable().getVariable().getScope() }
|
||||
|
||||
/** Gets an ultimate non-phi ancestor of this definition. */
|
||||
EssaVariable getAnUltimateDefinition() {
|
||||
if this instanceof PhiNode
|
||||
then
|
||||
exists(Ssa::Definition input |
|
||||
Ssa::phiHasInputFromBlock(this, input, _) and
|
||||
result = input.(EssaVariable).getAnUltimateDefinition()
|
||||
)
|
||||
else result = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjacent use-use and def-use relations exposed by the shared SSA
|
||||
* library. Provides the same interface as legacy
|
||||
* `semmle.python.essa.SsaCompute::AdjacentUses`.
|
||||
*/
|
||||
module AdjacentUses {
|
||||
/** Holds if `nodeFrom` and `nodeTo` are adjacent uses of the same SSA variable. */
|
||||
predicate adjacentUseUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) {
|
||||
exists(SsaSourceVariable v, CfgImpl::BasicBlock bb1, int i1, CfgImpl::BasicBlock bb2, int i2 |
|
||||
Ssa::adjacentUseUse(bb1, i1, bb2, i2, v, _) and
|
||||
nodeFrom = bb1.getNode(i1) and
|
||||
nodeTo = bb2.getNode(i2)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `use` is a first use of definition `def`. */
|
||||
predicate firstUse(Ssa::Definition def, Cfg::NameNode use) {
|
||||
exists(CfgImpl::BasicBlock bb, int i |
|
||||
Ssa::firstUse(def, bb, i, _) and
|
||||
use = bb.getNode(i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` is any reachable use of definition `def`. Combines
|
||||
* `firstUse` with transitive use-use adjacency.
|
||||
*/
|
||||
predicate useOfDef(Ssa::Definition def, Cfg::NameNode use) {
|
||||
firstUse(def, use)
|
||||
or
|
||||
exists(Cfg::NameNode mid | useOfDef(def, mid) and adjacentUseUse(mid, use))
|
||||
}
|
||||
}
|
||||
@@ -94,10 +94,8 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||
Node returnOf(Node callable, SummaryComponent return) {
|
||||
return = FlowSummaryImpl::Private::SummaryComponent::return() and
|
||||
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
|
||||
exists(Return ret |
|
||||
ret.getScope() = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and
|
||||
result.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
result.asCfgNode() =
|
||||
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
// Relating callables to nodes
|
||||
|
||||
@@ -61,7 +61,7 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
||||
class VariableWrite extends ControlFlowNode {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableWrite() { exists(DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue()) }
|
||||
VariableWrite() { this = v.getAStore().getAFlowNode().(DefinitionNode).getValue() }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
|
||||
@@ -71,7 +71,7 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
||||
class VariableRead extends Expr {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableRead() { this.getNode() = v.getALoad() }
|
||||
VariableRead() { this = v.getALoad().getAFlowNode() }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
}
|
||||
|
||||
@@ -448,7 +448,8 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
|
||||
context = TNoParam() and
|
||||
src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and
|
||||
node.asCfgNode() = call and
|
||||
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
|
||||
retval.asCfgNode() =
|
||||
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
|
||||
) and
|
||||
edgeLabel = "return"
|
||||
}
|
||||
@@ -470,7 +471,8 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
|
||||
this.callContexts(call, src, pyfunc, context, callee) and
|
||||
retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and
|
||||
node.asCfgNode() = call and
|
||||
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
|
||||
retval.asCfgNode() =
|
||||
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
|
||||
) and
|
||||
edgeLabel = "call"
|
||||
}
|
||||
@@ -714,10 +716,8 @@ private class EssaTaintTracking extends string instanceof TaintTracking::Configu
|
||||
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
|
||||
path.noAttribute()
|
||||
|
|
||||
srcnode.asCfgNode().getNode() = assign.getValue() and
|
||||
exists(SequenceNode left_parent | left_parent.getNode() = assign.getATarget() |
|
||||
depth = iterable_unpacking_descent(left_parent, defn.getDefiningNode())
|
||||
) and
|
||||
assign.getValue().getAFlowNode() = srcnode.asCfgNode() and
|
||||
depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and
|
||||
kind = taint_at_depth(srckind, depth)
|
||||
)
|
||||
}
|
||||
@@ -964,7 +964,7 @@ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) {
|
||||
* - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1
|
||||
*/
|
||||
int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) {
|
||||
exists(Assign a | left_parent.getNode() = a.getATarget().getASubExpression*()) and
|
||||
exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and
|
||||
left_parent.getAnElement() = left_defn and
|
||||
// Handle `a, *b = some_iterable`
|
||||
if left_defn instanceof StarredNode then result = 0 else result = 1
|
||||
|
||||
@@ -56,7 +56,7 @@ module SsaSource {
|
||||
predicate with_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(With with, Name var |
|
||||
with.getOptionalVars() = var and
|
||||
defn.getNode() = var
|
||||
var.getAFlowNode() = defn
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
@@ -67,7 +67,7 @@ module SsaSource {
|
||||
predicate pattern_capture_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(MatchCapturePattern capture, Name var |
|
||||
capture.getVariable() = var and
|
||||
defn.getNode() = var
|
||||
var.getAFlowNode() = defn
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
@@ -78,7 +78,7 @@ module SsaSource {
|
||||
predicate pattern_alias_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(MatchAsPattern pattern, Name var |
|
||||
pattern.getAlias() = var and
|
||||
defn.getNode() = var
|
||||
var.getAFlowNode() = defn
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ module Bottle {
|
||||
|
||||
override Parameter getARoutedParameter() { none() }
|
||||
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,10 +73,7 @@ module Bottle {
|
||||
/** A response returned by a view callable. */
|
||||
class BottleReturnResponse extends Http::Server::HttpResponse::Range {
|
||||
BottleReturnResponse() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(View::ViewCallable vc) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -2872,10 +2872,7 @@ module PrivateDjango {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
DjangoRedirectViewGetRedirectUrlReturn() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(GetRedirectUrlFunction f) and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getRedirectLocation() { result = this }
|
||||
|
||||
@@ -129,7 +129,7 @@ module FastApi {
|
||||
result in [this.getArg(0), this.getArgByName("path")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
|
||||
override string getFramework() { result = "FastAPI" }
|
||||
|
||||
@@ -309,10 +309,7 @@ module FastApi {
|
||||
FastApiRouteSetup routeSetup;
|
||||
|
||||
FastApiRequestHandlerReturn() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = routeSetup.getARequestHandler() and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
node = routeSetup.getARequestHandler().getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -371,7 +371,7 @@ module Flask {
|
||||
result in [this.getArg(0), this.getArgByName("rule")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -536,7 +536,7 @@ module Flask {
|
||||
FlaskRouteHandlerReturn() {
|
||||
exists(Function routeHandler |
|
||||
routeHandler = any(FlaskRouteSetup rs).getARequestHandler() and
|
||||
exists(Return ret | ret.getScope() = routeHandler and node.getNode() = ret.getValue()) and
|
||||
node = routeHandler.getAReturnValueFlowNode() and
|
||||
not this instanceof Flask::Response::InstanceSource
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ private module FlaskAdmin {
|
||||
result in [this.getArg(0), this.getArgByName("url")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ private module FlaskAdmin {
|
||||
|
||||
override Function getARequestHandler() {
|
||||
exists(Flask::FlaskViewClass cls |
|
||||
node.getNode() = cls.getADecorator() and
|
||||
cls.getADecorator().getAFlowNode() = node and
|
||||
result = cls.getARequestHandler()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -166,10 +166,7 @@ module Pyramid {
|
||||
/** A response returned by a view callable. */
|
||||
private class PyramidReturnResponse extends Http::Server::HttpResponse::Range {
|
||||
PyramidReturnResponse() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(View::ViewCallable vc) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
) and
|
||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() and
|
||||
not this = instance()
|
||||
}
|
||||
|
||||
|
||||
@@ -2254,9 +2254,8 @@ module StdlibPrivate {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
WsgirefSimpleServerApplicationReturn() {
|
||||
exists(WsgirefSimpleServerApplication requestHandler, Return ret |
|
||||
ret.getScope() = requestHandler and
|
||||
node.getNode() = ret.getValue()
|
||||
exists(WsgirefSimpleServerApplication requestHandler |
|
||||
node = requestHandler.getAReturnValueFlowNode()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -182,10 +182,7 @@ private module Twisted {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
TwistedResourceRenderMethodReturn() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(TwistedResourceRenderMethod meth) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -77,7 +77,7 @@ module Stages {
|
||||
or
|
||||
exists(any(AstExtended::AstNode n).getParentNode())
|
||||
or
|
||||
exists(PyFlow::ControlFlowNode cfg, AstExtended::AstNode n | cfg.getNode() = n)
|
||||
exists(any(AstExtended::AstNode n).getAFlowNode())
|
||||
or
|
||||
exists(any(PyFlow::BasicBlock b).getImmediateDominator())
|
||||
or
|
||||
|
||||
@@ -56,9 +56,8 @@ abstract class CallableObjectInternal extends ObjectInternal {
|
||||
/** A Python function. */
|
||||
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
|
||||
override Function getScope() {
|
||||
exists(CallableExpr expr, ControlFlowNode exprCfg |
|
||||
exprCfg.getNode() = expr and
|
||||
this = TPythonFunctionObject(exprCfg) and
|
||||
exists(CallableExpr expr |
|
||||
this = TPythonFunctionObject(expr.getAFlowNode()) and
|
||||
result = expr.getInnerScope()
|
||||
)
|
||||
}
|
||||
@@ -81,12 +80,11 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
|
||||
|
||||
pragma[nomagic]
|
||||
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
||||
exists(Function func, Return ret, ControlFlowNode rval, ControlFlowNode forigin |
|
||||
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
|
||||
func = this.getScope() and
|
||||
callee.appliesToScope(func)
|
||||
|
|
||||
ret.getScope() = func and
|
||||
rval.getNode() = ret.getValue() and
|
||||
rval = func.getAReturnValueFlowNode() and
|
||||
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
|
||||
origin = CfgOrigin::fromCfgNode(forigin)
|
||||
)
|
||||
@@ -162,11 +160,10 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
|
||||
}
|
||||
|
||||
private BasicBlock blockReturningNone(Function func) {
|
||||
exists(Return ret, ControlFlowNode ret_ |
|
||||
exists(Return ret |
|
||||
not exists(ret.getValue()) and
|
||||
ret.getScope() = func and
|
||||
ret_.getNode() = ret and
|
||||
result = ret_.getBasicBlock()
|
||||
result = ret.getAFlowNode().getBasicBlock()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -113,9 +113,8 @@ abstract class ClassObjectInternal extends ObjectInternal {
|
||||
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
|
||||
/** Gets the scope for this Python class */
|
||||
Class getScope() {
|
||||
exists(ClassExpr expr, ControlFlowNode exprCfg |
|
||||
exprCfg.getNode() = expr and
|
||||
this = TPythonClassObject(exprCfg) and
|
||||
exists(ClassExpr expr |
|
||||
this = TPythonClassObject(expr.getAFlowNode()) and
|
||||
result = expr.getInnerScope()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -745,12 +745,7 @@ class PythonFunctionValue extends FunctionValue {
|
||||
override int maxParameters() { result = this.getScope().getMaxPositionalArguments() }
|
||||
|
||||
/** Gets a control flow node corresponding to a return statement in this function */
|
||||
ControlFlowNode getAReturnedNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this.getScope() and
|
||||
result.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
|
||||
|
||||
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
|
||||
|
||||
|
||||
@@ -387,7 +387,7 @@ private PythonClassObjectInternal abcMetaClassObject() {
|
||||
private predicate neither_class_nor_static_method(Function f) {
|
||||
not exists(f.getADecorator())
|
||||
or
|
||||
exists(ControlFlowNode deco | deco.getNode() = f.getADecorator() |
|
||||
exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() |
|
||||
exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) |
|
||||
o != ObjectInternal::staticMethod() and
|
||||
o != ObjectInternal::classMethod()
|
||||
|
||||
@@ -711,7 +711,7 @@ private module InterModulePointsTo {
|
||||
ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin
|
||||
) {
|
||||
exists(string name, ImportExpr i |
|
||||
f.getNode() = i and
|
||||
i.getAFlowNode() = f and
|
||||
i.getImportedModuleName() = name and
|
||||
PointsToInternal::module_imported_as(value, name) and
|
||||
origin = f and
|
||||
@@ -2118,9 +2118,8 @@ module Types {
|
||||
result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0
|
||||
or
|
||||
exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() |
|
||||
exists(ObjectInternal base, ControlFlowNode baseNode |
|
||||
baseNode.getNode() = pycls.getBase(n) and
|
||||
PointsToInternal::pointsTo(baseNode, _, base, _)
|
||||
exists(ObjectInternal base |
|
||||
PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _)
|
||||
|
|
||||
result = base and base != ObjectInternal::unknown()
|
||||
or
|
||||
@@ -2224,10 +2223,7 @@ module Types {
|
||||
}
|
||||
|
||||
private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) {
|
||||
exists(CallNode deco |
|
||||
deco.getNode() = cls.getScope().getADecorator() and
|
||||
result = deco.getFunction()
|
||||
)
|
||||
result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction()
|
||||
}
|
||||
|
||||
private boolean has_six_add_metaclass(PythonClassObjectInternal cls) {
|
||||
@@ -2266,7 +2262,7 @@ module Types {
|
||||
}
|
||||
|
||||
private EssaVariable metaclass_var(Class cls) {
|
||||
result.getASourceUse().getNode() = cls.getMetaClass()
|
||||
result.getASourceUse() = cls.getMetaClass().getAFlowNode()
|
||||
or
|
||||
major_version() = 2 and
|
||||
not exists(cls.getMetaClass()) and
|
||||
|
||||
@@ -181,7 +181,7 @@ class ClassObject extends Object {
|
||||
)
|
||||
}
|
||||
|
||||
ControlFlowNode declaredMetaClass() { result.getNode() = this.getPyClass().getMetaClass() }
|
||||
ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() }
|
||||
|
||||
/** Has type inference failed to compute the full class hierarchy for this class for the reason given. */
|
||||
predicate failedInference(string reason) { Types::failedInference(this.theClass(), reason) }
|
||||
@@ -195,9 +195,8 @@ class ClassObject extends Object {
|
||||
* It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject.
|
||||
*/
|
||||
Object getProbableSingletonInstance() {
|
||||
exists(ControlFlowNodeWithPointsTo use, Expr origin, ControlFlowNode origin_ |
|
||||
origin_.getNode() = origin and
|
||||
use.refersTo(result, this, origin_)
|
||||
exists(ControlFlowNodeWithPointsTo use, Expr origin |
|
||||
use.refersTo(result, this, origin.getAFlowNode())
|
||||
|
|
||||
this.hasStaticallyUniqueInstance() and
|
||||
/* Ensure that original expression will be executed only one. */
|
||||
|
||||
@@ -427,7 +427,7 @@ class ExceptFlowNodeWithPointsTo extends ExceptFlowNode {
|
||||
}
|
||||
|
||||
private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) {
|
||||
exists(Tuple t | t = tuple.getOrigin() and result.getNode() = t.getAnElt())
|
||||
exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,8 +36,8 @@ class RangeIterationVariableFact extends PointsToExtension {
|
||||
RangeIterationVariableFact() {
|
||||
exists(For f, ControlFlowNode iterable |
|
||||
iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and
|
||||
iterable.getNode() = f.getIter() and
|
||||
this.(ControlFlowNode).getNode() = f.getTarget() and
|
||||
f.getIter().getAFlowNode() = iterable and
|
||||
f.getTarget().getAFlowNode() = this and
|
||||
exists(ObjectInternal range |
|
||||
PointsTo::pointsTo(iterable, _, range, _) and
|
||||
range.getClass() = ObjectInternal::builtin("range")
|
||||
|
||||
@@ -137,10 +137,7 @@ class PyFunctionObject extends FunctionObject {
|
||||
|
||||
/** Gets a control flow node corresponding to the value of a return statement */
|
||||
ControlFlowNodeWithPointsTo getAReturnedNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this.getFunction() and
|
||||
result.getNode() = ret.getValue()
|
||||
)
|
||||
result = this.getFunction().getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override string descriptiveString() {
|
||||
@@ -173,7 +170,7 @@ class PyFunctionObject extends FunctionObject {
|
||||
predicate unconditionallyReturnsParameter(int n) {
|
||||
exists(SsaVariable pvar |
|
||||
exists(Parameter p | p = this.getFunction().getArg(n) |
|
||||
pvar.getDefinition().getNode() = p.asName()
|
||||
p.asName().getAFlowNode() = pvar.getDefinition()
|
||||
) and
|
||||
exists(NameNode rval |
|
||||
rval = pvar.getAUse() and
|
||||
|
||||
@@ -337,7 +337,7 @@ class TupleObject extends SequenceObject {
|
||||
or
|
||||
this instanceof TupleNode
|
||||
or
|
||||
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
|
||||
exists(Function func | func.getVararg().getAFlowNode() = this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,9 +352,7 @@ module TupleObject {
|
||||
}
|
||||
|
||||
class NonEmptyTupleObject extends TupleObject {
|
||||
NonEmptyTupleObject() {
|
||||
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
|
||||
}
|
||||
NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) }
|
||||
|
||||
override boolean booleanValue() { result = true }
|
||||
}
|
||||
|
||||
@@ -48,11 +48,9 @@ class CheckClass extends ClassObject {
|
||||
self_dict = sub.getObject()
|
||||
or
|
||||
/* Indirect assignment via temporary variable */
|
||||
exists(SsaVariable v, ControlFlowNode subObjCfg, ControlFlowNode selfDictCfg |
|
||||
subObjCfg.getNode() = sub.getObject() and selfDictCfg.getNode() = self_dict
|
||||
|
|
||||
v.getAUse() = subObjCfg and
|
||||
v.getDefinition().(DefinitionNode).getValue() = selfDictCfg
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = sub.getObject().getAFlowNode() and
|
||||
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode()
|
||||
)
|
||||
) and
|
||||
a.getATarget() = sub and
|
||||
@@ -64,10 +62,9 @@ class CheckClass extends ClassObject {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate monkeyPatched(string name) {
|
||||
exists(Attribute a, ControlFlowNode objCfg |
|
||||
objCfg.getNode() = a.getObject() and
|
||||
exists(Attribute a |
|
||||
a.getCtx() instanceof Store and
|
||||
PointsTo::points_to(objCfg, _, this, _, _) and
|
||||
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and
|
||||
a.getName() = name
|
||||
)
|
||||
}
|
||||
@@ -87,9 +84,9 @@ class CheckClass extends ClassObject {
|
||||
}
|
||||
|
||||
predicate interestingUndefined(SelfAttributeRead a) {
|
||||
exists(string name, ControlFlowNode aCfg | name = a.getName() and aCfg.getNode() = a |
|
||||
exists(string name | name = a.getName() |
|
||||
this.interestingContext(a, name) and
|
||||
not this.definedInBlock(aCfg.getBasicBlock(), name)
|
||||
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -112,9 +109,8 @@ class CheckClass extends ClassObject {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate definitionInBlock(BasicBlock b, string name) {
|
||||
exists(SelfAttributeStore sa, ControlFlowNode saCfg |
|
||||
saCfg.getNode() = sa and
|
||||
saCfg.getBasicBlock() = b and
|
||||
exists(SelfAttributeStore sa |
|
||||
sa.getAFlowNode().getBasicBlock() = b and
|
||||
sa.getName() = name and
|
||||
sa.getClass() = this.getPyClass()
|
||||
)
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
predicate doesnt_reraise(ExceptStmt ex) {
|
||||
exists(ControlFlowNode exCfg | exCfg.getNode() = ex | exCfg.getBasicBlock().reachesExit())
|
||||
}
|
||||
predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() }
|
||||
|
||||
predicate catches_base_exception(ExceptStmt ex) {
|
||||
ex.getType() = API::builtin("BaseException").getAValueReachableFromSource().asExpr()
|
||||
|
||||
@@ -116,7 +116,7 @@ FunctionValue get_function_or_initializer(Value func_or_cls) {
|
||||
predicate illegally_named_parameter_objectapi(Call call, Object func, string name) {
|
||||
not func.isC() and
|
||||
name = call.getANamedArgumentName() and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call_objectapi(func)) and
|
||||
call.getAFlowNode() = get_a_call_objectapi(func) and
|
||||
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ predicate illegally_named_parameter_objectapi(Call call, Object func, string nam
|
||||
predicate illegally_named_parameter(Call call, Value func, string name) {
|
||||
not func.isBuiltin() and
|
||||
name = call.getANamedArgumentName() and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(func)) and
|
||||
call.getAFlowNode() = get_a_call(func) and
|
||||
not get_function_or_initializer(func).isLegalArgumentName(name)
|
||||
}
|
||||
|
||||
@@ -146,9 +146,7 @@ predicate too_few_args_objectapi(Call call, Object callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassObject and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call |
|
||||
callCfg = get_a_call_objectapi(callable)
|
||||
) and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and
|
||||
limit = func.minParameters() - 1
|
||||
)
|
||||
}
|
||||
@@ -174,7 +172,7 @@ predicate too_few_args(Call call, Value callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and
|
||||
call.getAFlowNode() = get_a_call(callable) and
|
||||
limit = func.minParameters() - 1
|
||||
)
|
||||
}
|
||||
@@ -193,9 +191,7 @@ predicate too_many_args_objectapi(Call call, Object callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassObject and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call |
|
||||
callCfg = get_a_call_objectapi(callable)
|
||||
) and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and
|
||||
limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_for_call_objectapi(call, callable) > limit
|
||||
@@ -215,7 +211,7 @@ predicate too_many_args(Call call, Value callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and
|
||||
call.getAFlowNode() = get_a_call(callable) and
|
||||
limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_for_call(call, callable) > limit
|
||||
|
||||
@@ -36,15 +36,11 @@ where
|
||||
exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and
|
||||
(
|
||||
exists(BasicBlock b, int i1, int i2 |
|
||||
b.getNode(i1).getNode() = k1 and
|
||||
b.getNode(i2).getNode() = k2 and
|
||||
k1.getAFlowNode() = b.getNode(i1) and
|
||||
k2.getAFlowNode() = b.getNode(i2) and
|
||||
i1 < i2
|
||||
)
|
||||
or
|
||||
exists(ControlFlowNode k1Cfg, ControlFlowNode k2Cfg |
|
||||
k1Cfg.getNode() = k1 and k2Cfg.getNode() = k2
|
||||
|
|
||||
k1Cfg.getBasicBlock().strictlyDominates(k2Cfg.getBasicBlock())
|
||||
)
|
||||
k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock())
|
||||
)
|
||||
select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten"
|
||||
|
||||
@@ -98,18 +98,16 @@ private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int en
|
||||
}
|
||||
|
||||
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
|
||||
exists(CallNode call, ControlFlowNode fmtCfg |
|
||||
call.getNode() = format_expr and fmtCfg.getNode() = fmt
|
||||
|
|
||||
exists(CallNode call | call = format_expr.getAFlowNode() |
|
||||
call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(Value::named("format")) and
|
||||
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmtCfg) and
|
||||
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmt.getAFlowNode()) and
|
||||
args = count(format_expr.getAnArg()) - 1
|
||||
or
|
||||
call.getFunction()
|
||||
.(AttrNode)
|
||||
.getObject("format")
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.pointsTo(_, fmtCfg) and
|
||||
.pointsTo(_, fmt.getAFlowNode()) and
|
||||
args = count(format_expr.getAnArg())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import python
|
||||
|
||||
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
|
||||
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(CompareNode fcomp | fcomp.getNode() = comp |
|
||||
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
|
||||
fcomp.operands(left, op, right) and
|
||||
(op instanceof Is or op instanceof IsNot)
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ private import LegacyPointsTo
|
||||
|
||||
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
|
||||
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(CompareNode fcomp | fcomp.getNode() = comp |
|
||||
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
|
||||
fcomp.operands(left, op, right) and
|
||||
(op instanceof Is or op instanceof IsNot)
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ where
|
||||
// Only relevant for Python 2, as all later versions implement true division
|
||||
major_version() = 2 and
|
||||
exists(BinaryExprNode bin, Value lval, Value rval |
|
||||
bin.getNode() = div and
|
||||
bin = div.getAFlowNode() and
|
||||
bin.getNode().getOp() instanceof Div and
|
||||
bin.getLeft().(ControlFlowNodeWithPointsTo).pointsTo(lval, left) and
|
||||
lval.getClass() = ClassValue::int_() and
|
||||
|
||||
@@ -19,9 +19,7 @@ where
|
||||
exists(Function init | init.isInitMethod() and r.getScope() = init) and
|
||||
r.getValue() = rv and
|
||||
not rv.pointsTo(Value::none_()) and
|
||||
not exists(FunctionValue f, ControlFlowNode rvCfg | rvCfg.getNode() = rv |
|
||||
f.getACall() = rvCfg and f.neverReturns()
|
||||
) and
|
||||
not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and
|
||||
// to avoid double reporting, don't trigger if returning result from other __init__ function
|
||||
not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__")
|
||||
select r, "Explicit return in __init__ method."
|
||||
|
||||
@@ -69,12 +69,7 @@ where
|
||||
returns_meaningful_value(callee) and
|
||||
not wrapped_in_try_except(call) and
|
||||
exists(int unused |
|
||||
unused =
|
||||
count(ExprStmt e |
|
||||
exists(ControlFlowNode eValCfg | eValCfg.getNode() = e.getValue() |
|
||||
eValCfg = callee.getACall()
|
||||
)
|
||||
) and
|
||||
unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and
|
||||
total = count(callee.getACall())
|
||||
|
|
||||
percentage_used = (100.0 * (total - unused) / total).floor()
|
||||
|
||||
@@ -138,12 +138,12 @@ predicate function_opens_file(FunctionValue f) {
|
||||
f = Value::named("open")
|
||||
or
|
||||
exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() |
|
||||
v.getNode() = ret.getValue().getAUse() and
|
||||
ret.getValue().getAFlowNode() = v.getAUse() and
|
||||
var_is_open(v, _)
|
||||
)
|
||||
or
|
||||
exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() |
|
||||
callee.getNode() = ret.getValue().getACall() and
|
||||
ret.getValue().getAFlowNode() = callee.getACall() and
|
||||
function_opens_file(callee)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class CredentialSink extends DataFlow::Node {
|
||||
this.(DataFlow::ArgumentNode).argumentOf(_, pos)
|
||||
)
|
||||
or
|
||||
exists(Keyword k | k.getArg() = name and this.asCfgNode().getNode() = k.getValue())
|
||||
exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this.asCfgNode())
|
||||
or
|
||||
exists(CompareNode cmp, NameNode n | n.getId() = name |
|
||||
cmp.operands(this.asCfgNode(), any(Eq eq), n)
|
||||
|
||||
@@ -25,7 +25,7 @@ from
|
||||
For loop, ControlFlowNodeWithPointsTo iter, Value str, Value seq, ControlFlowNode seq_origin,
|
||||
ControlFlowNode str_origin
|
||||
where
|
||||
iter.getNode() = loop.getIter() and
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.pointsTo(str, str_origin) and
|
||||
iter.pointsTo(seq, seq_origin) and
|
||||
has_string_type(str) and
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import python
|
||||
|
||||
predicate loop_variable_ssa(For f, Variable v, SsaVariable s) {
|
||||
s.getDefinition().getNode() = f.getTarget() and v = s.getVariable()
|
||||
f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable()
|
||||
}
|
||||
|
||||
predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) {
|
||||
|
||||
@@ -16,7 +16,7 @@ private import LegacyPointsTo
|
||||
|
||||
from For loop, ControlFlowNodeWithPointsTo iter, Value v, ClassValue t, ControlFlowNode origin
|
||||
where
|
||||
iter.getNode() = loop.getIter() and
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.pointsTo(_, v, origin) and
|
||||
v.getClass() = t and
|
||||
not t.isIterable() and
|
||||
|
||||
@@ -24,13 +24,11 @@ predicate func_with_side_effects(Expr e) {
|
||||
}
|
||||
|
||||
predicate call_with_side_effect(Call e) {
|
||||
exists(ControlFlowNode eCfg | eCfg.getNode() = e |
|
||||
eCfg =
|
||||
API::moduleImport("subprocess")
|
||||
.getMember(["call", "check_call", "check_output"])
|
||||
.getACall()
|
||||
.asCfgNode()
|
||||
)
|
||||
e.getAFlowNode() =
|
||||
API::moduleImport("subprocess")
|
||||
.getMember(["call", "check_call", "check_output"])
|
||||
.getACall()
|
||||
.asCfgNode()
|
||||
}
|
||||
|
||||
predicate probable_side_effect(Expr e) {
|
||||
|
||||
@@ -133,11 +133,7 @@ class ListComprehensionDeclaration extends ListComp {
|
||||
major_version() = 2 and
|
||||
this.getIterationVariable(_).getId() = result.getId() and
|
||||
result.getScope() = this.getScope() and
|
||||
exists(ControlFlowNode thisCfg, ControlFlowNode resultCfg |
|
||||
thisCfg.getNode() = this and resultCfg.getNode() = result
|
||||
|
|
||||
thisCfg.strictlyReaches(resultCfg)
|
||||
) and
|
||||
this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and
|
||||
result.isUse()
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user