mirror of
https://github.com/github/codeql.git
synced 2026-05-30 02:51:24 +02:00
Compare commits
3 Commits
yoff/pytho
...
bazookamus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc12740c0e | ||
|
|
acb5c0e70f | ||
|
|
6042adebae |
@@ -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"
|
||||
|
||||
@@ -30,6 +30,8 @@ class Options extends string {
|
||||
predicate overrideReturnsNull(Call call) {
|
||||
// Used in CVS:
|
||||
call.(FunctionCall).getTarget().hasGlobalName("Xstrdup")
|
||||
or
|
||||
CustomOptions::overrideReturnsNull(call) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,6 +45,8 @@ class Options extends string {
|
||||
// Used in CVS:
|
||||
call.(FunctionCall).getTarget().hasGlobalName("Xstrdup") and
|
||||
nullValue(call.getArgument(0))
|
||||
or
|
||||
CustomOptions::returnsNull(call) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,6 +65,8 @@ class Options extends string {
|
||||
f.hasGlobalOrStdName([
|
||||
"exit", "_exit", "_Exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable"
|
||||
])
|
||||
or
|
||||
CustomOptions::exits(f) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +79,8 @@ class Options extends string {
|
||||
* runtime, the program's behavior is undefined)
|
||||
*/
|
||||
predicate exprExits(Expr e) {
|
||||
e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0
|
||||
e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or
|
||||
CustomOptions::exprExits(e) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +88,10 @@ class Options extends string {
|
||||
*
|
||||
* By default holds only for `fgets`.
|
||||
*/
|
||||
predicate alwaysCheckReturnValue(Function f) { f.hasGlobalOrStdName("fgets") }
|
||||
predicate alwaysCheckReturnValue(Function f) {
|
||||
f.hasGlobalOrStdName("fgets") or
|
||||
CustomOptions::alwaysCheckReturnValue(f) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if it is reasonable to ignore the return value of function
|
||||
@@ -97,6 +107,8 @@ class Options extends string {
|
||||
// common way of sleeping using select:
|
||||
fc.getTarget().hasGlobalName("select") and
|
||||
fc.getArgument(0).getValue() = "0"
|
||||
or
|
||||
CustomOptions::okToIgnoreReturnValue(fc) // old Options.qll
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,3 +98,57 @@ class CustomMutexType extends MutexType {
|
||||
*/
|
||||
override predicate unlockAccess(FunctionCall fc, Expr arg) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.overrideReturnsNull` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate overrideReturnsNull(Call call) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.returnsNull` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate returnsNull(Call call) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.exits` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate exits(Function f) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.exprExits` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate exprExits(Expr e) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.alwaysCheckReturnValue` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate alwaysCheckReturnValue(Function f) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.okToIgnoreReturnValue` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate okToIgnoreReturnValue(FunctionCall fc) { none() }
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* Removed the deprecated `overrideReturnsNull` predicate from `Options.qll`. Use `CustomOptions.overrideReturnsNull` instead.
|
||||
* Removed the deprecated `returnsNull` predicate from `Options.qll`. Use `CustomOptions.returnsNull` instead.
|
||||
* Removed the deprecated `exits` predicate from `Options.qll`. Use `CustomOptions.exits` instead.
|
||||
* Removed the deprecated `exprExits` predicate from `Options.qll`. Use `CustomOptions.exprExits` instead.
|
||||
* Removed the deprecated `alwaysCheckReturnValue` predicate from `Options.qll`. Use `CustomOptions.alwaysCheckReturnValue` instead.
|
||||
* Removed the deprecated `okToIgnoreReturnValue` predicate from `Options.qll`. Use `CustomOptions.okToIgnoreReturnValue` instead.
|
||||
* Removed the deprecated `semmle.code.cpp.Member`. Import `semmle.code.cpp.Element` and/or `semmle.code.cpp.Type` directly.
|
||||
* Removed the deprecated `UnknownDefaultLocation` class. Use `UnknownLocation` instead.
|
||||
* Removed the deprecated `UnknownExprLocation` class. Use `UnknownLocation` instead.
|
||||
* Removed the deprecated `UnknownStmtLocation` class. Use `UnknownLocation` instead.
|
||||
* Removed the deprecated `TemplateParameter` class. Use `TypeTemplateParameter` instead.
|
||||
* Support for class resolution across link targets has been removed for databases which were created with CodeQL versions before 1.23.0.
|
||||
@@ -32,6 +32,7 @@ import semmle.code.cpp.Class
|
||||
import semmle.code.cpp.Struct
|
||||
import semmle.code.cpp.Union
|
||||
import semmle.code.cpp.Enum
|
||||
import semmle.code.cpp.Member
|
||||
import semmle.code.cpp.Field
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.MemberFunction
|
||||
|
||||
@@ -148,3 +148,28 @@ class UnknownLocation extends Location {
|
||||
this.getFile().getAbsolutePath() = "" and locations_default(this, _, 0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy location which is used when something doesn't have a location in
|
||||
* the source code but needs to have a `Location` associated with it.
|
||||
*
|
||||
* DEPRECATED: use `UnknownLocation`
|
||||
*/
|
||||
deprecated class UnknownDefaultLocation extends UnknownLocation { }
|
||||
|
||||
/**
|
||||
* A dummy location which is used when an expression doesn't have a
|
||||
* location in the source code but needs to have a `Location` associated
|
||||
* with it.
|
||||
*
|
||||
* DEPRECATED: use `UnknownLocation`
|
||||
*/
|
||||
deprecated class UnknownExprLocation extends UnknownLocation { }
|
||||
|
||||
/**
|
||||
* A dummy location which is used when a statement doesn't have a location
|
||||
* in the source code but needs to have a `Location` associated with it.
|
||||
*
|
||||
* DEPRECATED: use `UnknownLocation`
|
||||
*/
|
||||
deprecated class UnknownStmtLocation extends UnknownLocation { }
|
||||
|
||||
6
cpp/ql/lib/semmle/code/cpp/Member.qll
Normal file
6
cpp/ql/lib/semmle/code/cpp/Member.qll
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* DEPRECATED: import `semmle.code.cpp.Element` and/or `semmle.code.cpp.Type` directly as required.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Type
|
||||
@@ -35,6 +35,13 @@ class NonTypeTemplateParameter extends Literal, TemplateParameterImpl {
|
||||
override string getAPrimaryQlClass() { result = "NonTypeTemplateParameter" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `typename` (or `class`) template parameter.
|
||||
*
|
||||
* DEPRECATED: Use `TypeTemplateParameter` instead.
|
||||
*/
|
||||
deprecated class TemplateParameter = TypeTemplateParameter;
|
||||
|
||||
/**
|
||||
* A C++ `typename` (or `class`) template parameter.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,59 @@
|
||||
import semmle.code.cpp.Type
|
||||
|
||||
/** For upgraded databases without mangled name info. */
|
||||
pragma[noinline]
|
||||
private string getTopLevelClassName(@usertype c) {
|
||||
not mangled_name(_, _, _) and
|
||||
isClass(c) and
|
||||
usertypes(c, result, _) and
|
||||
not namespacembrs(_, c) and // not in a namespace
|
||||
not member(_, _, c) and // not in some structure
|
||||
not class_instantiation(c, _) // not a template instantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* For upgraded databases without mangled name info.
|
||||
* Holds if `d` is a unique complete class named `name`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate existsCompleteWithName(string name, @usertype d) {
|
||||
not mangled_name(_, _, _) and
|
||||
is_complete(d) and
|
||||
name = getTopLevelClassName(d) and
|
||||
onlyOneCompleteClassExistsWithName(name)
|
||||
}
|
||||
|
||||
/** For upgraded databases without mangled name info. */
|
||||
pragma[noinline]
|
||||
private predicate onlyOneCompleteClassExistsWithName(string name) {
|
||||
not mangled_name(_, _, _) and
|
||||
strictcount(@usertype c | is_complete(c) and getTopLevelClassName(c) = name) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* For upgraded databases without mangled name info.
|
||||
* Holds if `c` is an incomplete class named `name`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate existsIncompleteWithName(string name, @usertype c) {
|
||||
not mangled_name(_, _, _) and
|
||||
not is_complete(c) and
|
||||
name = getTopLevelClassName(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* For upgraded databases without mangled name info.
|
||||
* Holds if `c` is an incomplete class, and there exists a unique complete class `d`
|
||||
* with the same name.
|
||||
*/
|
||||
private predicate oldHasCompleteTwin(@usertype c, @usertype d) {
|
||||
not mangled_name(_, _, _) and
|
||||
exists(string name |
|
||||
existsIncompleteWithName(name, c) and
|
||||
existsCompleteWithName(name, d)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private @mangledname getClassMangledName(@usertype c) {
|
||||
isClass(c) and
|
||||
@@ -49,7 +103,10 @@ private module Cached {
|
||||
@usertype resolveClass(@usertype c) {
|
||||
hasCompleteTwin(c, result)
|
||||
or
|
||||
oldHasCompleteTwin(c, result)
|
||||
or
|
||||
not hasCompleteTwin(c, _) and
|
||||
not oldHasCompleteTwin(c, _) and
|
||||
result = c
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
| file://:0:0:0:0 | E<C>'s friend | loop.cpp:5:26:5:26 | E<D> |
|
||||
| file://:0:0:0:0 | E<C>'s friend | loop.cpp:5:26:5:26 | E<T> |
|
||||
| file://:0:0:0:0 | E<C>'s friend | loop.cpp:5:26:5:29 | E<D> |
|
||||
| file://:0:0:0:0 | E<C>'s friend | loop.cpp:10:26:10:26 | F<D> |
|
||||
| file://:0:0:0:0 | E<C>'s friend | loop.cpp:10:26:10:26 | F<T> |
|
||||
| file://:0:0:0:0 | E<C>'s friend | loop.cpp:10:26:10:29 | F<D> |
|
||||
| file://:0:0:0:0 | E<D>'s friend | loop.cpp:5:26:5:26 | E<C> |
|
||||
| file://:0:0:0:0 | E<D>'s friend | loop.cpp:5:26:5:26 | E<T> |
|
||||
| file://:0:0:0:0 | E<D>'s friend | loop.cpp:5:26:5:29 | E<C> |
|
||||
| file://:0:0:0:0 | E<D>'s friend | loop.cpp:10:26:10:26 | F<D> |
|
||||
| file://:0:0:0:0 | E<D>'s friend | loop.cpp:10:26:10:26 | F<T> |
|
||||
| file://:0:0:0:0 | E<D>'s friend | loop.cpp:10:26:10:29 | F<D> |
|
||||
| file://:0:0:0:0 | F<D>'s friend | loop.cpp:5:26:5:26 | E<C> |
|
||||
| file://:0:0:0:0 | F<D>'s friend | loop.cpp:5:26:5:26 | E<D> |
|
||||
| file://:0:0:0:0 | F<D>'s friend | loop.cpp:5:26:5:26 | E<T> |
|
||||
| file://:0:0:0:0 | F<D>'s friend | loop.cpp:5:26:5:29 | E<C> |
|
||||
| file://:0:0:0:0 | F<D>'s friend | loop.cpp:5:26:5:29 | E<D> |
|
||||
| loop.cpp:6:5:6:5 | E<T>'s friend | loop.cpp:5:26:5:26 | E<T> |
|
||||
| loop.cpp:7:5:7:5 | E<T>'s friend | loop.cpp:7:36:7:36 | F<U> |
|
||||
| loop.cpp:11:5:11:5 | F<T>'s friend | loop.cpp:11:36:11:36 | E<U> |
|
||||
|
||||
@@ -664,7 +664,7 @@ namespace Semmle.Extraction.CSharp
|
||||
// Find the (possibly unbound) original extension method that maps to this implementation (if any).
|
||||
var unboundDeclaration = extensions.SelectMany(e => e.GetMembers())
|
||||
.OfType<IMethodSymbol>()
|
||||
.FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation?.ConstructedFrom, method.ConstructedFrom));
|
||||
.FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method.ConstructedFrom));
|
||||
|
||||
var isFullyConstructed = method.IsBoundGenericMethod();
|
||||
if (isFullyConstructed && unboundDeclaration?.ContainingType is INamedTypeSymbol extensionType)
|
||||
|
||||
@@ -69,7 +69,6 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
}
|
||||
|
||||
Overrides(trapFile);
|
||||
ExtractRefReturn(trapFile, Symbol, this);
|
||||
|
||||
if (Symbol.FromSource() && !HasBody)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ source https://api.nuget.org/v3/index.json
|
||||
# behave like nuget in choosing transitive dependency versions
|
||||
strategy: max
|
||||
|
||||
nuget Basic.CompilerLog.Util 0.9.39
|
||||
nuget Basic.CompilerLog.Util 0.9.25
|
||||
nuget Mono.Posix.NETStandard
|
||||
nuget Newtonsoft.Json
|
||||
nuget NuGet.Versioning
|
||||
@@ -12,7 +12,7 @@ nuget xunit
|
||||
nuget xunit.runner.visualstudio
|
||||
nuget xunit.runner.utility
|
||||
nuget Microsoft.NET.Test.Sdk
|
||||
nuget Microsoft.CodeAnalysis.CSharp 5.3.0
|
||||
nuget Microsoft.CodeAnalysis 5.3.0
|
||||
nuget Microsoft.Build 18.6.3
|
||||
nuget Microsoft.CodeAnalysis.CSharp 5.0.0
|
||||
nuget Microsoft.CodeAnalysis 5.0.0
|
||||
nuget Microsoft.Build 18.0.2
|
||||
nuget Microsoft.VisualStudio.SolutionPersistence
|
||||
|
||||
100
csharp/paket.lock
generated
100
csharp/paket.lock
generated
@@ -3,42 +3,45 @@ STRATEGY: MAX
|
||||
RESTRICTION: == net10.0
|
||||
NUGET
|
||||
remote: https://api.nuget.org/v3/index.json
|
||||
Basic.CompilerLog.Util (0.9.39)
|
||||
Basic.CompilerLog.Util (0.9.25)
|
||||
MessagePack (>= 3.1.4)
|
||||
Microsoft.Bcl.Memory (>= 10.0.7)
|
||||
Microsoft.Bcl.Memory (>= 9.0.10)
|
||||
Microsoft.CodeAnalysis (>= 4.8)
|
||||
Microsoft.CodeAnalysis.CSharp (>= 4.8)
|
||||
Microsoft.CodeAnalysis.VisualBasic (>= 4.8)
|
||||
Microsoft.Extensions.ObjectPool (>= 10.0.7)
|
||||
MSBuild.StructuredLogger (>= 2.3.178)
|
||||
Microsoft.Extensions.ObjectPool (>= 9.0.10)
|
||||
MSBuild.StructuredLogger (>= 2.3.71)
|
||||
NaturalSort.Extension (>= 4.4)
|
||||
NuGet.Versioning (>= 6.14)
|
||||
Humanizer.Core (3.0.10)
|
||||
MessagePack (3.1.6)
|
||||
MessagePack.Annotations (>= 3.1.6)
|
||||
MessagePackAnalyzer (>= 3.1.6)
|
||||
MessagePack (3.1.4)
|
||||
MessagePack.Annotations (>= 3.1.4)
|
||||
MessagePackAnalyzer (>= 3.1.4)
|
||||
Microsoft.NET.StringTools (>= 17.11.4)
|
||||
MessagePack.Annotations (3.1.6)
|
||||
MessagePackAnalyzer (3.1.6)
|
||||
MessagePack.Annotations (3.1.4)
|
||||
MessagePackAnalyzer (3.1.4)
|
||||
Microsoft.Bcl.AsyncInterfaces (10.0.8)
|
||||
Microsoft.Bcl.Memory (10.0.8)
|
||||
Microsoft.Build (18.6.3)
|
||||
Microsoft.Build.Framework (>= 18.6.3)
|
||||
System.Configuration.ConfigurationManager (>= 10.0.3)
|
||||
System.Diagnostics.EventLog (>= 10.0.3)
|
||||
System.Reflection.MetadataLoadContext (>= 10.0.3)
|
||||
System.Security.Cryptography.ProtectedData (>= 10.0.3)
|
||||
Microsoft.Build.Framework (18.6.3)
|
||||
Microsoft.NET.StringTools (>= 18.6.3)
|
||||
Microsoft.Build.Utilities.Core (18.6.3)
|
||||
Microsoft.Build.Framework (>= 18.6.3)
|
||||
System.Configuration.ConfigurationManager (>= 10.0.3)
|
||||
System.Diagnostics.EventLog (>= 10.0.3)
|
||||
System.Security.Cryptography.ProtectedData (>= 10.0.3)
|
||||
Microsoft.CodeAnalysis (5.3)
|
||||
Microsoft.Build (18.0.2)
|
||||
Microsoft.Build.Framework (>= 18.0.2)
|
||||
Microsoft.NET.StringTools (>= 18.0.2)
|
||||
System.Configuration.ConfigurationManager (>= 9.0)
|
||||
System.Diagnostics.EventLog (>= 9.0)
|
||||
System.Reflection.MetadataLoadContext (>= 9.0)
|
||||
System.Security.Cryptography.ProtectedData (>= 9.0.6)
|
||||
Microsoft.Build.Framework (18.4)
|
||||
Microsoft.Build.Utilities.Core (18.4)
|
||||
Microsoft.Build.Framework (>= 18.4)
|
||||
Microsoft.NET.StringTools (>= 18.4)
|
||||
System.Configuration.ConfigurationManager (>= 10.0.1)
|
||||
System.Diagnostics.EventLog (>= 10.0.1)
|
||||
System.Security.Cryptography.ProtectedData (>= 10.0.1)
|
||||
Microsoft.CodeAnalysis (5.0)
|
||||
Humanizer.Core (>= 2.14.1)
|
||||
Microsoft.Bcl.AsyncInterfaces (>= 9.0)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 5.3.0-2.25625.1)
|
||||
Microsoft.CodeAnalysis.CSharp.Workspaces (5.3)
|
||||
Microsoft.CodeAnalysis.VisualBasic.Workspaces (5.3)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 3.11)
|
||||
Microsoft.CodeAnalysis.CSharp.Workspaces (5.0)
|
||||
Microsoft.CodeAnalysis.VisualBasic.Workspaces (5.0)
|
||||
System.Buffers (>= 4.6)
|
||||
System.Collections.Immutable (>= 9.0)
|
||||
System.Composition (>= 9.0)
|
||||
@@ -51,36 +54,36 @@ NUGET
|
||||
System.Threading.Channels (>= 8.0)
|
||||
System.Threading.Tasks.Extensions (>= 4.6)
|
||||
Microsoft.CodeAnalysis.Analyzers (5.3)
|
||||
Microsoft.CodeAnalysis.Common (5.3)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 5.3.0-2.25625.1)
|
||||
Microsoft.CodeAnalysis.CSharp (5.3)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 5.3.0-2.25625.1)
|
||||
Microsoft.CodeAnalysis.Common (5.3)
|
||||
Microsoft.CodeAnalysis.CSharp.Workspaces (5.3)
|
||||
Microsoft.CodeAnalysis.Common (5.0)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 3.11)
|
||||
Microsoft.CodeAnalysis.CSharp (5.0)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 3.11)
|
||||
Microsoft.CodeAnalysis.Common (5.0)
|
||||
Microsoft.CodeAnalysis.CSharp.Workspaces (5.0)
|
||||
Humanizer.Core (>= 2.14.1)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 5.3.0-2.25625.1)
|
||||
Microsoft.CodeAnalysis.Common (5.3)
|
||||
Microsoft.CodeAnalysis.CSharp (5.3)
|
||||
Microsoft.CodeAnalysis.Workspaces.Common (5.3)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 3.11)
|
||||
Microsoft.CodeAnalysis.Common (5.0)
|
||||
Microsoft.CodeAnalysis.CSharp (5.0)
|
||||
Microsoft.CodeAnalysis.Workspaces.Common (5.0)
|
||||
System.Composition (>= 9.0)
|
||||
Microsoft.CodeAnalysis.VisualBasic (5.3)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 5.3.0-2.25625.1)
|
||||
Microsoft.CodeAnalysis.Common (5.3)
|
||||
Microsoft.CodeAnalysis.VisualBasic.Workspaces (5.3)
|
||||
Microsoft.CodeAnalysis.VisualBasic (5.0)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 3.11)
|
||||
Microsoft.CodeAnalysis.Common (5.0)
|
||||
Microsoft.CodeAnalysis.VisualBasic.Workspaces (5.0)
|
||||
Humanizer.Core (>= 2.14.1)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 5.3.0-2.25625.1)
|
||||
Microsoft.CodeAnalysis.Common (5.3)
|
||||
Microsoft.CodeAnalysis.VisualBasic (5.3)
|
||||
Microsoft.CodeAnalysis.Workspaces.Common (5.3)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 3.11)
|
||||
Microsoft.CodeAnalysis.Common (5.0)
|
||||
Microsoft.CodeAnalysis.VisualBasic (5.0)
|
||||
Microsoft.CodeAnalysis.Workspaces.Common (5.0)
|
||||
System.Composition (>= 9.0)
|
||||
Microsoft.CodeAnalysis.Workspaces.Common (5.3)
|
||||
Microsoft.CodeAnalysis.Workspaces.Common (5.0)
|
||||
Humanizer.Core (>= 2.14.1)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 5.3.0-2.25625.1)
|
||||
Microsoft.CodeAnalysis.Common (5.3)
|
||||
Microsoft.CodeAnalysis.Analyzers (>= 3.11)
|
||||
Microsoft.CodeAnalysis.Common (5.0)
|
||||
System.Composition (>= 9.0)
|
||||
Microsoft.CodeCoverage (18.5.1)
|
||||
Microsoft.Extensions.ObjectPool (10.0.8)
|
||||
Microsoft.NET.StringTools (18.6.3)
|
||||
Microsoft.NET.StringTools (18.4)
|
||||
Microsoft.NET.Test.Sdk (18.5.1)
|
||||
Microsoft.CodeCoverage (>= 18.5.1)
|
||||
Microsoft.TestPlatform.TestHost (>= 18.5.1)
|
||||
@@ -94,6 +97,7 @@ NUGET
|
||||
MSBuild.StructuredLogger (2.3.204)
|
||||
Microsoft.Build.Framework (>= 17.5)
|
||||
Microsoft.Build.Utilities.Core (>= 17.5)
|
||||
NaturalSort.Extension (4.4.1)
|
||||
Newtonsoft.Json (13.0.4)
|
||||
NuGet.Versioning (7.6)
|
||||
System.Buffers (4.6.1)
|
||||
|
||||
31
csharp/paket.main.bzl
generated
31
csharp/paket.main.bzl
generated
File diff suppressed because one or more lines are too long
@@ -22,6 +22,7 @@
|
||||
| [...]/csharp/tools/[...]/Microsoft.Win32.Primitives.dll |
|
||||
| [...]/csharp/tools/[...]/Microsoft.Win32.Registry.dll |
|
||||
| [...]/csharp/tools/[...]/Mono.Posix.NETStandard.dll |
|
||||
| [...]/csharp/tools/[...]/NaturalSort.Extension.dll |
|
||||
| [...]/csharp/tools/[...]/Newtonsoft.Json.dll |
|
||||
| [...]/csharp/tools/[...]/NuGet.Versioning.dll |
|
||||
| [...]/csharp/tools/[...]/StructuredLogger.dll |
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved call target resolution for ref-return properties and indexers.
|
||||
@@ -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 csharp as CS
|
||||
private import internal.rangeanalysis.BoundSpecific
|
||||
private import internal.rangeanalysis.BoundSpecific as BoundSpecific
|
||||
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 BoundImpl = SharedBound::Bound<CS::Location, BoundSpecific::BoundDefs>;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/** Gets an expression that equals this bound plus `delta`. */
|
||||
abstract Expr getExpr(int delta);
|
||||
|
||||
/** Gets an expression that equals this bound. */
|
||||
Expr getExpr() { result = this.getExpr(0) }
|
||||
|
||||
/** Gets the location of this bound. */
|
||||
abstract Location getLocation();
|
||||
}
|
||||
class Bound = BoundImpl::Bound;
|
||||
|
||||
/**
|
||||
* 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" }
|
||||
|
||||
override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta }
|
||||
|
||||
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
|
||||
}
|
||||
class ZeroBound = BoundImpl::ZeroBound;
|
||||
|
||||
/**
|
||||
* 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() }
|
||||
}
|
||||
class SsaBound = BoundImpl::SsaBound;
|
||||
|
||||
/**
|
||||
* 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() }
|
||||
}
|
||||
class ExprBound = BoundImpl::ExprBound;
|
||||
@@ -7,16 +7,26 @@ 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;
|
||||
private import codeql.rangeanalysis.Bound as SharedBound
|
||||
|
||||
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
|
||||
predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.getExpr()) }
|
||||
|
||||
|
||||
module BoundDefs implements SharedBound::BoundDefinitions<CS::Location> {
|
||||
class Type = CS::Type;
|
||||
|
||||
class SsaVariable = SU::SsaVariable;
|
||||
|
||||
class SsaSourceVariable = Ssa::SourceVariable;
|
||||
|
||||
class Expr = CS::ControlFlowNodes::ExprNode;
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,16 +766,7 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr {
|
||||
}
|
||||
|
||||
override Accessor getWriteTarget() {
|
||||
this instanceof AssignableWrite and
|
||||
exists(Property p | p = this.getProperty() |
|
||||
result = p.getSetter()
|
||||
or
|
||||
result =
|
||||
any(Getter g |
|
||||
g = p.getGetter() and
|
||||
g.getAnnotatedReturnType().isRef()
|
||||
)
|
||||
)
|
||||
this instanceof AssignableWrite and result = this.getProperty().getSetter()
|
||||
}
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
@@ -810,16 +801,7 @@ class IndexerCall extends AccessorCall, IndexerAccessExpr {
|
||||
}
|
||||
|
||||
override Accessor getWriteTarget() {
|
||||
this instanceof AssignableWrite and
|
||||
exists(Indexer i | i = this.getIndexer() |
|
||||
result = i.getSetter()
|
||||
or
|
||||
result =
|
||||
any(Getter g |
|
||||
g = i.getGetter() and
|
||||
g.getAnnotatedReturnType().isRef()
|
||||
)
|
||||
)
|
||||
this instanceof AssignableWrite and result = this.getIndexer().getSetter()
|
||||
}
|
||||
|
||||
override Expr getArgument(int i) {
|
||||
|
||||
@@ -227,7 +227,7 @@ returnTypes
|
||||
| NullableRefTypes.cs:107:26:107:36 | ReturnsRef5 | readonly MyClass! |
|
||||
| NullableRefTypes.cs:108:26:108:36 | ReturnsRef6 | readonly MyClass! |
|
||||
| NullableRefTypes.cs:110:10:110:20 | Parameters1 | Void! |
|
||||
| NullableRefTypes.cs:113:32:113:44 | get_RefProperty | ref MyClass! |
|
||||
| NullableRefTypes.cs:113:32:113:44 | get_RefProperty | MyClass! |
|
||||
| NullableRefTypes.cs:116:7:116:23 | <object initializer> | Void |
|
||||
| NullableRefTypes.cs:116:7:116:23 | ToStringWithTypes | Void! |
|
||||
| NullableRefTypes.cs:136:7:136:24 | <object initializer> | Void |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class SBCS
|
||||
class SBCS
|
||||
{
|
||||
string sbcs = "<22>";
|
||||
string sbcs = "<22>";
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
| indexers.cs:24:21:24:24 | Item | indexers.cs:62:22:62:29 | access to indexer | indexers.cs:26:13:26:15 | get_Item |
|
||||
| indexers.cs:24:21:24:24 | Item | indexers.cs:65:25:65:32 | access to indexer | indexers.cs:34:13:34:15 | set_Item |
|
||||
| indexers.cs:143:24:143:27 | Item | indexers.cs:156:13:156:16 | access to indexer | indexers.cs:145:13:145:15 | get_Item |
|
||||
| indexers.cs:143:24:143:27 | Item | indexers.cs:157:21:157:24 | access to indexer | indexers.cs:145:13:145:15 | get_Item |
|
||||
@@ -1,8 +0,0 @@
|
||||
import csharp
|
||||
|
||||
from IndexerCall ic, Indexer i, Accessor target
|
||||
where
|
||||
ic.getIndexer() = i and
|
||||
ic.getTarget() = target and
|
||||
i.fromSource()
|
||||
select i, ic, target
|
||||
@@ -360,57 +360,3 @@ indexers.cs:
|
||||
# 130| 4: [BlockStmt] {...}
|
||||
# 130| 0: [ReturnStmt] return ...;
|
||||
# 130| 0: [IntLiteral] 0
|
||||
# 134| 5: [RefStruct] S
|
||||
# 136| 6: [Field] x
|
||||
# 136| -1: [TypeMention] int
|
||||
# 138| 7: [InstanceConstructor] S
|
||||
#-----| 2: (Parameters)
|
||||
# 138| 0: [Parameter] v
|
||||
# 138| -1: [TypeMention] int
|
||||
# 139| 4: [BlockStmt] {...}
|
||||
# 140| 0: [ExprStmt] ...;
|
||||
# 140| 0: [AssignExpr] ... = ...
|
||||
# 140| 0: [FieldAccess] access to field x
|
||||
# 140| 1: [RefExpr] ref ...
|
||||
# 140| 0: [ParameterAccess] access to parameter v
|
||||
# 143| 8: [Indexer] Item
|
||||
# 143| -1: [TypeMention] int
|
||||
#-----| 1: (Parameters)
|
||||
# 143| 0: [Parameter] i
|
||||
# 143| -1: [TypeMention] int
|
||||
# 145| 3: [Getter] get_Item
|
||||
#-----| 2: (Parameters)
|
||||
# 143| 0: [Parameter] i
|
||||
# 145| 4: [BlockStmt] {...}
|
||||
# 145| 0: [ReturnStmt] return ...;
|
||||
# 145| 0: [RefExpr] ref ...
|
||||
# 145| 0: [FieldAccess] access to field x
|
||||
# 149| 6: [Class] TestRefReturns
|
||||
# 151| 6: [Method] M
|
||||
# 151| -1: [TypeMention] Void
|
||||
# 152| 4: [BlockStmt] {...}
|
||||
# 153| 0: [LocalVariableDeclStmt] ... ...;
|
||||
# 153| 0: [LocalVariableDeclAndInitExpr] Int32 a = ...
|
||||
# 153| -1: [TypeMention] int
|
||||
# 153| 0: [LocalVariableAccess] access to local variable a
|
||||
# 153| 1: [IntLiteral] 0
|
||||
# 155| 1: [LocalVariableDeclStmt] ... ...;
|
||||
# 155| 0: [LocalVariableDeclAndInitExpr] S s = ...
|
||||
# 155| -1: [TypeMention] S
|
||||
# 155| 0: [LocalVariableAccess] access to local variable s
|
||||
# 155| 1: [ObjectCreation] object creation of type S
|
||||
# 155| -1: [TypeMention] S
|
||||
# 155| 0: [LocalVariableAccess] access to local variable a
|
||||
# 156| 2: [ExprStmt] ...;
|
||||
# 156| 0: [AssignExpr] ... = ...
|
||||
# 156| 0: [IndexerCall] access to indexer
|
||||
# 156| -1: [LocalVariableAccess] access to local variable s
|
||||
# 156| 0: [IntLiteral] 0
|
||||
# 156| 1: [IntLiteral] 1
|
||||
# 157| 3: [LocalVariableDeclStmt] ... ...;
|
||||
# 157| 0: [LocalVariableDeclAndInitExpr] Int32 x = ...
|
||||
# 157| -1: [TypeMention] int
|
||||
# 157| 0: [LocalVariableAccess] access to local variable x
|
||||
# 157| 1: [IndexerCall] access to indexer
|
||||
# 157| -1: [LocalVariableAccess] access to local variable s
|
||||
# 157| 0: [IntLiteral] 0
|
||||
|
||||
@@ -130,31 +130,4 @@ namespace Indexers
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct S
|
||||
{
|
||||
private ref int x;
|
||||
|
||||
public S(ref int v)
|
||||
{
|
||||
x = ref v;
|
||||
}
|
||||
|
||||
public ref int this[int i]
|
||||
{
|
||||
get { return ref x; }
|
||||
}
|
||||
}
|
||||
|
||||
public class TestRefReturns
|
||||
{
|
||||
public void M()
|
||||
{
|
||||
int a = 0;
|
||||
|
||||
S s = new S(ref a);
|
||||
s[0] = 1;
|
||||
var x = s[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,50 +246,3 @@ properties.cs:
|
||||
# 133| 0: [FieldAccess] access to field Prop.field
|
||||
# 133| 1: [ParameterAccess] access to parameter value
|
||||
# 130| 7: [Field] Prop.field
|
||||
# 137| 11: [RefStruct] S
|
||||
# 139| 6: [Field] x
|
||||
# 139| -1: [TypeMention] int
|
||||
# 141| 7: [InstanceConstructor] S
|
||||
#-----| 2: (Parameters)
|
||||
# 141| 0: [Parameter] v
|
||||
# 141| -1: [TypeMention] int
|
||||
# 142| 4: [BlockStmt] {...}
|
||||
# 143| 0: [ExprStmt] ...;
|
||||
# 143| 0: [AssignExpr] ... = ...
|
||||
# 143| 0: [FieldAccess] access to field x
|
||||
# 143| 1: [RefExpr] ref ...
|
||||
# 143| 0: [ParameterAccess] access to parameter v
|
||||
# 146| 8: [Property] Prop
|
||||
# 146| -1: [TypeMention] int
|
||||
# 148| 3: [Getter] get_Prop
|
||||
# 148| 4: [BlockStmt] {...}
|
||||
# 148| 0: [ReturnStmt] return ...;
|
||||
# 148| 0: [RefExpr] ref ...
|
||||
# 148| 0: [FieldAccess] access to field x
|
||||
# 152| 12: [Class] TestRefReturns
|
||||
# 154| 6: [Method] M
|
||||
# 154| -1: [TypeMention] Void
|
||||
# 155| 4: [BlockStmt] {...}
|
||||
# 156| 0: [LocalVariableDeclStmt] ... ...;
|
||||
# 156| 0: [LocalVariableDeclAndInitExpr] Int32 a = ...
|
||||
# 156| -1: [TypeMention] int
|
||||
# 156| 0: [LocalVariableAccess] access to local variable a
|
||||
# 156| 1: [IntLiteral] 0
|
||||
# 158| 1: [LocalVariableDeclStmt] ... ...;
|
||||
# 158| 0: [LocalVariableDeclAndInitExpr] S s = ...
|
||||
# 158| -1: [TypeMention] S
|
||||
# 158| 0: [LocalVariableAccess] access to local variable s
|
||||
# 158| 1: [ObjectCreation] object creation of type S
|
||||
# 158| -1: [TypeMention] S
|
||||
# 158| 0: [LocalVariableAccess] access to local variable a
|
||||
# 159| 2: [ExprStmt] ...;
|
||||
# 159| 0: [AssignExpr] ... = ...
|
||||
# 159| 0: [PropertyCall] access to property Prop
|
||||
# 159| -1: [LocalVariableAccess] access to local variable s
|
||||
# 159| 1: [IntLiteral] 1
|
||||
# 160| 3: [LocalVariableDeclStmt] ... ...;
|
||||
# 160| 0: [LocalVariableDeclAndInitExpr] Int32 x = ...
|
||||
# 160| -1: [TypeMention] int
|
||||
# 160| 0: [LocalVariableAccess] access to local variable x
|
||||
# 160| 1: [PropertyCall] access to property Prop
|
||||
# 160| -1: [LocalVariableAccess] access to local variable s
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
| Prop.field |
|
||||
| caption |
|
||||
| next |
|
||||
| x |
|
||||
| y |
|
||||
| z |
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
| properties.cs:12:23:12:29 | Caption | properties.cs:29:13:29:28 | access to property Caption | properties.cs:17:13:17:15 | set_Caption |
|
||||
| properties.cs:12:23:12:29 | Caption | properties.cs:30:24:30:39 | access to property Caption | properties.cs:15:13:15:15 | get_Caption |
|
||||
| properties.cs:57:20:57:20 | X | properties.cs:61:13:61:13 | access to property X | properties.cs:57:37:57:39 | set_X |
|
||||
| properties.cs:58:20:58:20 | Y | properties.cs:62:13:62:13 | access to property Y | properties.cs:58:37:58:39 | set_Y |
|
||||
| properties.cs:70:28:70:28 | X | properties.cs:82:46:82:51 | access to property X | properties.cs:70:32:70:34 | get_X |
|
||||
| properties.cs:71:28:71:28 | Y | properties.cs:83:39:83:44 | access to property Y | properties.cs:74:13:74:15 | set_Y |
|
||||
| properties.cs:146:24:146:27 | Prop | properties.cs:159:13:159:18 | access to property Prop | properties.cs:148:13:148:15 | get_Prop |
|
||||
| properties.cs:146:24:146:27 | Prop | properties.cs:160:21:160:26 | access to property Prop | properties.cs:148:13:148:15 | get_Prop |
|
||||
@@ -1,8 +0,0 @@
|
||||
import csharp
|
||||
|
||||
from PropertyCall pc, Property p, Accessor target
|
||||
where
|
||||
pc.getProperty() = p and
|
||||
pc.getTarget() = target and
|
||||
p.fromSource()
|
||||
select p, pc, target
|
||||
@@ -133,31 +133,4 @@ namespace Properties
|
||||
set { field = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct S
|
||||
{
|
||||
private ref int x;
|
||||
|
||||
public S(ref int v)
|
||||
{
|
||||
x = ref v;
|
||||
}
|
||||
|
||||
public ref int Prop
|
||||
{
|
||||
get { return ref x; }
|
||||
}
|
||||
}
|
||||
|
||||
public class TestRefReturns
|
||||
{
|
||||
public void M()
|
||||
{
|
||||
int a = 0;
|
||||
|
||||
S s = new S(ref a);
|
||||
s.Prop = 1;
|
||||
var x = s.Prop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
| Quality.cs:26:19:26:26 | access to indexer | Call without target $@. | Quality.cs:26:19:26:26 | access to indexer | access to indexer |
|
||||
| Quality.cs:29:21:29:27 | access to indexer | Call without target $@. | Quality.cs:29:21:29:27 | access to indexer | access to indexer |
|
||||
| Quality.cs:32:9:32:21 | access to indexer | Call without target $@. | Quality.cs:32:9:32:21 | access to indexer | access to indexer |
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
| Quality.cs:23:9:23:30 | delegate call | Call without target $@. | Quality.cs:23:9:23:30 | delegate call | delegate call |
|
||||
| Quality.cs:26:19:26:26 | access to indexer | Call without target $@. | Quality.cs:26:19:26:26 | access to indexer | access to indexer |
|
||||
| Quality.cs:29:21:29:27 | access to indexer | Call without target $@. | Quality.cs:29:21:29:27 | access to indexer | access to indexer |
|
||||
| Quality.cs:32:9:32:21 | access to indexer | Call without target $@. | Quality.cs:32:9:32:21 | access to indexer | access to indexer |
|
||||
| Quality.cs:38:16:38:26 | access to property MyProperty2 | Call without target $@. | Quality.cs:38:16:38:26 | access to property MyProperty2 | access to property MyProperty2 |
|
||||
| Quality.cs:50:20:50:26 | object creation of type T | Call without target $@. | Quality.cs:50:20:50:26 | object creation of type T | object creation of type T |
|
||||
|
||||
@@ -29,7 +29,7 @@ public class Test
|
||||
var slice = sp[..3]; // TODO: this is not an indexer call, but rather a `sp.Slice(0, 3)` call.
|
||||
|
||||
Span<byte> guidBytes = stackalloc byte[16];
|
||||
guidBytes[08] = 1;
|
||||
guidBytes[08] = 1; // TODO: this indexer call has no target, because the target is a `ref` returning getter.
|
||||
|
||||
new MyList([new(), new Test()]);
|
||||
}
|
||||
|
||||
@@ -4,67 +4,30 @@
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import internal.rangeanalysis.BoundSpecific
|
||||
private import java as J
|
||||
private import internal.rangeanalysis.BoundSpecific as BoundSpecific
|
||||
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 BoundImpl = SharedBound::Bound<J::Location, BoundSpecific::BoundDefs>;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/** Gets an expression that equals this bound plus `delta`. */
|
||||
abstract Expr getExpr(int delta);
|
||||
|
||||
/** Gets an expression that equals this bound. */
|
||||
Expr getExpr() { result = this.getExpr(0) }
|
||||
|
||||
/** Gets the location of this bound. */
|
||||
abstract Location getLocation();
|
||||
}
|
||||
class Bound = BoundImpl::Bound;
|
||||
|
||||
/**
|
||||
* 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" }
|
||||
|
||||
override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta }
|
||||
|
||||
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
|
||||
}
|
||||
class ZeroBound = BoundImpl::ZeroBound;
|
||||
|
||||
/**
|
||||
* 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() }
|
||||
}
|
||||
class SsaBound = BoundImpl::SsaBound;
|
||||
|
||||
/**
|
||||
* 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() }
|
||||
}
|
||||
class ExprBound = BoundImpl::ExprBound;
|
||||
@@ -7,21 +7,26 @@ module;
|
||||
private import java as J
|
||||
private import semmle.code.java.dataflow.SSA as Ssa
|
||||
private import semmle.code.java.dataflow.RangeUtils as RU
|
||||
private import codeql.rangeanalysis.Bound as SharedBound
|
||||
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
/** Gets a use of this variable. */
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
module BoundDefs implements SharedBound::BoundDefinitions<J::Location> {
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
/** Gets a use of this variable. */
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
class Expr = J::Expr;
|
||||
class SsaSourceVariable = Ssa::SourceVariable;
|
||||
|
||||
class Location = J::Location;
|
||||
class Type = J::Type;
|
||||
|
||||
class IntegralType = J::IntegralType;
|
||||
class Expr = J::Expr;
|
||||
|
||||
class ConstantIntegerExpr = RU::ConstantIntegerExpr;
|
||||
class IntegralType = J::IntegralType;
|
||||
|
||||
/** 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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
import semmle.python.controlflow.internal.AstNodeImpl
|
||||
import ControlFlow::Consistency
|
||||
@@ -9,7 +9,6 @@ private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
|
||||
private import semmle.python.dataflow.new.internal.DataFlowDispatch
|
||||
private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
|
||||
private module Input implements InputSig<Location, PythonDataFlow> {
|
||||
private import Private
|
||||
@@ -73,7 +72,7 @@ private module Input implements InputSig<Location, PythonDataFlow> {
|
||||
// resolve to multiple functions), but we only make _one_ ArgumentNode for each
|
||||
// argument in the CallNode, we end up violating this consistency check in those
|
||||
// cases. (see `getCallArg` in DataFlowDispatch.qll)
|
||||
exists(DataFlowCall other, Cfg::CallNode cfgCall | other != call |
|
||||
exists(DataFlowCall other, CallNode cfgCall | other != call |
|
||||
call.getNode() = cfgCall and
|
||||
other.getNode() = cfgCall and
|
||||
isArgumentNode(arg, call, _) and
|
||||
@@ -89,16 +88,16 @@ private module Input implements InputSig<Location, PythonDataFlow> {
|
||||
// allow it instead.
|
||||
(
|
||||
call.getScope() = attr.getScope() and
|
||||
any(CfgNode n | n.asCfgNode() = call.getNode().(Cfg::CallNode).getFunction())
|
||||
.getALocalSource() = attr
|
||||
any(CfgNode n | n.asCfgNode() = call.getNode().(CallNode).getFunction()).getALocalSource() =
|
||||
attr
|
||||
or
|
||||
not exists(call.getScope().(Function).getDefinition()) and
|
||||
call.getScope().getScope+() = attr.getScope()
|
||||
) and
|
||||
(
|
||||
other.getScope() = attr.getScope() and
|
||||
any(CfgNode n | n.asCfgNode() = other.getNode().(Cfg::CallNode).getFunction())
|
||||
.getALocalSource() = attr
|
||||
any(CfgNode n | n.asCfgNode() = other.getNode().(CallNode).getFunction()).getALocalSource() =
|
||||
attr
|
||||
or
|
||||
not exists(other.getScope().(Function).getDefinition()) and
|
||||
other.getScope().getScope+() = attr.getScope()
|
||||
|
||||
@@ -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
|
||||
---
|
||||
* The Python dataflow library is now built on the shared CFG and SSA libraries (`shared/controlflow` and `shared/ssa`), bringing Python in line with the other CodeQL languages. The legacy CFG in `semmle/python/Flow.qll` and the legacy ESSA SSA in `semmle/python/essa/*` remain available for downstream queries but are no longer used by the new dataflow library, type tracking, or API graphs. Most queries should be unaffected; a small number may produce slightly different results because of differences in CFG granularity (e.g. separate pre/post nodes per expression) and in how attribute and tuple-unpacking writes are modelled.
|
||||
@@ -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>
|
||||
@@ -7,7 +7,6 @@ library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/concepts: ${workspace}
|
||||
codeql/controlflow: ${workspace}
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/regex: ${workspace}
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
* directed and labeled; they specify how the components represented by nodes relate to each other.
|
||||
*/
|
||||
|
||||
// Importing python under the `py` namespace to avoid importing `Cfg::CallNode` from `Flow.qll` and thereby having a naming conflict with `API::CallNode`.
|
||||
// Importing python under the `py` namespace to avoid importing `CallNode` from `Flow.qll` and thereby having a naming conflict with `API::CallNode`.
|
||||
private import python as PY
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.internal.CachedStages
|
||||
|
||||
@@ -283,7 +282,7 @@ module API {
|
||||
index = this.getIndex() and
|
||||
(
|
||||
// subscripting
|
||||
exists(Cfg::SubscriptNode subscript |
|
||||
exists(PY::SubscriptNode subscript |
|
||||
subscript.getObject() = this.getAValueReachableFromSource().asCfgNode() and
|
||||
subscript.getIndex() = index.asSink().asCfgNode()
|
||||
|
|
||||
@@ -291,7 +290,7 @@ module API {
|
||||
subscript = result.asSource().asCfgNode()
|
||||
or
|
||||
// writing
|
||||
subscript.(Cfg::DefinitionNode).getValue() = result.asSink().asCfgNode()
|
||||
subscript.(PY::DefinitionNode).getValue() = result.asSink().asCfgNode()
|
||||
)
|
||||
or
|
||||
// dictionary literals
|
||||
@@ -685,7 +684,7 @@ module API {
|
||||
* Ignores relative imports, such as `from ..foo.bar import baz`.
|
||||
*/
|
||||
private predicate imports(DataFlow::CfgNode imp, string name) {
|
||||
exists(Cfg::ImportExprNode iexpr |
|
||||
exists(PY::ImportExprNode iexpr |
|
||||
imp.getNode() = iexpr and
|
||||
not iexpr.getNode().isRelative() and
|
||||
name = iexpr.getNode().getImportedModuleName()
|
||||
@@ -776,7 +775,7 @@ module API {
|
||||
// list literals, from `x` to `[x]`
|
||||
// TODO: once convenient, this should be done at a higher level than the AST,
|
||||
// at least at the CFG layer, to take splitting into account.
|
||||
// Also consider `Cfg::SequenceNode for generality.
|
||||
// Also consider `SequenceNode for generality.
|
||||
exists(PY::List list | list = pred.(DataFlow::ExprNode).getNode().getNode() |
|
||||
rhs.(DataFlow::ExprNode).getNode().getNode() = list.getAnElt() and
|
||||
lbl = Label::subscript()
|
||||
@@ -806,7 +805,7 @@ module API {
|
||||
subscript = trackUseNode(src).getSubscript(index)
|
||||
|
|
||||
// from `x` to a definition of `x[...]`
|
||||
rhs.asCfgNode() = subscript.asCfgNode().(Cfg::DefinitionNode).getValue() and
|
||||
rhs.asCfgNode() = subscript.asCfgNode().(PY::DefinitionNode).getValue() and
|
||||
lbl = Label::subscript()
|
||||
or
|
||||
// from `x` to `"key"` in `x["key"]`
|
||||
|
||||
@@ -16,6 +16,17 @@ abstract class AstNode extends AstNode_ {
|
||||
/** Gets the scope that this node occurs in */
|
||||
abstract Scope getScope();
|
||||
|
||||
/**
|
||||
* Gets a flow node corresponding directly to this node.
|
||||
* NOTE: For some statements and other purely syntactic elements,
|
||||
* there may not be a `ControlFlowNode`
|
||||
*/
|
||||
cached
|
||||
ControlFlowNode getAFlowNode() {
|
||||
Stages::AST::ref() and
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
/** Gets the location for this AST node */
|
||||
cached
|
||||
Location getLocation() { none() }
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
@@ -215,7 +214,7 @@ module Path {
|
||||
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
|
||||
}
|
||||
|
||||
private predicate safeAccessCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
|
||||
private predicate safeAccessCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
g.(SafeAccessCheck::Range).checks(node, branch)
|
||||
}
|
||||
|
||||
@@ -224,7 +223,7 @@ module Path {
|
||||
/** A data-flow node that checks that a path is safe to access in some way, for example by having a controlled prefix. */
|
||||
abstract class Range extends DataFlow::GuardNode {
|
||||
/** Holds if this guard validates `node` upon evaluating to `branch`. */
|
||||
abstract predicate checks(Cfg::ControlFlowNode node, boolean branch);
|
||||
abstract predicate checks(ControlFlowNode node, boolean branch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -1,43 +1,36 @@
|
||||
/** Provides commonly used BarrierGuards. */
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
private predicate constCompare(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
|
||||
exists(Cfg::CompareNode cn | cn = g |
|
||||
exists(ImmutableLiteral const, Cmpop op, Cfg::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)
|
||||
private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
exists(CompareNode cn | cn = g |
|
||||
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, Cfg::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(Cfg::IterableNode const_iterable, Cmpop op |
|
||||
exists(IterableNode const_iterable, Cmpop op |
|
||||
op = any(In in_) and branch = true
|
||||
or
|
||||
op = any(NotIn ni) and branch = false
|
||||
|
|
||||
forall(Cfg::ControlFlowNode elem | elem = const_iterable.getAnElement() |
|
||||
forall(ControlFlowNode elem | elem = const_iterable.getAnElement() |
|
||||
elem.getNode() instanceof ImmutableLiteral
|
||||
) and
|
||||
cn.operands(node, op, const_iterable)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
|
||||
private import semmle.python.Frameworks
|
||||
@@ -106,7 +105,7 @@ private module SensitiveDataModeling {
|
||||
or
|
||||
// to cover functions that we don't have the definition for, and where the
|
||||
// reference to the function has not already been marked as being sensitive
|
||||
this.getFunction().asCfgNode().(Cfg::NameNode).getId() = sensitiveString(classification)
|
||||
this.getFunction().asCfgNode().(NameNode).getId() = sensitiveString(classification)
|
||||
}
|
||||
|
||||
override SensitiveDataClassification getClassification() { result = classification }
|
||||
@@ -252,12 +251,12 @@ private module SensitiveDataModeling {
|
||||
SensitiveDataClassification classification;
|
||||
|
||||
SensitiveVariableAssignment() {
|
||||
exists(Cfg::DefinitionNode def |
|
||||
def.(Cfg::NameNode).getId() = sensitiveString(classification) and
|
||||
exists(DefinitionNode def |
|
||||
def.(NameNode).getId() = sensitiveString(classification) and
|
||||
(
|
||||
this.asCfgNode() = def.getValue()
|
||||
or
|
||||
this.asCfgNode() = def.getValue().(Cfg::ForNode).getSequence()
|
||||
this.asCfgNode() = def.getValue().(ForNode).getSequence()
|
||||
) and
|
||||
not this.asExpr() instanceof FunctionExpr and
|
||||
not this.asExpr() instanceof ClassExpr
|
||||
@@ -294,7 +293,7 @@ private module SensitiveDataModeling {
|
||||
SensitiveDataClassification classification;
|
||||
|
||||
SensitiveSubscript() {
|
||||
this.asCfgNode().(Cfg::SubscriptNode).getIndex() =
|
||||
this.asCfgNode().(SubscriptNode).getIndex() =
|
||||
sensitiveLookupStringConst(classification).asCfgNode()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ overlay[local]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
import DataFlowUtil
|
||||
import DataFlowPublic
|
||||
private import DataFlowPrivate
|
||||
@@ -84,9 +83,9 @@ abstract class AttrWrite extends AttrRef {
|
||||
* ```python
|
||||
* object.attr = value
|
||||
* ```
|
||||
* Also gives access to the `value` being written, by extending `Cfg::DefinitionNode`.
|
||||
* Also gives access to the `value` being written, by extending `DefinitionNode`.
|
||||
*/
|
||||
private class AttributeAssignmentNode extends Cfg::DefinitionNode, Cfg::AttrNode { }
|
||||
private class AttributeAssignmentNode extends DefinitionNode, AttrNode { }
|
||||
|
||||
/** A simple attribute assignment: `object.attr = value`. */
|
||||
private class AttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
|
||||
@@ -132,13 +131,13 @@ private class GlobalAttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
|
||||
override string getAttributeName() { result = node.getName() }
|
||||
}
|
||||
|
||||
/** Represents `Cfg::CallNode`s that may refer to calls to built-in functions or classes. */
|
||||
private class BuiltInCallNode extends Cfg::CallNode {
|
||||
/** Represents `CallNode`s that may refer to calls to built-in functions or classes. */
|
||||
private class BuiltInCallNode extends CallNode {
|
||||
string name;
|
||||
|
||||
BuiltInCallNode() {
|
||||
// TODO disallow instances where the name of the built-in may refer to an in-scope variable of that name.
|
||||
exists(Cfg::NameNode id |
|
||||
exists(NameNode id |
|
||||
name = Builtins::getBuiltinName() and
|
||||
this.getFunction() = id and
|
||||
id.getId() = name and
|
||||
@@ -146,7 +145,7 @@ private class BuiltInCallNode extends Cfg::CallNode {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the name of the built-in function that is called at this `Cfg::CallNode` */
|
||||
/** Gets the name of the built-in function that is called at this `CallNode` */
|
||||
string getBuiltinName() { result = name }
|
||||
}
|
||||
|
||||
@@ -158,20 +157,20 @@ private class BuiltinAttrCallNode extends BuiltInCallNode {
|
||||
BuiltinAttrCallNode() { name in ["setattr", "getattr", "hasattr", "delattr"] }
|
||||
|
||||
/** Gets the control flow node for object on which the attribute is accessed. */
|
||||
Cfg::ControlFlowNode getObject() { result in [this.getArg(0), this.getArgByName("object")] }
|
||||
ControlFlowNode getObject() { result in [this.getArg(0), this.getArgByName("object")] }
|
||||
|
||||
/**
|
||||
* Gets the control flow node for the value that is being written to the attribute.
|
||||
* Only relevant for `setattr` calls.
|
||||
*/
|
||||
Cfg::ControlFlowNode getValue() {
|
||||
ControlFlowNode getValue() {
|
||||
// only valid for `setattr`
|
||||
name = "setattr" and
|
||||
result in [this.getArg(2), this.getArgByName("value")]
|
||||
}
|
||||
|
||||
/** Gets the control flow node that defines the name of the attribute being accessed. */
|
||||
Cfg::ControlFlowNode getName() { result in [this.getArg(1), this.getArgByName("name")] }
|
||||
ControlFlowNode getName() { result in [this.getArg(1), this.getArgByName("name")] }
|
||||
}
|
||||
|
||||
/** Represents calls to the built-in `setattr`. */
|
||||
@@ -206,10 +205,10 @@ private class SetAttrCallAsAttrWrite extends AttrWrite, CfgNode {
|
||||
* attr = value
|
||||
* ...
|
||||
* ```
|
||||
* Instances of this class correspond to the `Cfg::NameNode` for `attr`, and also gives access to `value` by
|
||||
* virtue of being a `Cfg::DefinitionNode`.
|
||||
* Instances of this class correspond to the `NameNode` for `attr`, and also gives access to `value` by
|
||||
* virtue of being a `DefinitionNode`.
|
||||
*/
|
||||
private class ClassAttributeAssignmentNode extends Cfg::DefinitionNode, Cfg::NameNode {
|
||||
private class ClassAttributeAssignmentNode extends DefinitionNode, NameNode {
|
||||
ClassAttributeAssignmentNode() { this.getScope() = any(ClassExpr c).getInnerScope() }
|
||||
}
|
||||
|
||||
@@ -229,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() }
|
||||
|
||||
@@ -249,7 +248,7 @@ abstract class AttrRead extends AttrRef, Node, LocalSourceNode {
|
||||
|
||||
/** A simple attribute read, e.g. `object.attr` */
|
||||
private class AttributeReadAsAttrRead extends AttrRead, CfgNode {
|
||||
override Cfg::AttrNode node;
|
||||
override AttrNode node;
|
||||
|
||||
AttributeReadAsAttrRead() { node.isLoad() }
|
||||
|
||||
@@ -286,7 +285,7 @@ private class GetAttrCallAsAttrRead extends AttrRead, CfgNode {
|
||||
* is treated as if it is a read of the attribute `module.attr`, even if `module` is not imported directly.
|
||||
*/
|
||||
private class ModuleAttributeImportAsAttrRead extends AttrRead, CfgNode {
|
||||
override Cfg::ImportMemberNode node;
|
||||
override ImportMemberNode node;
|
||||
|
||||
override Node getObject() { result.asCfgNode() = node.getModule(_) }
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ overlay[local]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
|
||||
@@ -68,7 +67,7 @@ module Builtins {
|
||||
DataFlow::CfgNode likelyBuiltin(string name) {
|
||||
exists(Module m |
|
||||
result.getNode() =
|
||||
any(Cfg::NameNode n |
|
||||
any(NameNode n |
|
||||
possible_builtin_accessed_in_module(n, name, m) and
|
||||
not possible_builtin_defined_in_module(name, m)
|
||||
)
|
||||
@@ -88,7 +87,7 @@ module Builtins {
|
||||
* Holds if `n` is an access of a global variable called `name` (which is also the name of a
|
||||
* built-in) inside the module `m`.
|
||||
*/
|
||||
private predicate possible_builtin_accessed_in_module(Cfg::NameNode n, string name, Module m) {
|
||||
private predicate possible_builtin_accessed_in_module(NameNode n, string name, Module m) {
|
||||
n.isGlobal() and
|
||||
n.isLoad() and
|
||||
name = n.getId() and
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* what callable this call might end up targeting.
|
||||
*
|
||||
* Specifically this means that we cannot use type-backtrackers from the function of a
|
||||
* `Cfg::CallNode`, since there is no `Cfg::CallNode` to backtrack from for `func` in the example
|
||||
* `CallNode`, since there is no `CallNode` to backtrack from for `func` in the example
|
||||
* above.
|
||||
*
|
||||
* Note: This hasn't been 100% realized yet, so we don't currently expose a predicate to
|
||||
@@ -35,7 +35,6 @@ overlay[local?]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import DataFlowPublic
|
||||
private import DataFlowPrivate
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
@@ -163,7 +162,7 @@ newtype TArgumentPosition =
|
||||
*/
|
||||
TLambdaSelfArgumentPosition() or
|
||||
TPositionalArgumentPosition(int index) {
|
||||
exists(any(Cfg::CallNode c).getArg(index))
|
||||
exists(any(CallNode c).getArg(index))
|
||||
or
|
||||
// since synthetic calls within a summarized callable could use a unique argument
|
||||
// position, we need to ensure we make these available (these are specified as
|
||||
@@ -175,7 +174,7 @@ newtype TArgumentPosition =
|
||||
index = 0
|
||||
} or
|
||||
TKeywordArgumentPosition(string name) {
|
||||
exists(any(Cfg::CallNode c).getArgByName(name))
|
||||
exists(any(CallNode c).getArgByName(name))
|
||||
or
|
||||
// see comment for TPositionalArgumentPosition
|
||||
FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name)
|
||||
@@ -257,13 +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. Even if a class redefines
|
||||
// `staticmethod`, the binding hasn't happened yet at the decorator
|
||||
// position, so Python's runtime semantics is "use the builtin".
|
||||
// Matches legacy ESSA semantics which used `isGlobal()` (i.e. "no
|
||||
// SSA def reaches this load") at the decorator's `NameNode`.
|
||||
func.getADecorator().(Name).getId() = "staticmethod"
|
||||
exists(NameNode id | id.getId() = "staticmethod" and id.isGlobal() |
|
||||
func.getADecorator() = id.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,7 +268,9 @@ predicate isStaticmethod(Function func) {
|
||||
*/
|
||||
overlay[local]
|
||||
predicate isClassmethod(Function func) {
|
||||
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
|
||||
@@ -287,19 +284,21 @@ predicate isClassmethod(Function func) {
|
||||
|
||||
/** Holds if the function `func` has a `property` decorator. */
|
||||
overlay[local]
|
||||
predicate hasPropertyDecorator(Function func) { func.getADecorator().(Name).getId() = "property" }
|
||||
predicate hasPropertyDecorator(Function func) {
|
||||
exists(NameNode id | id.getId() = "property" and id.isGlobal() |
|
||||
func.getADecorator() = id.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function `func` has a `contextlib.contextmanager`.
|
||||
*/
|
||||
overlay[local]
|
||||
predicate hasContextmanagerDecorator(Function func) {
|
||||
exists(Cfg::ControlFlowNode contextmanager |
|
||||
contextmanager.(Cfg::NameNode).getId() = "contextmanager" and
|
||||
contextmanager.(Cfg::NameNode).isGlobal()
|
||||
exists(ControlFlowNode contextmanager |
|
||||
contextmanager.(NameNode).getId() = "contextmanager" and contextmanager.(NameNode).isGlobal()
|
||||
or
|
||||
contextmanager.(Cfg::AttrNode).getObject("contextmanager").(Cfg::NameNode).getId() =
|
||||
"contextlib"
|
||||
contextmanager.(AttrNode).getObject("contextmanager").(NameNode).getId() = "contextlib"
|
||||
|
|
||||
func.getADecorator() = contextmanager.getNode()
|
||||
)
|
||||
@@ -315,10 +314,10 @@ predicate hasContextmanagerDecorator(Function func) {
|
||||
*/
|
||||
overlay[local]
|
||||
private predicate hasOverloadDecorator(Function func) {
|
||||
exists(Cfg::ControlFlowNode overload |
|
||||
overload.(Cfg::NameNode).getId() = "overload" and overload.(Cfg::NameNode).isGlobal()
|
||||
exists(ControlFlowNode overload |
|
||||
overload.(NameNode).getId() = "overload" and overload.(NameNode).isGlobal()
|
||||
or
|
||||
overload.(Cfg::AttrNode).getObject("overload").(Cfg::NameNode).isGlobal()
|
||||
overload.(AttrNode).getObject("overload").(NameNode).isGlobal()
|
||||
|
|
||||
func.getADecorator() = overload.getNode()
|
||||
)
|
||||
@@ -537,7 +536,7 @@ class LibraryCallableValue extends DataFlowCallable, TLibraryCallable {
|
||||
// =============================================================================
|
||||
/** Gets a call to `type`. */
|
||||
private CallCfgNode getTypeCall() {
|
||||
exists(Cfg::NameNode id | id.getId() = "type" and id.isGlobal() |
|
||||
exists(NameNode id | id.getId() = "type" and id.isGlobal() |
|
||||
result.getFunction().asCfgNode() = id
|
||||
)
|
||||
}
|
||||
@@ -549,7 +548,7 @@ private CallCfgNode getSuperCall() {
|
||||
// link below), but otherwise only 2 edgecases. Overall it seems ok to ignore this complexity.
|
||||
//
|
||||
// https://github.com/python/cpython/blob/18b1782192f85bd26db89f5bc850f8bee4247c1a/Lib/unittest/mock.py#L48-L50
|
||||
exists(Cfg::NameNode id | id.getId() = "super" and id.isGlobal() |
|
||||
exists(NameNode id | id.getId() = "super" and id.isGlobal() |
|
||||
result.getFunction().asCfgNode() = id
|
||||
)
|
||||
}
|
||||
@@ -1035,7 +1034,7 @@ private module MethodCalls {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate directCall(
|
||||
Cfg::CallNode call, Function target, string functionName, Class cls, AttrRead attr, Node self
|
||||
CallNode call, Function target, string functionName, Class cls, AttrRead attr, Node self
|
||||
) {
|
||||
target = findFunctionAccordingToMroKnownStartingClass(cls, functionName) and
|
||||
directCall_join(call, functionName, cls, attr, self)
|
||||
@@ -1044,7 +1043,7 @@ private module MethodCalls {
|
||||
/** Extracted to give good join order */
|
||||
pragma[nomagic]
|
||||
private predicate directCall_join(
|
||||
Cfg::CallNode call, string functionName, Class cls, AttrRead attr, Node self
|
||||
CallNode call, string functionName, Class cls, AttrRead attr, Node self
|
||||
) {
|
||||
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
||||
attr.accesses(self, functionName) and
|
||||
@@ -1061,7 +1060,7 @@ private module MethodCalls {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate callWithinMethodImplicitSelfOrCls(
|
||||
Cfg::CallNode call, Function target, string functionName, Class classWithMethod, AttrRead attr,
|
||||
CallNode call, Function target, string functionName, Class classWithMethod, AttrRead attr,
|
||||
Node self
|
||||
) {
|
||||
target = findFunctionAccordingToMro(getADirectSubclass*(classWithMethod), functionName) and
|
||||
@@ -1071,7 +1070,7 @@ private module MethodCalls {
|
||||
/** Extracted to give good join order */
|
||||
pragma[nomagic]
|
||||
private predicate callWithinMethodImplicitSelfOrCls_join(
|
||||
Cfg::CallNode call, string functionName, Class classWithMethod, AttrRead attr, Node self
|
||||
CallNode call, string functionName, Class classWithMethod, AttrRead attr, Node self
|
||||
) {
|
||||
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
||||
attr.accesses(self, functionName) and
|
||||
@@ -1083,7 +1082,7 @@ private module MethodCalls {
|
||||
* resolve the call to a known target (since the only super class might be the
|
||||
* builtin `object`, so we never have the implementation of `__new__` in the DB).
|
||||
*/
|
||||
predicate fromSuperNewCall(Cfg::CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
|
||||
predicate fromSuperNewCall(CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
|
||||
fromSuper_join(call, "__new__", classUsedInSuper, attr, self) and
|
||||
self in [classTracker(_), clsArgumentTracker(_)]
|
||||
}
|
||||
@@ -1105,7 +1104,7 @@ private module MethodCalls {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate fromSuper(
|
||||
Cfg::CallNode call, Function target, string functionName, Class classUsedInSuper, AttrRead attr,
|
||||
CallNode call, Function target, string functionName, Class classUsedInSuper, AttrRead attr,
|
||||
Node self
|
||||
) {
|
||||
target = findFunctionAccordingToMro(getNextClassInMro(classUsedInSuper), functionName) and
|
||||
@@ -1115,7 +1114,7 @@ private module MethodCalls {
|
||||
/** Extracted to give good join order */
|
||||
pragma[nomagic]
|
||||
private predicate fromSuper_join(
|
||||
Cfg::CallNode call, string functionName, Class classUsedInSuper, AttrRead attr, Node self
|
||||
CallNode call, string functionName, Class classUsedInSuper, AttrRead attr, Node self
|
||||
) {
|
||||
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
||||
(
|
||||
@@ -1134,7 +1133,7 @@ private module MethodCalls {
|
||||
)
|
||||
}
|
||||
|
||||
predicate resolveMethodCall(Cfg::CallNode call, Function target, CallType type, Node self) {
|
||||
predicate resolveMethodCall(CallNode call, Function target, CallType type, Node self) {
|
||||
(
|
||||
directCall(call, target, _, _, _, self)
|
||||
or
|
||||
@@ -1181,7 +1180,7 @@ import MethodCalls
|
||||
* NOTE: We have this predicate mostly to be able to compare with old point-to
|
||||
* call-graph resolution. So it could be removed in the future.
|
||||
*/
|
||||
predicate resolveClassCall(Cfg::CallNode call, Class cls) {
|
||||
predicate resolveClassCall(CallNode call, Class cls) {
|
||||
call.getFunction() = classTracker(cls).asCfgNode()
|
||||
or
|
||||
// `cls()` inside a classmethod (which also contains `type(self)()` inside a method)
|
||||
@@ -1211,7 +1210,7 @@ Function invokedFunctionFromClassConstruction(Class cls, string funcName) {
|
||||
*
|
||||
* See https://docs.python.org/3/reference/datamodel.html#object.__call__
|
||||
*/
|
||||
predicate resolveClassInstanceCall(Cfg::CallNode call, Function target, Node self) {
|
||||
predicate resolveClassInstanceCall(CallNode call, Function target, Node self) {
|
||||
exists(Class cls |
|
||||
call.getFunction() = classInstanceTracker(cls).asCfgNode() and
|
||||
target = findFunctionAccordingToMroKnownStartingClass(cls, "__call__")
|
||||
@@ -1230,7 +1229,7 @@ predicate resolveClassInstanceCall(Cfg::CallNode call, Function target, Node sel
|
||||
* Holds if `call` is a call to the `target`, with call-type `type`.
|
||||
*/
|
||||
cached
|
||||
predicate resolveCall(Cfg::CallNode call, Function target, CallType type) {
|
||||
predicate resolveCall(CallNode call, Function target, CallType type) {
|
||||
Stages::DataFlow::ref() and
|
||||
(
|
||||
type instanceof CallTypePlainFunction and
|
||||
@@ -1255,11 +1254,11 @@ predicate resolveCall(Cfg::CallNode call, Function target, CallType type) {
|
||||
// =============================================================================
|
||||
/**
|
||||
* Holds if the argument of `call` at position `apos` is `arg`. This is just a helper
|
||||
* predicate that maps ArgumentPositions to the arguments of the underlying `Cfg::CallNode`.
|
||||
* predicate that maps ArgumentPositions to the arguments of the underlying `CallNode`.
|
||||
*/
|
||||
overlay[local]
|
||||
cached
|
||||
predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) {
|
||||
predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
|
||||
exists(int index |
|
||||
apos.isPositional(index) and
|
||||
arg.asCfgNode() = call.getArg(index)
|
||||
@@ -1274,7 +1273,7 @@ predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) {
|
||||
exists(int index |
|
||||
apos.isStarArgs(index) and
|
||||
arg.asCfgNode() = call.getStarArg() and
|
||||
// since `Cfg::CallNode.getArg` doesn't include `*args`, we need to drop to the AST level
|
||||
// since `CallNode.getArg` doesn't include `*args`, we need to drop to the AST level
|
||||
// to get the index. Notice that we only use the AST for getting the index, so we
|
||||
// don't need to check for dominance in regards to splitting.
|
||||
call.getStarArg().getNode() = call.getNode().getPositionalArg(index).(Starred).getValue()
|
||||
@@ -1348,9 +1347,7 @@ predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) {
|
||||
* translated into `l.clear()`, and we can still have use-use flow.
|
||||
*/
|
||||
cached
|
||||
predicate getCallArg(
|
||||
Cfg::CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos
|
||||
) {
|
||||
predicate getCallArg(CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos) {
|
||||
Stages::DataFlow::ref() and
|
||||
resolveCall(call, target, type) and
|
||||
(
|
||||
@@ -1443,13 +1440,10 @@ private predicate sameEnclosingCallable(Node node1, Node node2) {
|
||||
// DataFlowCall
|
||||
// =============================================================================
|
||||
newtype TDataFlowCall =
|
||||
TNormalCall(Cfg::CallNode call, Function target, CallType type) {
|
||||
resolveCall(call, target, type) and
|
||||
Cfg::isCanonicalAstNodeRepresentative(call)
|
||||
} or
|
||||
TNormalCall(CallNode call, Function target, CallType type) { resolveCall(call, target, type) } or
|
||||
/** A call to the generated function inside a comprehension */
|
||||
TComprehensionCall(Comp c) or
|
||||
TPotentialLibraryCall(Cfg::CallNode call) { Cfg::isCanonicalAstNodeRepresentative(call) } or
|
||||
TPotentialLibraryCall(CallNode call) or
|
||||
/** A synthesized call inside a summarized callable */
|
||||
TSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
@@ -1469,7 +1463,7 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
abstract ArgumentNode getArgument(ArgumentPosition apos);
|
||||
|
||||
/** Get the control flow node representing this call, if any. */
|
||||
abstract Cfg::ControlFlowNode getNode();
|
||||
abstract ControlFlowNode getNode();
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
|
||||
@@ -1500,28 +1494,28 @@ abstract class ExtractedDataFlowCall extends DataFlowCall {
|
||||
}
|
||||
|
||||
/**
|
||||
* A resolved call in source code with an underlying `Cfg::CallNode`.
|
||||
* A resolved call in source code with an underlying `CallNode`.
|
||||
*
|
||||
* This is considered normal, compared with special calls such as `obj[0]` calling the
|
||||
* `__getitem__` method on the object. However, this also includes calls that go to the
|
||||
* `__call__` special method.
|
||||
*/
|
||||
class NormalCall extends ExtractedDataFlowCall, TNormalCall {
|
||||
Cfg::CallNode call;
|
||||
CallNode call;
|
||||
Function target;
|
||||
CallType type;
|
||||
|
||||
NormalCall() { this = TNormalCall(call, target, type) }
|
||||
|
||||
override string toString() {
|
||||
// note: if we used toString directly on the Cfg::CallNode we would get
|
||||
// `Cfg::ControlFlowNode for func()`
|
||||
// but the `Cfg::ControlFlowNode` part is just clutter, so we go directly to the AST node
|
||||
// note: if we used toString directly on the CallNode we would get
|
||||
// `ControlFlowNode for func()`
|
||||
// but the `ControlFlowNode` part is just clutter, so we go directly to the AST node
|
||||
// instead.
|
||||
result = call.getNode().toString()
|
||||
}
|
||||
|
||||
override Cfg::ControlFlowNode getNode() { result = call }
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override Scope getScope() { result = call.getScope() }
|
||||
|
||||
@@ -1549,7 +1543,7 @@ class ComprehensionCall extends ExtractedDataFlowCall, TComprehensionCall {
|
||||
|
||||
override string toString() { result = "comprehension call" }
|
||||
|
||||
override Cfg::ControlFlowNode getNode() { result.getNode() = c }
|
||||
override ControlFlowNode getNode() { result.getNode() = c }
|
||||
|
||||
override Scope getScope() { result = c.getScope() }
|
||||
|
||||
@@ -1572,14 +1566,14 @@ class ComprehensionCall extends ExtractedDataFlowCall, TComprehensionCall {
|
||||
* in this class.
|
||||
*/
|
||||
class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall {
|
||||
Cfg::CallNode call;
|
||||
CallNode call;
|
||||
|
||||
PotentialLibraryCall() { this = TPotentialLibraryCall(call) }
|
||||
|
||||
override string toString() {
|
||||
// note: if we used toString directly on the Cfg::CallNode we would get
|
||||
// `Cfg::ControlFlowNode for func()`
|
||||
// but the `Cfg::ControlFlowNode` part is just clutter, so we go directly to the AST node
|
||||
// note: if we used toString directly on the CallNode we would get
|
||||
// `ControlFlowNode for func()`
|
||||
// but the `ControlFlowNode` part is just clutter, so we go directly to the AST node
|
||||
// instead.
|
||||
result = call.getNode().toString()
|
||||
}
|
||||
@@ -1596,10 +1590,10 @@ class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall
|
||||
// potential self argument, from `foo.bar()` -- note that this could also just be a
|
||||
// module reference, but we really don't have a good way of knowing :|
|
||||
apos.isSelf() and
|
||||
result.asCfgNode() = call.getFunction().(Cfg::AttrNode).getObject()
|
||||
result.asCfgNode() = call.getFunction().(AttrNode).getObject()
|
||||
}
|
||||
|
||||
override Cfg::ControlFlowNode getNode() { result = call }
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override Scope getScope() { result = call.getScope() }
|
||||
}
|
||||
@@ -1631,7 +1625,7 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
|
||||
|
||||
override ArgumentNode getArgument(ArgumentPosition apos) { none() }
|
||||
|
||||
override Cfg::ControlFlowNode getNode() { none() }
|
||||
override ControlFlowNode getNode() { none() }
|
||||
|
||||
override string toString() { result = "[summary] call to " + receiver + " in " + c }
|
||||
|
||||
@@ -1773,12 +1767,12 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl
|
||||
* This is used for tracking flow through captured variables.
|
||||
*/
|
||||
class SynthCapturedVariablesArgumentNode extends Node, TSynthCapturedVariablesArgumentNode {
|
||||
Cfg::ControlFlowNode callable;
|
||||
ControlFlowNode callable;
|
||||
|
||||
SynthCapturedVariablesArgumentNode() { this = TSynthCapturedVariablesArgumentNode(callable) }
|
||||
|
||||
/** Gets the `Cfg::CallNode` corresponding to this captured variables argument node. */
|
||||
Cfg::CallNode getCallNode() { result.getFunction() = callable }
|
||||
/** Gets the `CallNode` corresponding to this captured variables argument node. */
|
||||
CallNode getCallNode() { result.getFunction() = callable }
|
||||
|
||||
/** Gets the `CfgNode` that corresponds to this synthetic node. */
|
||||
CfgNode getUnderlyingNode() { result.asCfgNode() = callable }
|
||||
@@ -1796,7 +1790,7 @@ class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
|
||||
{
|
||||
overlay[global]
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
exists(Cfg::CallNode callNode | callNode = this.getCallNode() |
|
||||
exists(CallNode callNode | callNode = this.getCallNode() |
|
||||
callNode = call.getNode() and
|
||||
exists(Function target | resolveCall(callNode, target, _) |
|
||||
target = any(VariableCapture::CapturedVariable v).getACapturingScope()
|
||||
@@ -1810,7 +1804,7 @@ class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
|
||||
class SynthCapturedVariablesArgumentPostUpdateNode extends PostUpdateNodeImpl,
|
||||
TSynthCapturedVariablesArgumentPostUpdateNode
|
||||
{
|
||||
Cfg::ControlFlowNode callable;
|
||||
ControlFlowNode callable;
|
||||
|
||||
SynthCapturedVariablesArgumentPostUpdateNode() {
|
||||
this = TSynthCapturedVariablesArgumentPostUpdateNode(callable)
|
||||
@@ -1917,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() }
|
||||
@@ -1936,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() }
|
||||
|
||||
@@ -2,9 +2,8 @@ overlay[local?]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import DataFlowPublic
|
||||
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||
private import semmle.python.essa.SsaCompute
|
||||
private import semmle.python.dataflow.new.internal.ImportResolution
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
@@ -44,23 +43,13 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
|
||||
// Nodes
|
||||
//--------
|
||||
overlay[local]
|
||||
predicate isExpressionNode(Cfg::ControlFlowNode node) {
|
||||
node.getNode() instanceof Expr
|
||||
or
|
||||
// `Cfg::ForNode` wraps a `For` statement's iter position, but
|
||||
// overrides `.getNode()` to return the `Py::For` statement (for
|
||||
// legacy parity). The underlying AST is still an `Expr` (the iter
|
||||
// expression); we want a dataflow node here so that for-loop
|
||||
// content reads (`for y in l`) have a source expression node to
|
||||
// read content from.
|
||||
node instanceof Cfg::ForNode
|
||||
}
|
||||
predicate isExpressionNode(ControlFlowNode node) { node.getNode() instanceof Expr }
|
||||
|
||||
// =============================================================================
|
||||
// SyntheticPreUpdateNode
|
||||
// =============================================================================
|
||||
class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
|
||||
Cfg::CallNode node;
|
||||
CallNode node;
|
||||
|
||||
SyntheticPreUpdateNode() { this = TSyntheticPreUpdateNode(node) }
|
||||
|
||||
@@ -162,7 +151,7 @@ predicate synthStarArgsElementParameterNodeStoreStep(
|
||||
* been passed in a `**kwargs` argument.
|
||||
*/
|
||||
class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
|
||||
Cfg::CallNode node;
|
||||
CallNode node;
|
||||
|
||||
SynthDictSplatArgumentNode() { this = TSynthDictSplatArgumentNode(node) }
|
||||
|
||||
@@ -176,7 +165,7 @@ class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
|
||||
private predicate synthDictSplatArgumentNodeStoreStep(
|
||||
ArgumentNode nodeFrom, DictionaryElementContent c, SynthDictSplatArgumentNode nodeTo
|
||||
) {
|
||||
exists(string name, Cfg::CallNode call, ArgumentPosition keywordPos |
|
||||
exists(string name, CallNode call, ArgumentPosition keywordPos |
|
||||
nodeTo = TSynthDictSplatArgumentNode(call) and
|
||||
getCallArg(call, _, _, nodeFrom, keywordPos) and
|
||||
keywordPos.isKeyword(name) and
|
||||
@@ -196,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
|
||||
@@ -300,7 +289,7 @@ abstract class PostUpdateNodeImpl extends Node {
|
||||
* Synthetic post-update nodes for synthetic nodes need to be listed one by one.
|
||||
*/
|
||||
class SyntheticPostUpdateNode extends PostUpdateNodeImpl, TSyntheticPostUpdateNode {
|
||||
Cfg::ControlFlowNode node;
|
||||
ControlFlowNode node;
|
||||
|
||||
SyntheticPostUpdateNode() { this = TSyntheticPostUpdateNode(node) }
|
||||
|
||||
@@ -344,42 +333,16 @@ module LocalFlow {
|
||||
// `x = f(42)`
|
||||
// nodeFrom is `f(42)`
|
||||
// nodeTo is `x`
|
||||
//
|
||||
// We use the CFG-level `DefinitionNode.getValue()` directly rather
|
||||
// than going through SSA, because the new SSA library prunes write
|
||||
// definitions that have no subsequent read in the same scope (e.g.
|
||||
// a module-level `def f():` whose `f` is only read inside other
|
||||
// functions). The CFG-level link is unconditional.
|
||||
//
|
||||
// The Name-target restriction mirrors legacy ESSA's
|
||||
// `SsaDefinitions::assignment_definition`, which required
|
||||
// `defn.(NameNode).defines(v)`. Subscript and attribute writes
|
||||
// (`x[i] = 42`, `obj.attr = 42`) are intentionally excluded — their
|
||||
// value flow is handled by the content-flow / `AttrWrite` machinery,
|
||||
// not by a local-flow step *into* the Subscript/Attribute expression.
|
||||
// Excluding them is essential for keeping augmented-assignment
|
||||
// targets (`x[i] += 42`) classifiable as `LocalSourceNode` on the
|
||||
// read side: the single canonical CFG node is both a load and a
|
||||
// store, and any incoming local-flow step would disqualify it from
|
||||
// being a local source.
|
||||
exists(Cfg::DefinitionNode def |
|
||||
exists(AssignmentDefinition def |
|
||||
nodeFrom.(CfgNode).getNode() = def.getValue() and
|
||||
nodeTo.(CfgNode).getNode() = def and
|
||||
def instanceof Cfg::NameNode and
|
||||
// Parameter defaults are evaluated in the enclosing scope, while the
|
||||
// parameter itself lives in the function's scope. The cross-scope
|
||||
// edge is provided by `runtimeJumpStep` instead.
|
||||
not exists(Py::Parameter param | def.getNode() = param.asName())
|
||||
nodeTo.(CfgNode).getNode() = def.getDefiningNode()
|
||||
)
|
||||
or
|
||||
// With definition
|
||||
// `with f(42) as x:`
|
||||
// nodeFrom is `f(42)`
|
||||
// nodeTo is `x`
|
||||
exists(
|
||||
With with, Cfg::ControlFlowNode contextManager, SsaImpl::WithDefinition withDef,
|
||||
Cfg::ControlFlowNode var
|
||||
|
|
||||
exists(With with, ControlFlowNode contextManager, WithDefinition withDef, ControlFlowNode var |
|
||||
var = withDef.getDefiningNode()
|
||||
|
|
||||
nodeFrom.(CfgNode).getNode() = contextManager and
|
||||
@@ -398,13 +361,13 @@ module LocalFlow {
|
||||
|
||||
predicate expressionFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// If expressions
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::IfExprNode).getAnOperand()
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
|
||||
or
|
||||
// Assignment expressions
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::AssignmentExprNode).getValue()
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
|
||||
or
|
||||
// boolean inline expressions such as `x or y` or `x and y`
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::BoolExprNode).getAnOperand()
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
|
||||
or
|
||||
// Flow inside an unpacking assignment
|
||||
iterableUnpackingFlowStep(nodeFrom, nodeTo)
|
||||
@@ -413,28 +376,12 @@ module LocalFlow {
|
||||
matchFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate useToNextUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) {
|
||||
// The SSA-level adjacent-use predicate works on specific CFG variants
|
||||
// (e.g. boolean-outcome `[true]`/`[false]` or emptiness `[empty]`/`[non-empty]`
|
||||
// splits of the same AST node), but dataflow values are insensitive to
|
||||
// those splits — there is at most one `CfgNode` per AST. Project both
|
||||
// ends through `Cfg::isCanonicalAstNodeRepresentative` so all variants
|
||||
// contribute their use-use edges to the canonical pair.
|
||||
exists(Cfg::NameNode fromVariant, Cfg::NameNode toVariant |
|
||||
SsaImpl::AdjacentUses::adjacentUseUse(fromVariant, toVariant) and
|
||||
fromVariant.getNode() = nodeFrom.getNode() and
|
||||
toVariant.getNode() = nodeTo.getNode() and
|
||||
Cfg::isCanonicalAstNodeRepresentative(nodeFrom) and
|
||||
Cfg::isCanonicalAstNodeRepresentative(nodeTo)
|
||||
)
|
||||
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
|
||||
AdjacentUses::adjacentUseUse(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate defToFirstUse(SsaImpl::EssaVariable var, Cfg::NameNode nodeTo) {
|
||||
exists(Cfg::NameNode toVariant |
|
||||
SsaImpl::AdjacentUses::firstUse(var.getDefinition(), toVariant) and
|
||||
toVariant.getNode() = nodeTo.getNode() and
|
||||
Cfg::isCanonicalAstNodeRepresentative(nodeTo)
|
||||
)
|
||||
predicate defToFirstUse(EssaVariable var, NameNode nodeTo) {
|
||||
AdjacentUses::firstUse(var.getDefinition(), nodeTo)
|
||||
}
|
||||
|
||||
predicate useUseFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
@@ -443,14 +390,12 @@ module LocalFlow {
|
||||
// `x = f(y)`
|
||||
// nodeFrom is `y` on first line
|
||||
// nodeTo is `y` on second line
|
||||
exists(SsaImpl::EssaDefinition def, Cfg::NameNode toVariant |
|
||||
nodeFrom.(CfgNode).getNode() = def.(SsaImpl::EssaNodeDefinition).getDefiningNode()
|
||||
exists(EssaDefinition def |
|
||||
nodeFrom.(CfgNode).getNode() = def.(EssaNodeDefinition).getDefiningNode()
|
||||
or
|
||||
nodeFrom.(ScopeEntryDefinitionNode).getDefinition() = def
|
||||
|
|
||||
SsaImpl::AdjacentUses::firstUse(def, toVariant) and
|
||||
toVariant.getNode() = nodeTo.(CfgNode).getNode().getNode() and
|
||||
Cfg::isCanonicalAstNodeRepresentative(nodeTo.(CfgNode).getNode())
|
||||
AdjacentUses::firstUse(def, nodeTo.(CfgNode).getNode())
|
||||
)
|
||||
or
|
||||
// Next use after use
|
||||
@@ -612,9 +557,9 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
|
||||
// a parameter with a default value, since the parameter will be in the scope of the
|
||||
// function, while the default value itself will be in the scope that _defines_ the
|
||||
// function.
|
||||
exists(SsaImpl::ParameterDefinition param |
|
||||
exists(ParameterDefinition param |
|
||||
// note: we go to the _control-flow node_ of the parameter, and not the ESSA node of the parameter, since for type-tracking, the ESSA node is not a LocalSourceNode, so we would get in trouble.
|
||||
nodeFrom.asCfgNode().getNode() = param.getParameter().(Parameter).getDefault() and
|
||||
nodeFrom.asCfgNode() = param.getDefault() and
|
||||
nodeTo.asCfgNode() = param.getDefiningNode()
|
||||
)
|
||||
or
|
||||
@@ -718,7 +663,7 @@ predicate neverSkipInPathGraph(Node n) {
|
||||
// ```
|
||||
// we would end up saying that the path MUST not skip the x in `y = x`, which is just
|
||||
// annoying and doesn't help the path explanation become clearer.
|
||||
n.asCfgNode() = any(SsaImpl::EssaNodeDefinition def).getDefiningNode()
|
||||
n.asCfgNode() = any(EssaNodeDefinition def).getDefiningNode()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -927,7 +872,7 @@ predicate listStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo)
|
||||
// nodeFrom is `42`, cfg node
|
||||
// nodeTo is the list, `[..., 42, ...]`, cfg node
|
||||
// c denotes element of list
|
||||
nodeTo.getNode().(Cfg::ListNode).getAnElement() = nodeFrom.getNode() and
|
||||
nodeTo.getNode().(ListNode).getAnElement() = nodeFrom.getNode() and
|
||||
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
|
||||
// Suppress unused variable warning
|
||||
c = c
|
||||
@@ -940,7 +885,7 @@ predicate setStoreStep(CfgNode nodeFrom, SetElementContent c, CfgNode nodeTo) {
|
||||
// nodeFrom is `42`, cfg node
|
||||
// nodeTo is the set, `{..., 42, ...}`, cfg node
|
||||
// c denotes element of list
|
||||
nodeTo.getNode().(Cfg::SetNode).getAnElement() = nodeFrom.getNode() and
|
||||
nodeTo.getNode().(SetNode).getAnElement() = nodeFrom.getNode() and
|
||||
// Suppress unused variable warning
|
||||
c = c
|
||||
}
|
||||
@@ -953,7 +898,7 @@ predicate tupleStoreStep(CfgNode nodeFrom, TupleElementContent c, CfgNode nodeTo
|
||||
// nodeTo is the tuple, `(..., 42, ...)`, cfg node
|
||||
// c denotes element of tuple and index of nodeFrom
|
||||
exists(int n |
|
||||
nodeTo.getNode().(Cfg::TupleNode).getElement(n) = nodeFrom.getNode() and
|
||||
nodeTo.getNode().(TupleNode).getElement(n) = nodeFrom.getNode() and
|
||||
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
|
||||
c.getIndex() = n
|
||||
)
|
||||
@@ -967,7 +912,7 @@ predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeT
|
||||
// nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node
|
||||
// c denotes element of dictionary and the key `"key"`
|
||||
exists(KeyValuePair item |
|
||||
item = nodeTo.asCfgNode().(Cfg::DictNode).getNode().(Dict).getAnItem() and
|
||||
item = nodeTo.asCfgNode().(DictNode).getNode().(Dict).getAnItem() and
|
||||
nodeFrom.getNode().getNode() = item.getValue() and
|
||||
c.getKey() = item.getKey().(StringLiteral).getS()
|
||||
)
|
||||
@@ -982,9 +927,9 @@ predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeT
|
||||
private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
|
||||
// NOTE: It's important to add logic to the newtype definition of
|
||||
// DictionaryElementContent if you add new cases here.
|
||||
exists(Cfg::SubscriptNode subscript |
|
||||
exists(SubscriptNode subscript |
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() = subscript.getObject() and
|
||||
nodeFrom.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
nodeFrom.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
|
||||
)
|
||||
or
|
||||
@@ -997,8 +942,8 @@ private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent
|
||||
}
|
||||
|
||||
predicate dictClearStep(Node node, DictionaryElementContent c) {
|
||||
exists(Cfg::SubscriptNode subscript |
|
||||
subscript instanceof Cfg::DefinitionNode and
|
||||
exists(SubscriptNode subscript |
|
||||
subscript instanceof DefinitionNode and
|
||||
node.asCfgNode() = subscript.getObject() and
|
||||
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
|
||||
)
|
||||
@@ -1073,7 +1018,7 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
// nodeFrom is `l`, cfg node
|
||||
// nodeTo is `l[3]`, cfg node
|
||||
// c is compatible with 3
|
||||
nodeFrom.getNode() = nodeTo.getNode().(Cfg::SubscriptNode).getObject() and
|
||||
nodeFrom.getNode() = nodeTo.getNode().(SubscriptNode).getObject() and
|
||||
(
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
@@ -1082,10 +1027,10 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
c instanceof DictionaryElementAnyContent
|
||||
or
|
||||
c.(TupleElementContent).getIndex() =
|
||||
nodeTo.getNode().(Cfg::SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
|
||||
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
|
||||
or
|
||||
c.(DictionaryElementContent).getKey() =
|
||||
nodeTo.getNode().(Cfg::SubscriptNode).getIndex().getNode().(StringLiteral).getS()
|
||||
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(StringLiteral).getS()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,11 @@ overlay[local]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import DataFlowPrivate
|
||||
import semmle.python.dataflow.new.TypeTracking
|
||||
import Attributes
|
||||
import LocalSources
|
||||
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||
private import semmle.python.essa.SsaCompute
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
@@ -28,21 +27,16 @@ private import semmle.python.frameworks.data.ModelsAsData
|
||||
overlay[local]
|
||||
newtype TNode =
|
||||
/** A node corresponding to a control flow node. */
|
||||
TCfgNode(Cfg::ControlFlowNode node) {
|
||||
(
|
||||
isExpressionNode(node)
|
||||
or
|
||||
node.getNode() instanceof Pattern
|
||||
) and
|
||||
Cfg::isCanonicalAstNodeRepresentative(node)
|
||||
TCfgNode(ControlFlowNode node) {
|
||||
isExpressionNode(node)
|
||||
or
|
||||
node.getNode() instanceof Pattern
|
||||
} or
|
||||
/**
|
||||
* A node corresponding to a scope entry definition. That is, the value of a variable
|
||||
* as it enters a scope.
|
||||
*/
|
||||
TScopeEntryDefinitionNode(SsaImpl::ScopeEntryDefinition def) {
|
||||
not def.getScope() instanceof Module
|
||||
} or
|
||||
TScopeEntryDefinitionNode(ScopeEntryDefinition def) { not def.getScope() instanceof Module } or
|
||||
/**
|
||||
* A synthetic node representing the value of an object before a state change.
|
||||
*
|
||||
@@ -53,39 +47,36 @@ newtype TNode =
|
||||
// NOTE: since we can't rely on the call graph, but we want to have synthetic
|
||||
// pre-update nodes for class calls, we end up getting synthetic pre-update nodes for
|
||||
// ALL calls :|
|
||||
TSyntheticPreUpdateNode(Cfg::CallNode call) { Cfg::isCanonicalAstNodeRepresentative(call) } or
|
||||
TSyntheticPreUpdateNode(CallNode call) or
|
||||
/**
|
||||
* A synthetic node representing the value of an object after a state change.
|
||||
* See QLDoc for `PostUpdateNode`.
|
||||
*/
|
||||
TSyntheticPostUpdateNode(Cfg::ControlFlowNode node) {
|
||||
Cfg::isCanonicalAstNodeRepresentative(node) and
|
||||
(
|
||||
exists(Cfg::CallNode call |
|
||||
node = call.getArg(_)
|
||||
or
|
||||
node = call.getArgByName(_)
|
||||
or
|
||||
// `self` argument when handling class instance calls (`__call__` special method))
|
||||
node = call.getFunction()
|
||||
)
|
||||
TSyntheticPostUpdateNode(ControlFlowNode node) {
|
||||
exists(CallNode call |
|
||||
node = call.getArg(_)
|
||||
or
|
||||
node = any(Cfg::AttrNode a).getObject()
|
||||
node = call.getArgByName(_)
|
||||
or
|
||||
node = any(Cfg::SubscriptNode s).getObject()
|
||||
or
|
||||
// self parameter when used implicitly in `super()`
|
||||
exists(Class cls, Function func, SsaImpl::ParameterDefinition def |
|
||||
func = cls.getAMethod() and
|
||||
not isStaticmethod(func) and
|
||||
// this matches what we do in ExtractedParameterNode
|
||||
def.getDefiningNode() = node and
|
||||
def.getParameter() = func.getArg(0)
|
||||
)
|
||||
or
|
||||
// the iterable argument to the implicit comprehension function
|
||||
node.getNode() = any(Comp c).getIterable()
|
||||
// `self` argument when handling class instance calls (`__call__` special method))
|
||||
node = call.getFunction()
|
||||
)
|
||||
or
|
||||
node = any(AttrNode a).getObject()
|
||||
or
|
||||
node = any(SubscriptNode s).getObject()
|
||||
or
|
||||
// self parameter when used implicitly in `super()`
|
||||
exists(Class cls, Function func, ParameterDefinition def |
|
||||
func = cls.getAMethod() and
|
||||
not isStaticmethod(func) and
|
||||
// this matches what we do in ExtractedParameterNode
|
||||
def.getDefiningNode() = node and
|
||||
def.getParameter() = func.getArg(0)
|
||||
)
|
||||
or
|
||||
// the iterable argument to the implicit comprehension function
|
||||
node.getNode() = any(Comp c).getIterable()
|
||||
} or
|
||||
/** A node representing a global (module-level) variable in a specific module. */
|
||||
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m } or
|
||||
@@ -121,9 +112,7 @@ newtype TNode =
|
||||
exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos)))
|
||||
} or
|
||||
/** A synthetic node to capture keyword arguments that are passed to a `**kwargs` parameter. */
|
||||
TSynthDictSplatArgumentNode(Cfg::CallNode call) {
|
||||
exists(call.getArgByName(_)) and Cfg::isCanonicalAstNodeRepresentative(call)
|
||||
} or
|
||||
TSynthDictSplatArgumentNode(CallNode call) { exists(call.getArgByName(_)) } or
|
||||
/** A synthetic node to allow flow to keyword parameters from a `**kwargs` argument. */
|
||||
TSynthDictSplatParameterNode(DataFlowCallable callable) {
|
||||
exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos)))
|
||||
@@ -139,17 +128,15 @@ newtype TNode =
|
||||
* A synthetic node representing the values of the variables captured
|
||||
* by the callable being called.
|
||||
*/
|
||||
TSynthCapturedVariablesArgumentNode(Cfg::ControlFlowNode callable) {
|
||||
callable = any(Cfg::CallNode c).getFunction() and
|
||||
Cfg::isCanonicalAstNodeRepresentative(callable)
|
||||
TSynthCapturedVariablesArgumentNode(ControlFlowNode callable) {
|
||||
callable = any(CallNode c).getFunction()
|
||||
} or
|
||||
/**
|
||||
* A synthetic node representing the values of the variables captured
|
||||
* by the callable being called, after the output has been computed.
|
||||
*/
|
||||
TSynthCapturedVariablesArgumentPostUpdateNode(Cfg::ControlFlowNode callable) {
|
||||
callable = any(Cfg::CallNode c).getFunction() and
|
||||
Cfg::isCanonicalAstNodeRepresentative(callable)
|
||||
TSynthCapturedVariablesArgumentPostUpdateNode(ControlFlowNode callable) {
|
||||
callable = any(CallNode c).getFunction()
|
||||
} or
|
||||
/** A synthetic node representing the values of variables captured by a comprehension. */
|
||||
TSynthCompCapturedVariablesArgumentNode(Comp comp) {
|
||||
@@ -207,7 +194,7 @@ class Node extends TNode {
|
||||
}
|
||||
|
||||
/** Gets the control-flow node corresponding to this node, if any. */
|
||||
Cfg::ControlFlowNode asCfgNode() { none() }
|
||||
ControlFlowNode asCfgNode() { none() }
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { none() }
|
||||
@@ -220,14 +207,14 @@ class Node extends TNode {
|
||||
|
||||
/** A data-flow node corresponding to a control-flow node. */
|
||||
class CfgNode extends Node, TCfgNode {
|
||||
Cfg::ControlFlowNode node;
|
||||
ControlFlowNode node;
|
||||
|
||||
CfgNode() { this = TCfgNode(node) }
|
||||
|
||||
/** Gets the `Cfg::ControlFlowNode` represented by this data-flow node. */
|
||||
Cfg::ControlFlowNode getNode() { result = node }
|
||||
/** Gets the `ControlFlowNode` represented by this data-flow node. */
|
||||
ControlFlowNode getNode() { result = node }
|
||||
|
||||
override Cfg::ControlFlowNode asCfgNode() { result = node }
|
||||
override ControlFlowNode asCfgNode() { result = node }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = node.toString() }
|
||||
@@ -237,9 +224,9 @@ class CfgNode extends Node, TCfgNode {
|
||||
override Location getLocation() { result = node.getLocation() }
|
||||
}
|
||||
|
||||
/** A data-flow node corresponding to a `Cfg::CallNode` in the control-flow graph. */
|
||||
/** A data-flow node corresponding to a `CallNode` in the control-flow graph. */
|
||||
class CallCfgNode extends CfgNode, LocalSourceNode {
|
||||
override Cfg::CallNode node;
|
||||
override CallNode node;
|
||||
|
||||
/**
|
||||
* Gets the data-flow node for the function component of the call corresponding to this data-flow
|
||||
@@ -320,15 +307,15 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
|
||||
* as it enters a scope.
|
||||
*/
|
||||
class ScopeEntryDefinitionNode extends Node, TScopeEntryDefinitionNode {
|
||||
SsaImpl::ScopeEntryDefinition def;
|
||||
ScopeEntryDefinition def;
|
||||
|
||||
ScopeEntryDefinitionNode() { this = TScopeEntryDefinitionNode(def) }
|
||||
|
||||
/** Gets the `SsaImpl::ScopeEntryDefinition` associated with this node. */
|
||||
SsaImpl::ScopeEntryDefinition getDefinition() { result = def }
|
||||
/** Gets the `ScopeEntryDefinition` associated with this node. */
|
||||
ScopeEntryDefinition getDefinition() { result = def }
|
||||
|
||||
/** Gets the source variable represented by this node. */
|
||||
SsaImpl::SsaSourceVariable getVariable() { result = def.getSourceVariable() }
|
||||
SsaSourceVariable getVariable() { result = def.getSourceVariable() }
|
||||
|
||||
override Location getLocation() { result = def.getLocation() }
|
||||
|
||||
@@ -350,7 +337,7 @@ class ParameterNode extends Node instanceof ParameterNodeImpl {
|
||||
/** A parameter node found in the source code (not in a summary). */
|
||||
class ExtractedParameterNode extends ParameterNodeImpl, CfgNode {
|
||||
//, LocalSourceNode {
|
||||
SsaImpl::ParameterDefinition def;
|
||||
ParameterDefinition def;
|
||||
|
||||
ExtractedParameterNode() { node = def.getDefiningNode() }
|
||||
|
||||
@@ -381,10 +368,10 @@ Node getCallArgApproximation() {
|
||||
exists(Class c | result.asExpr() = c.getAMethod().getArg(0))
|
||||
or
|
||||
// the object part of an attribute expression (which might be a bound method)
|
||||
result.asCfgNode() = any(Cfg::AttrNode a).getObject()
|
||||
result.asCfgNode() = any(AttrNode a).getObject()
|
||||
or
|
||||
// the function part of any call
|
||||
result.asCfgNode() = any(Cfg::CallNode c).getFunction()
|
||||
result.asCfgNode() = any(CallNode c).getFunction()
|
||||
}
|
||||
|
||||
/** Gets the extracted argument nodes that do not rely on `getCallArg`. */
|
||||
@@ -393,7 +380,7 @@ private Node implicitArgumentNode() {
|
||||
normalCallArg(_, result, _)
|
||||
or
|
||||
// and self arguments
|
||||
result.asCfgNode() = any(Cfg::CallNode c).getFunction().(Cfg::AttrNode).getObject()
|
||||
result.asCfgNode() = any(CallNode c).getFunction().(AttrNode).getObject()
|
||||
or
|
||||
// for comprehensions, we allow the synthetic `iterable` argument
|
||||
result.asExpr() = any(Comp c).getIterable()
|
||||
@@ -498,24 +485,21 @@ 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
|
||||
}
|
||||
|
||||
/** Gets a CFG node that corresponds to an assignment of this global variable. */
|
||||
/** Gets an `EssaNode` that corresponds to an assignment of this global variable. */
|
||||
Node getAWrite() {
|
||||
exists(Cfg::NameNode n |
|
||||
n.defines(var) and
|
||||
result.asCfgNode() = n
|
||||
)
|
||||
any(EssaNodeDefinition def).definedBy(var, result.asCfgNode().(DefinitionNode))
|
||||
}
|
||||
|
||||
/** Gets the possible values of the variable at the end of import time */
|
||||
CfgNode getADefiningWrite() {
|
||||
exists(SsaImpl::EssaVariable def |
|
||||
def = any(SsaImpl::EssaVariable ssa_var).getAnUltimateDefinition() and
|
||||
def.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode() = result.asCfgNode() and
|
||||
def.getSourceVariable().getVariable() = var
|
||||
exists(SsaVariable def |
|
||||
def = any(SsaVariable ssa_var).getAnUltimateDefinition() and
|
||||
def.getDefinition() = result.asCfgNode() and
|
||||
def.getVariable() = var
|
||||
)
|
||||
}
|
||||
|
||||
@@ -532,7 +516,7 @@ private ModuleVariableNode import_star_read(Node n) {
|
||||
overlay[global]
|
||||
pragma[nomagic]
|
||||
private predicate resolved_import_star_module(Module m, string name, Node n) {
|
||||
exists(Cfg::NameNode nn | nn = n.asCfgNode() |
|
||||
exists(NameNode nn | nn = n.asCfgNode() |
|
||||
ImportStar::importStarResolvesTo(pragma[only_bind_into](nn), m) and
|
||||
nn.getId() = name
|
||||
)
|
||||
@@ -590,110 +574,88 @@ class StarPatternElementNode extends Node, TStarPatternElementNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that participates in a conditional split: a CFG node whose
|
||||
* evaluation outcome (true/false) is used to choose between two
|
||||
* successor basic blocks. In the new shared CFG, such nodes appear in
|
||||
* pairs of `isAfterTrue`/`isAfterFalse` annotated CFG nodes.
|
||||
* Gets a node that controls whether other nodes are evaluated.
|
||||
*
|
||||
* Users typically obtain a `GuardNode` by casting from a more specific
|
||||
* Cfg type: `g.(Cfg::CallNode)` for a call-based check, etc.
|
||||
* In the base case, this is the last node of `conditionBlock`, and `flipped` is `false`.
|
||||
* This definition accounts for (short circuting) `and`- and `or`-expressions, as the structure
|
||||
* of basic blocks will reflect their semantics.
|
||||
*
|
||||
* This replaces the legacy (pre-shared-CFG) `GuardNode`/`flipped`
|
||||
* machinery: the shared CFG carries outcome information structurally
|
||||
* (via `isAfterTrue`/`isAfterFalse`), so no separate polarity field
|
||||
* is required.
|
||||
* However, in the program
|
||||
* ```python
|
||||
* if not is_safe(path):
|
||||
* return
|
||||
* ```
|
||||
* the last node in the `ConditionBlock` is `not is_safe(path)`.
|
||||
*
|
||||
* We would like to consider also `is_safe(path)` a guard node, albeit with `flipped` being `true`.
|
||||
* Thus we recurse through `not`-expressions.
|
||||
*/
|
||||
class GuardNode extends Cfg::ControlFlowNode {
|
||||
GuardNode() {
|
||||
// This is the canonical (post-order) version of an AST node, and
|
||||
// some `[true]`/`[false]` variant of the same AST exists. We
|
||||
// include the canonical node because users identify guards by
|
||||
// their AST (`g.(Cfg::CallNode)` etc.), and the outcome-tagged
|
||||
// variants are accessed by `outcomeOfGuard` below.
|
||||
exists(Cfg::ControlFlowNode outcome |
|
||||
outcome.getNode() = this.getNode() and
|
||||
(outcome.isAfterTrue(_) or outcome.isAfterFalse(_))
|
||||
ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) {
|
||||
// Base case: the last node truly does determine which successor is chosen
|
||||
result = conditionBlock.getLastNode() and
|
||||
flipped = false
|
||||
or
|
||||
// Recursive cases:
|
||||
// if a guard node is a `not`-expression,
|
||||
// the operand is also a guard node, but with inverted polarity.
|
||||
exists(UnaryExprNode notNode |
|
||||
result = notNode.getOperand() and
|
||||
notNode.getNode().getOp() instanceof Not
|
||||
|
|
||||
notNode = guardNode(conditionBlock, flipped.booleanNot())
|
||||
)
|
||||
or
|
||||
// if a guard node is compared to a boolean literal,
|
||||
// the other operand is also a guard node,
|
||||
// but with polarity depending on the literal (and on the comparison).
|
||||
exists(CompareNode cmpNode, Cmpop op, ControlFlowNode b, boolean should_flip |
|
||||
(
|
||||
cmpNode.operands(result, op, b) or
|
||||
cmpNode.operands(b, op, result)
|
||||
) and
|
||||
not result.getNode() instanceof BooleanLiteral and
|
||||
(
|
||||
// comparing to the boolean
|
||||
(op instanceof Eq or op instanceof Is) and
|
||||
// we should flip if the value compared against, here the value of `b`, is false
|
||||
should_flip = b.getNode().(BooleanLiteral).booleanValue().booleanNot()
|
||||
or
|
||||
// comparing to the negation of the boolean
|
||||
(op instanceof NotEq or op instanceof IsNot) and
|
||||
// again, we should flip if the value compared against, here the value of `not b`, is false.
|
||||
// That is, if the value of `b` is true.
|
||||
should_flip = b.getNode().(BooleanLiteral).booleanValue()
|
||||
)
|
||||
or
|
||||
// Or: this IS one of the outcome-tagged variants, supporting
|
||||
// users who want to query the split point directly.
|
||||
this.isAfterTrue(_)
|
||||
or
|
||||
this.isAfterFalse(_)
|
||||
}
|
||||
|
||||
/** Holds if this guard controls block `b` upon evaluating to `branch`. */
|
||||
predicate controlsBlock(Cfg::BasicBlock b, boolean branch) {
|
||||
branch in [true, false] and
|
||||
exists(Cfg::ControlFlowNode outcomeNode |
|
||||
outcomeOfGuard(this, outcomeNode, branch) and
|
||||
outcomeNode.getBasicBlock().dominates(b)
|
||||
)
|
||||
}
|
||||
|
|
||||
// we flip `flipped` according to `should_flip` via the formula `flipped xor should_flip`.
|
||||
flipped in [true, false] and
|
||||
cmpNode = guardNode(conditionBlock, flipped.booleanXor(should_flip))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if some execution that arrives at `outcomeNode` corresponds
|
||||
* to `guard` having evaluated to `branch`.
|
||||
* A node that controls whether other nodes are evaluated.
|
||||
*
|
||||
* For a direct guard `if g:`, the outcome node is `g` itself with
|
||||
* `isAfterTrue`/`isAfterFalse`. For wrapped guards like `not g` or
|
||||
* `g == True`, the outcome is on the wrapping expression with an
|
||||
* appropriate polarity transform — we follow those wrappers up the
|
||||
* AST to find the outermost expression that carries an actual
|
||||
* `isAfterTrue`/`isAfterFalse` outcome.
|
||||
* The field `flipped` allows us to match `GuardNode`s underneath
|
||||
* `not`-expressions and still choose the appropriate branch.
|
||||
*/
|
||||
private predicate outcomeOfGuard(
|
||||
Cfg::ControlFlowNode guard, Cfg::ControlFlowNode outcomeNode, boolean branch
|
||||
) {
|
||||
// Base case: the guard itself splits — the outcome node is the
|
||||
// first node of an outcome BB, with matching outcome label.
|
||||
// (The shared CFG also marks inner expressions with outcome flags
|
||||
// for analysis purposes, but only "splitting" nodes — those that
|
||||
// actually start an outcome BB — are valid guards on their own.)
|
||||
outcomeNode.getNode() = guard.getNode() and
|
||||
outcomeNode = outcomeNode.getBasicBlock().firstNode() and
|
||||
(
|
||||
outcomeNode.isAfterTrue(_) and branch = true
|
||||
or
|
||||
outcomeNode.isAfterFalse(_) and branch = false
|
||||
)
|
||||
or
|
||||
// Recursive: `not guard` — same outcome split as `guard`, flipped.
|
||||
exists(Cfg::UnaryExprNode notNode, boolean notBranch |
|
||||
notNode.getOperand().getNode() = guard.getNode() and
|
||||
notNode.getNode().getOp() instanceof Not and
|
||||
outcomeOfGuard(notNode, outcomeNode, notBranch) and
|
||||
branch = notBranch.booleanNot()
|
||||
)
|
||||
or
|
||||
// Recursive: comparisons against a boolean literal.
|
||||
exists(
|
||||
Cfg::CompareNode cmpNode, Cmpop op, Cfg::ControlFlowNode otherOperand,
|
||||
Cfg::ControlFlowNode guardOperand, boolean polarity, boolean cmpBranch
|
||||
|
|
||||
guardOperand.getNode() = guard.getNode() and
|
||||
(
|
||||
cmpNode.operands(guardOperand, op, otherOperand) or
|
||||
cmpNode.operands(otherOperand, op, guardOperand)
|
||||
) and
|
||||
not guard.getNode() instanceof BooleanLiteral and
|
||||
(
|
||||
(op instanceof Eq or op instanceof Is) and
|
||||
polarity = otherOperand.getNode().(BooleanLiteral).booleanValue()
|
||||
or
|
||||
(op instanceof NotEq or op instanceof IsNot) and
|
||||
polarity = otherOperand.getNode().(BooleanLiteral).booleanValue().booleanNot()
|
||||
) and
|
||||
outcomeOfGuard(cmpNode, outcomeNode, cmpBranch) and
|
||||
branch = cmpBranch.booleanXor(polarity.booleanNot())
|
||||
)
|
||||
class GuardNode extends ControlFlowNode {
|
||||
ConditionBlock conditionBlock;
|
||||
boolean flipped;
|
||||
|
||||
GuardNode() { this = guardNode(conditionBlock, flipped) }
|
||||
|
||||
/** Holds if this guard controls block `b` upon evaluating to `branch`. */
|
||||
predicate controlsBlock(BasicBlock b, boolean branch) {
|
||||
branch in [true, false] and
|
||||
conditionBlock.controls(b, branch.booleanXor(flipped))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` validates `node` upon evaluating to `branch`.
|
||||
*/
|
||||
signature predicate guardChecksSig(GuardNode g, Cfg::ControlFlowNode node, boolean branch);
|
||||
signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch);
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates a node.
|
||||
@@ -708,9 +670,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
result = ParameterizedBarrierGuard<Unit, extendedGuardChecks/4>::getABarrierNode(_)
|
||||
}
|
||||
|
||||
private predicate extendedGuardChecks(
|
||||
GuardNode g, Cfg::ControlFlowNode node, boolean branch, Unit u
|
||||
) {
|
||||
private predicate extendedGuardChecks(GuardNode g, ControlFlowNode node, boolean branch, Unit u) {
|
||||
guardChecks(g, node, branch) and
|
||||
u = u
|
||||
}
|
||||
@@ -720,7 +680,7 @@ bindingset[this]
|
||||
private signature class ParamSig;
|
||||
|
||||
private module WithParam<ParamSig P> {
|
||||
signature predicate guardChecksSig(GuardNode g, Cfg::ControlFlowNode node, boolean branch, P param);
|
||||
signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch, P param);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -733,10 +693,10 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
|
||||
/** Gets a node that is safely guarded by the given guard check with parameter `param`. */
|
||||
overlay[global]
|
||||
ExprNode getABarrierNode(P param) {
|
||||
exists(GuardNode g, SsaImpl::EssaDefinition def, Cfg::ControlFlowNode node, boolean branch |
|
||||
SsaImpl::AdjacentUses::useOfDef(def, node) and
|
||||
exists(GuardNode g, EssaDefinition def, ControlFlowNode node, boolean branch |
|
||||
AdjacentUses::useOfDef(def, node) and
|
||||
guardChecks(g, node, branch, param) and
|
||||
SsaImpl::AdjacentUses::useOfDef(def, result.asCfgNode()) and
|
||||
AdjacentUses::useOfDef(def, result.asCfgNode()) and
|
||||
g.controlsBlock(result.asCfgNode().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
@@ -752,7 +712,7 @@ module ExternalBarrierGuard {
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
overlay[global]
|
||||
private predicate guardCheck(GuardNode g, Cfg::ControlFlowNode node, boolean branch, string kind) {
|
||||
private predicate guardCheck(GuardNode g, ControlFlowNode node, boolean branch, string kind) {
|
||||
exists(API::CallNode call, API::Node parameter |
|
||||
parameter = call.getAParameter() and
|
||||
parameter = ModelOutput::getABarrierGuardNode(kind, branch)
|
||||
@@ -788,10 +748,10 @@ newtype TContent =
|
||||
TSetElementContent() or
|
||||
/** An element of a tuple at a specific index. */
|
||||
TTupleElementContent(int index) {
|
||||
exists(any(Cfg::TupleNode tn).getElement(index))
|
||||
exists(any(TupleNode tn).getElement(index))
|
||||
or
|
||||
// Arguments can overflow and end up in the starred parameter tuple.
|
||||
exists(any(Cfg::CallNode cn).getArg(index))
|
||||
exists(any(CallNode cn).getArg(index))
|
||||
or
|
||||
// since flow summaries might use tuples, we ensure that we at least have valid
|
||||
// TTupleElementContent for the 0..7 (7 was picked to match `small_tuple` in
|
||||
@@ -808,14 +768,10 @@ newtype TContent =
|
||||
or
|
||||
// d["key"] = ...
|
||||
key =
|
||||
any(Cfg::SubscriptNode sub |
|
||||
sub.isStore()
|
||||
|
|
||||
sub.getIndex().getNode().(StringLiteral).getText()
|
||||
)
|
||||
any(SubscriptNode sub | sub.isStore() | sub.getIndex().getNode().(StringLiteral).getText())
|
||||
or
|
||||
// d.setdefault("key", ...)
|
||||
exists(Cfg::CallNode call | call.getFunction().(Cfg::AttrNode).getName() = "setdefault" |
|
||||
exists(CallNode call | call.getFunction().(AttrNode).getName() = "setdefault" |
|
||||
key = call.getArg(0).getNode().(StringLiteral).getText()
|
||||
)
|
||||
} or
|
||||
|
||||
@@ -5,24 +5,11 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||
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 the name of `var` refers to a submodule of a package and `init` is
|
||||
* the `__init__` module of that package. Locally inlined replacement for the
|
||||
* legacy `SsaSource::init_module_submodule_defn` so that this module has no
|
||||
* direct dependency on `semmle.python.essa.SsaDefinitions`.
|
||||
*/
|
||||
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
|
||||
@@ -82,19 +69,13 @@ module ImportResolution {
|
||||
* Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed
|
||||
* for import resolution.
|
||||
*/
|
||||
private predicate allowedEssaImportStep(
|
||||
SsaImpl::EssaDefinition defFrom, SsaImpl::EssaDefinition defTo
|
||||
) {
|
||||
private predicate allowedEssaImportStep(EssaDefinition defFrom, EssaDefinition defTo) {
|
||||
// to handle definitions guarded by if-then-else
|
||||
defFrom = defTo.(SsaImpl::PhiFunction).getAnInput()
|
||||
defFrom = defTo.(PhiFunction).getAnInput()
|
||||
or
|
||||
// to handle uncertain writes such as `from X import *`, which create an
|
||||
// uncertain SSA definition for every name in the importing scope. The
|
||||
// immediately preceding definition is still potentially the value of the
|
||||
// module export.
|
||||
SsaImpl::Ssa::uncertainWriteDefinitionInput(defTo, defFrom)
|
||||
// Note: legacy ESSA refinement-step (e.g. for `foo.bar = X`) is
|
||||
// not modelled in the new SSA beyond the cases handled above.
|
||||
// refined variable
|
||||
// example: https://github.com/nvbn/thefuck/blob/ceeaeab94b5df5a4fe9d94d61e4f6b0bbea96378/thefuck/utils.py#L25-L45
|
||||
defFrom = defTo.(EssaNodeRefinement).getInput().getDefinition()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,32 +92,30 @@ module ImportResolution {
|
||||
// Definitions made inside `m` itself
|
||||
//
|
||||
// for code such as `foo = ...; foo.bar = ...` there will be TWO
|
||||
// SsaImpl::EssaDefinition/SsaImpl::EssaVariable. One for `foo = ...` (SsaImpl::AssignmentDefinition) and one
|
||||
// EssaDefinition/EssaVariable. One for `foo = ...` (AssignmentDefinition) and one
|
||||
// for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The
|
||||
// EssaNodeRefinement is the one that will reach the end of the module (normal
|
||||
// exit).
|
||||
//
|
||||
// However, we cannot just use the EssaNodeRefinement as the `val`, because the
|
||||
// normal data-flow depends on use-use flow, and use-use flow targets CFG nodes not
|
||||
// EssaNodes. So we need to go back from the SsaImpl::EssaDefinition/SsaImpl::EssaVariable that
|
||||
// EssaNodes. So we need to go back from the EssaDefinition/EssaVariable that
|
||||
// reaches the end of the module, to the first definition of the variable, and then
|
||||
// track forwards using use-use flow to find a suitable CFG node that has flow into
|
||||
// it from use-use flow.
|
||||
exists(SsaImpl::EssaVariable lastUseVar, SsaImpl::EssaVariable firstDef |
|
||||
exists(EssaVariable lastUseVar, EssaVariable firstDef |
|
||||
lastUseVar.getName() = name and
|
||||
// we ignore special variable $ introduced by our analysis (not used for anything)
|
||||
// we ignore special variable * introduced by `from <pkg> import *` -- TODO: understand why we even have this?
|
||||
not name in ["$", "*"] and
|
||||
exists(Cfg::ControlFlowNode exit |
|
||||
exit.isNormalExit() and exit.getScope() = m and lastUseVar.getAUse() = exit
|
||||
) and
|
||||
lastUseVar.getAUse() = m.getANormalExit() and
|
||||
allowedEssaImportStep*(firstDef, lastUseVar) and
|
||||
not allowedEssaImportStep(_, firstDef)
|
||||
|
|
||||
not LocalFlow::defToFirstUse(firstDef, _) and
|
||||
val.asCfgNode() = firstDef.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode()
|
||||
val.asCfgNode() = firstDef.getDefinition().(EssaNodeDefinition).getDefiningNode()
|
||||
or
|
||||
exists(Cfg::ControlFlowNode mid, Cfg::ControlFlowNode end |
|
||||
exists(ControlFlowNode mid, ControlFlowNode end |
|
||||
LocalFlow::defToFirstUse(firstDef, mid) and
|
||||
LocalFlow::useToNextUse*(mid, end) and
|
||||
not LocalFlow::useToNextUse(end, _) and
|
||||
@@ -164,9 +143,9 @@ module ImportResolution {
|
||||
* handles simple cases where we can statically tell that this is the case.
|
||||
*/
|
||||
private predicate all_mentions_name(Module m, string name) {
|
||||
exists(Cfg::DefinitionNode def, Cfg::SequenceNode n |
|
||||
exists(DefinitionNode def, SequenceNode n |
|
||||
def.getValue() = n and
|
||||
def.(Cfg::NameNode).getId() = "__all__" and
|
||||
def.(NameNode).getId() = "__all__" and
|
||||
def.getScope() = m and
|
||||
any(StringLiteral s | s.getText() = name) = n.getAnElement().getNode()
|
||||
)
|
||||
@@ -179,20 +158,18 @@ module ImportResolution {
|
||||
*/
|
||||
private predicate no_or_complicated_all(Module m) {
|
||||
// No mention of `__all__` in the module
|
||||
not exists(Cfg::DefinitionNode def |
|
||||
def.getScope() = m and def.(Cfg::NameNode).getId() = "__all__"
|
||||
)
|
||||
not exists(DefinitionNode def | def.getScope() = m and def.(NameNode).getId() = "__all__")
|
||||
or
|
||||
// `__all__` is set to a non-sequence value
|
||||
exists(Cfg::DefinitionNode def |
|
||||
def.(Cfg::NameNode).getId() = "__all__" and
|
||||
exists(DefinitionNode def |
|
||||
def.(NameNode).getId() = "__all__" and
|
||||
def.getScope() = m and
|
||||
not def.getValue() instanceof Cfg::SequenceNode
|
||||
not def.getValue() instanceof SequenceNode
|
||||
)
|
||||
or
|
||||
// `__all__` is used in some way that doesn't involve storing a value in it. This usually means
|
||||
// it is being mutated through `append` or `extend`, which we don't handle.
|
||||
exists(Cfg::NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
|
||||
exists(NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
|
||||
}
|
||||
|
||||
private predicate potential_module_export(Module m, string name) {
|
||||
@@ -200,7 +177,7 @@ module ImportResolution {
|
||||
or
|
||||
no_or_complicated_all(m) and
|
||||
(
|
||||
exists(Cfg::NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
|
||||
exists(NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
|
||||
or
|
||||
exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m)
|
||||
)
|
||||
@@ -230,12 +207,12 @@ module ImportResolution {
|
||||
|
||||
/** Gets a module that may have been added to `sys.modules`. */
|
||||
private Module sys_modules_module_with_name(string name) {
|
||||
exists(Cfg::ControlFlowNode n, DataFlow::Node mod |
|
||||
exists(Cfg::SubscriptNode sub |
|
||||
exists(ControlFlowNode n, DataFlow::Node mod |
|
||||
exists(SubscriptNode sub |
|
||||
sub.getObject() = sys_modules_reference().asCfgNode() and
|
||||
sub.getIndex() = n and
|
||||
n.getNode().(StringLiteral).getText() = name and
|
||||
sub.(Cfg::DefinitionNode).getValue() = mod.asCfgNode() and
|
||||
sub.(DefinitionNode).getValue() = mod.asCfgNode() and
|
||||
mod = getModuleReference(result)
|
||||
)
|
||||
)
|
||||
@@ -347,11 +324,11 @@ module ImportResolution {
|
||||
// name as a submodule, we always consider that this attribute _could_ be a
|
||||
// reference to the submodule, even if we don't know that the submodule has been
|
||||
// imported yet.
|
||||
exists(string submodule, Module package, SsaImpl::EssaVariable var |
|
||||
exists(string submodule, Module package, EssaVariable var |
|
||||
submodule = var.getName() and
|
||||
initModuleSubmoduleDefn(var.getSourceVariable().getVariable(), package) and
|
||||
SsaSource::init_module_submodule_defn(var.getSourceVariable(), package.getEntryNode()) and
|
||||
m = getModuleFromName(package.getPackageName() + "." + submodule) and
|
||||
result.asCfgNode() = var.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode()
|
||||
result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ overlay[local]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.internal.Builtins
|
||||
private import semmle.python.dataflow.new.internal.ImportResolution
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
@@ -16,7 +15,7 @@ module ImportStar {
|
||||
*/
|
||||
overlay[local]
|
||||
cached
|
||||
predicate namePossiblyDefinedInImportStar(Cfg::NameNode n, string name, Scope s) {
|
||||
predicate namePossiblyDefinedInImportStar(NameNode n, string name, Scope s) {
|
||||
n.isLoad() and
|
||||
name = n.getId() and
|
||||
s = n.getScope().getEnclosingScope*() and
|
||||
@@ -53,7 +52,7 @@ module ImportStar {
|
||||
/** Holds if a global variable called `name` is assigned a value in the module `m`. */
|
||||
cached
|
||||
predicate globalNameDefinedInModule(string name, Module m) {
|
||||
exists(Cfg::NameNode n |
|
||||
exists(NameNode n |
|
||||
not exists(LocalVariable v | n.defines(v)) and
|
||||
n.isStore() and
|
||||
name = n.getId() and
|
||||
@@ -67,7 +66,7 @@ module ImportStar {
|
||||
*/
|
||||
overlay[global]
|
||||
cached
|
||||
predicate importStarResolvesTo(Cfg::NameNode n, Module m) {
|
||||
predicate importStarResolvesTo(NameNode n, Module m) {
|
||||
m = getStarImported+(n.getEnclosingModule()) and
|
||||
globalNameDefinedInModule(n.getId(), m) and
|
||||
not isDefinedLocally(n.getNode())
|
||||
@@ -100,7 +99,7 @@ module ImportStar {
|
||||
*/
|
||||
overlay[local]
|
||||
cached
|
||||
Cfg::ControlFlowNode potentialImportStarBase(Scope s) {
|
||||
result = any(Cfg::ImportStarNode n | n.getScope() = s).getModule()
|
||||
ControlFlowNode potentialImportStarBase(Scope s) {
|
||||
result = any(ImportStarNode n | n.getScope() = s).getModule()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +170,6 @@ overlay[local]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||
private import DataFlowPublic
|
||||
|
||||
/**
|
||||
@@ -180,7 +178,7 @@ private import DataFlowPublic
|
||||
* This class abstracts away the differing representations of comprehensions and
|
||||
* for statements.
|
||||
*/
|
||||
class ForTarget extends Cfg::ControlFlowNode {
|
||||
class ForTarget extends ControlFlowNode {
|
||||
Expr source;
|
||||
|
||||
ForTarget() {
|
||||
@@ -200,7 +198,7 @@ class ForTarget extends Cfg::ControlFlowNode {
|
||||
}
|
||||
|
||||
/** The LHS of an assignment, it also records the assigned value. */
|
||||
class AssignmentTarget extends Cfg::ControlFlowNode {
|
||||
class AssignmentTarget extends ControlFlowNode {
|
||||
Expr value;
|
||||
|
||||
AssignmentTarget() {
|
||||
@@ -211,7 +209,7 @@ class AssignmentTarget extends Cfg::ControlFlowNode {
|
||||
}
|
||||
|
||||
/** A direct (or top-level) target of an unpacking assignment. */
|
||||
class UnpackingAssignmentDirectTarget extends Cfg::ControlFlowNode instanceof Cfg::SequenceNode {
|
||||
class UnpackingAssignmentDirectTarget extends ControlFlowNode instanceof SequenceNode {
|
||||
Expr value;
|
||||
|
||||
UnpackingAssignmentDirectTarget() {
|
||||
@@ -224,7 +222,7 @@ class UnpackingAssignmentDirectTarget extends Cfg::ControlFlowNode instanceof Cf
|
||||
}
|
||||
|
||||
/** A (possibly recursive) target of an unpacking assignment. */
|
||||
class UnpackingAssignmentTarget extends Cfg::ControlFlowNode {
|
||||
class UnpackingAssignmentTarget extends ControlFlowNode {
|
||||
UnpackingAssignmentTarget() {
|
||||
this instanceof UnpackingAssignmentDirectTarget
|
||||
or
|
||||
@@ -233,11 +231,10 @@ class UnpackingAssignmentTarget extends Cfg::ControlFlowNode {
|
||||
}
|
||||
|
||||
/** A (possibly recursive) target of an unpacking assignment which is also a sequence. */
|
||||
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof Cfg::SequenceNode
|
||||
{
|
||||
Cfg::ControlFlowNode getElement(int i) { result = super.getElement(i) }
|
||||
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof SequenceNode {
|
||||
ControlFlowNode getElement(int i) { result = super.getElement(i) }
|
||||
|
||||
Cfg::ControlFlowNode getAnElement() { result = this.getElement(_) }
|
||||
ControlFlowNode getAnElement() { result = this.getElement(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +255,7 @@ predicate iterableUnpackingAssignmentFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
predicate iterableUnpackingForReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
|
||||
exists(ForTarget target |
|
||||
nodeFrom.getNode().getNode() = target.getSource() and
|
||||
target instanceof Cfg::SequenceNode and
|
||||
target instanceof SequenceNode and
|
||||
nodeTo = TIterableSequenceNode(target)
|
||||
) and
|
||||
(
|
||||
@@ -326,11 +323,11 @@ predicate iterableUnpackingConvertingStoreStep(Node nodeFrom, Content c, Node no
|
||||
*/
|
||||
predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
exists(
|
||||
UnpackingAssignmentSequenceTarget target, int index, Cfg::ControlFlowNode element, int starIndex
|
||||
UnpackingAssignmentSequenceTarget target, int index, ControlFlowNode element, int starIndex
|
||||
|
|
||||
target.getElement(starIndex) instanceof Cfg::StarredNode
|
||||
target.getElement(starIndex) instanceof StarredNode
|
||||
or
|
||||
not exists(target.getAnElement().(Cfg::StarredNode)) and
|
||||
not exists(target.getAnElement().(StarredNode)) and
|
||||
starIndex = -1
|
||||
|
|
||||
nodeFrom.(CfgNode).getNode() = target and
|
||||
@@ -345,18 +342,18 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
|
||||
else c.(TupleElementContent).getIndex() >= index - 1
|
||||
) and
|
||||
(
|
||||
if element instanceof Cfg::SequenceNode
|
||||
if element instanceof SequenceNode
|
||||
then
|
||||
// Step 5b
|
||||
nodeTo = TIterableSequenceNode(element)
|
||||
else
|
||||
if element instanceof Cfg::StarredNode
|
||||
if element instanceof StarredNode
|
||||
then
|
||||
// Step 5c
|
||||
nodeTo = TIterableElementNode(element)
|
||||
else
|
||||
// Step 5a
|
||||
exists(SsaImpl::MultiAssignmentDefinition mad | element = mad.getDefiningNode() |
|
||||
exists(MultiAssignmentDefinition mad | element = mad.getDefiningNode() |
|
||||
nodeTo.(CfgNode).getNode() = element
|
||||
)
|
||||
)
|
||||
@@ -369,7 +366,7 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
|
||||
* content type `ListElementContent`.
|
||||
*/
|
||||
predicate iterableUnpackingStarredElementStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
exists(Cfg::ControlFlowNode starred, SsaImpl::MultiAssignmentDefinition mad |
|
||||
exists(ControlFlowNode starred, MultiAssignmentDefinition mad |
|
||||
starred.getNode() instanceof Starred and
|
||||
starred = mad.getDefiningNode()
|
||||
|
|
||||
|
||||
@@ -9,7 +9,6 @@ overlay[local]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
import DataFlowPublic
|
||||
private import DataFlowPrivate
|
||||
private import semmle.python.internal.CachedStages
|
||||
@@ -315,7 +314,7 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate subscript(LocalSourceNode node, CfgNode subscript, CfgNode index) {
|
||||
exists(CfgNode seq, Cfg::SubscriptNode subscriptNode | subscriptNode = subscript.getNode() |
|
||||
exists(CfgNode seq, SubscriptNode subscriptNode | subscriptNode = subscript.getNode() |
|
||||
node.flowsTo(seq) and
|
||||
seq.getNode() = subscriptNode.getObject() and
|
||||
index.getNode() = subscriptNode.getIndex()
|
||||
|
||||
@@ -55,7 +55,6 @@ module;
|
||||
|
||||
private import python
|
||||
private import DataFlowPublic
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
|
||||
/**
|
||||
* Holds when there is flow from the subject `nodeFrom` to the (top-level) pattern `nodeTo` of a `match` statement.
|
||||
@@ -92,8 +91,8 @@ predicate matchAsFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
or
|
||||
// the interior pattern flows to the alias
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject.getPattern() and
|
||||
exists(Cfg::ControlFlowNode aliasCfg | aliasCfg.getNode() = alias |
|
||||
nodeTo.(CfgNode).getNode() = aliasCfg
|
||||
exists(PatternAliasDefinition pad | pad.getDefiningNode().getNode() = alias |
|
||||
nodeTo.(CfgNode).getNode() = pad.getDefiningNode()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -127,8 +126,8 @@ predicate matchLiteralFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
predicate matchCaptureFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(MatchCapturePattern capture, Name var | capture.getVariable() = var |
|
||||
nodeFrom.(CfgNode).getNode().getNode() = capture and
|
||||
exists(Cfg::ControlFlowNode varCfg | varCfg.getNode() = var |
|
||||
nodeTo.(CfgNode).getNode() = varCfg
|
||||
exists(PatternCaptureDefinition pcd | pcd.getDefiningNode().getNode() = var |
|
||||
nodeTo.(CfgNode).getNode() = pcd.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))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
@@ -77,7 +75,7 @@ import Cached
|
||||
* and isn't a big problem in practice.
|
||||
*/
|
||||
predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
||||
exists(Cfg::BinaryExprNode add | add = nodeTo.getNode() |
|
||||
exists(BinaryExprNode add | add = nodeTo.getNode() |
|
||||
add.getOp() instanceof Add and add.getAnOperand() = nodeFrom.getNode()
|
||||
)
|
||||
}
|
||||
@@ -86,7 +84,7 @@ predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
||||
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to subscripting.
|
||||
*/
|
||||
predicate subscriptStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
||||
nodeTo.getNode().(Cfg::SubscriptNode).getObject() = nodeFrom.getNode()
|
||||
nodeTo.getNode().(SubscriptNode).getObject() = nodeFrom.getNode()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,15 +100,15 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
|
||||
(
|
||||
call = API::builtin(["str", "bytes", "unicode"]).getACall()
|
||||
or
|
||||
call.getFunction().asCfgNode().(Cfg::NameNode).getId() in ["str", "bytes", "unicode"]
|
||||
call.getFunction().asCfgNode().(NameNode).getId() in ["str", "bytes", "unicode"]
|
||||
) and
|
||||
nodeFrom in [call.getArg(0), call.getArgByName("object")]
|
||||
)
|
||||
or
|
||||
// String methods. Note that this doesn't recognize `meth = "foo".upper; meth()`
|
||||
exists(Cfg::CallNode call, string method_name, Cfg::ControlFlowNode object |
|
||||
exists(CallNode call, string method_name, ControlFlowNode object |
|
||||
call = nodeTo.getNode() and
|
||||
object = call.getFunction().(Cfg::AttrNode).getObject(method_name)
|
||||
object = call.getFunction().(AttrNode).getObject(method_name)
|
||||
|
|
||||
nodeFrom.getNode() = object and
|
||||
method_name in [
|
||||
@@ -141,7 +139,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
|
||||
)
|
||||
or
|
||||
// % formatting
|
||||
exists(Cfg::BinaryExprNode fmt | fmt = nodeTo.getNode() |
|
||||
exists(BinaryExprNode fmt | fmt = nodeTo.getNode() |
|
||||
fmt.getOp() instanceof Mod and
|
||||
(
|
||||
fmt.getLeft() = nodeFrom.getNode()
|
||||
@@ -151,7 +149,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
|
||||
)
|
||||
or
|
||||
// string multiplication -- `"foo" * 10`
|
||||
exists(Cfg::BinaryExprNode mult | mult = nodeTo.getNode() |
|
||||
exists(BinaryExprNode mult | mult = nodeTo.getNode() |
|
||||
mult.getOp() instanceof Mult and
|
||||
mult.getLeft() = nodeFrom.getNode()
|
||||
)
|
||||
@@ -209,8 +207,8 @@ predicate awaitStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
* the variable `f` is tainted if the result of `open("foo")` is tainted.
|
||||
*/
|
||||
predicate asyncWithStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(With with, Cfg::ControlFlowNode contextManager, Cfg::ControlFlowNode var |
|
||||
var = any(SsaImpl::WithDefinition wd).getDefiningNode()
|
||||
exists(With with, ControlFlowNode contextManager, ControlFlowNode var |
|
||||
var = any(WithDefinition wd).getDefiningNode()
|
||||
|
|
||||
nodeFrom.(DataFlow::CfgNode).getNode() = contextManager and
|
||||
nodeTo.(DataFlow::CfgNode).getNode() = var and
|
||||
|
||||
@@ -2,8 +2,6 @@ import codeql.util.Unit
|
||||
import codeql.typetracking.TypeTracking as Shared
|
||||
import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||
private import semmle.python.internal.CachedStages
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||
@@ -96,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
|
||||
@@ -164,7 +160,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
||||
// ignore the flow steps from the synthetic sequence node to the real sequence node,
|
||||
// since we only support one level of content in type-trackers, and the nested
|
||||
// structure requires two levels at least to be useful.
|
||||
not exists(Cfg::SequenceNode outer |
|
||||
not exists(SequenceNode outer |
|
||||
outer.getAnElement() = nodeTo.asCfgNode() and
|
||||
IterableUnpacking::iterableUnpackingTupleFlowStep(nodeFrom, nodeTo)
|
||||
)
|
||||
@@ -263,7 +259,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
||||
// Since we only support one level of content in type-trackers we don't actually
|
||||
// support `(aa, ab), (ba, bb) = ...`. Therefore we exclude the read-step from `(aa,
|
||||
// ab)` to `aa` (since it is not needed).
|
||||
not exists(Cfg::SequenceNode outer |
|
||||
not exists(SequenceNode outer |
|
||||
outer.getAnElement() = nodeFrom.asCfgNode() and
|
||||
IterableUnpacking::iterableUnpackingTupleFlowStep(_, nodeFrom)
|
||||
) and
|
||||
@@ -273,7 +269,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
||||
IterableUnpacking::iterableUnpackingForReadStep(_, _, seq) and
|
||||
IterableUnpacking::iterableUnpackingConvertingReadStep(seq, _, elem) and
|
||||
IterableUnpacking::iterableUnpackingConvertingStoreStep(elem, _, nodeFrom) and
|
||||
nodeFrom.asCfgNode() instanceof Cfg::SequenceNode
|
||||
nodeFrom.asCfgNode() instanceof SequenceNode
|
||||
)
|
||||
or
|
||||
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, content)
|
||||
@@ -310,15 +306,13 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
||||
//
|
||||
// nodeFrom is `expr`
|
||||
// nodeTo is entry node for `f`
|
||||
exists(
|
||||
SsaImpl::ScopeEntryDefinition e, SsaImpl::SsaSourceVariable var, Cfg::DefinitionNode def
|
||||
|
|
||||
exists(ScopeEntryDefinition e, SsaSourceVariable var, DefinitionNode def |
|
||||
e.getSourceVariable() = var and
|
||||
def.getNode() = var.getVariable().getAStore()
|
||||
var.hasDefiningNode(def)
|
||||
|
|
||||
nodeTo.(DataFlowPublic::ScopeEntryDefinitionNode).getDefinition() = e and
|
||||
nodeFrom.asCfgNode() = def and
|
||||
var.getVariable().getScope().getScope*() = nodeFrom.getScope()
|
||||
var.getScope().getScope*() = nodeFrom.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,6 @@ overlay[local]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
|
||||
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||
private import DataFlowPublic
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||
private import codeql.dataflow.VariableCapture as Shared
|
||||
@@ -17,10 +14,10 @@ private import codeql.dataflow.VariableCapture as Shared
|
||||
// The first is the main implementation, the second is a performance motivated restriction.
|
||||
// The restriction is to clear any `CapturedVariableContent` before writing a new one
|
||||
// to avoid long access paths (see the link for a nice explanation).
|
||||
private module CaptureInput implements Shared::InputSig<Location, CfgImpl::BasicBlock> {
|
||||
private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBlock> {
|
||||
private import python as PY
|
||||
|
||||
additional class ExprCfgNode extends Cfg::ControlFlowNode {
|
||||
additional class ExprCfgNode extends ControlFlowNode {
|
||||
ExprCfgNode() { isExpressionNode(this) }
|
||||
}
|
||||
|
||||
@@ -28,9 +25,7 @@ private module CaptureInput implements Shared::InputSig<Location, CfgImpl::Basic
|
||||
predicate isConstructor() { none() }
|
||||
}
|
||||
|
||||
Callable basicBlockGetEnclosingCallable(CfgImpl::BasicBlock bb) {
|
||||
result = bb.getEnclosingCallable().asScope()
|
||||
}
|
||||
Callable basicBlockGetEnclosingCallable(Cfg::BasicBlock bb) { result = bb.getScope() }
|
||||
|
||||
class CapturedVariable extends LocalVariable {
|
||||
Function f;
|
||||
@@ -56,29 +51,27 @@ private module CaptureInput implements Shared::InputSig<Location, CfgImpl::Basic
|
||||
class CapturedParameter extends CapturedVariable {
|
||||
CapturedParameter() { this.isParameter() }
|
||||
|
||||
Cfg::ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
|
||||
ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
|
||||
}
|
||||
|
||||
class Expr extends ExprCfgNode {
|
||||
predicate hasCfgNode(CfgImpl::BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||
predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||
}
|
||||
|
||||
class VariableWrite extends Cfg::ControlFlowNode {
|
||||
class VariableWrite extends ControlFlowNode {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableWrite() {
|
||||
exists(Cfg::DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue())
|
||||
}
|
||||
VariableWrite() { this = v.getAStore().getAFlowNode().(DefinitionNode).getValue() }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
|
||||
predicate hasCfgNode(CfgImpl::BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||
predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||
}
|
||||
|
||||
class VariableRead extends Expr {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableRead() { this.getNode() = v.getALoad() }
|
||||
VariableRead() { this = v.getALoad().getAFlowNode() }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
}
|
||||
@@ -87,14 +80,9 @@ private module CaptureInput implements Shared::InputSig<Location, CfgImpl::Basic
|
||||
// TODO: Other languages have an extra case here looking like
|
||||
// simpleAstFlowStep(nodeFrom, nodeTo)
|
||||
// we should investigate the potential benefit of adding that.
|
||||
exists(SsaImpl::EssaVariable def |
|
||||
exists(SsaVariable def |
|
||||
def.getAUse() = nodeTo and
|
||||
def.getAnUltimateDefinition()
|
||||
.getDefinition()
|
||||
.(SsaImpl::EssaNodeDefinition)
|
||||
.getDefiningNode()
|
||||
.(Cfg::DefinitionNode)
|
||||
.getValue() = nodeFrom
|
||||
def.getAnUltimateDefinition().getDefinition().(DefinitionNode).getValue() = nodeFrom
|
||||
)
|
||||
}
|
||||
|
||||
@@ -119,7 +107,7 @@ class CapturedVariable = CaptureInput::CapturedVariable;
|
||||
|
||||
class ClosureExpr = CaptureInput::ClosureExpr;
|
||||
|
||||
module Flow = Shared::Flow<Location, Cfg::CfgSigImpl, CaptureInput>;
|
||||
module Flow = Shared::Flow<Location, Cfg, CaptureInput>;
|
||||
|
||||
private Flow::ClosureNode asClosureNode(Node n) {
|
||||
result = n.(SynthCaptureNode).getSynthesizedCaptureNode()
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
@@ -60,7 +59,7 @@ module Bottle {
|
||||
|
||||
override Parameter getARoutedParameter() { none() }
|
||||
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +73,7 @@ module Bottle {
|
||||
/** A response returned by a view callable. */
|
||||
class BottleReturnResponse extends Http::Server::HttpResponse::Range {
|
||||
BottleReturnResponse() {
|
||||
exists(View::ViewCallable vc, Return ret |
|
||||
ret.getScope() = vc and this.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
@@ -157,9 +154,9 @@ module Bottle {
|
||||
DataFlow::Node value;
|
||||
|
||||
HeaderWriteSubscript() {
|
||||
exists(Cfg::SubscriptNode subscript |
|
||||
exists(SubscriptNode subscript |
|
||||
this.asCfgNode() = subscript and
|
||||
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
name.asCfgNode() = subscript.getIndex() and
|
||||
subscript.getObject() = headers().asSource().asCfgNode()
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
@@ -1306,7 +1305,7 @@ module PrivateDjango {
|
||||
dict.(DataFlow::MethodCallNode).calls(files, "dict")
|
||||
)
|
||||
|
|
||||
this.asCfgNode().(Cfg::SubscriptNode).getObject() = dict.asCfgNode()
|
||||
this.asCfgNode().(SubscriptNode).getObject() = dict.asCfgNode()
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(dict, "get")
|
||||
)
|
||||
@@ -1315,7 +1314,7 @@ module PrivateDjango {
|
||||
exists(DataFlow::AttrRead files, DataFlow::MethodCallNode getlistCall |
|
||||
files.accesses(instance(), "FILES") and
|
||||
getlistCall.calls(files, "getlist") and
|
||||
this.asCfgNode().(Cfg::SubscriptNode).getObject() = getlistCall.asCfgNode()
|
||||
this.asCfgNode().(SubscriptNode).getObject() = getlistCall.asCfgNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2217,7 +2216,7 @@ module PrivateDjango {
|
||||
DataFlow::Node value;
|
||||
|
||||
DjangoResponseCookieSubscriptWrite() {
|
||||
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead cookieLookup |
|
||||
exists(SubscriptNode subscript, DataFlow::AttrRead cookieLookup |
|
||||
// To give `this` a value, we need to choose between either LHS or RHS,
|
||||
// and just go with the LHS
|
||||
this.asCfgNode() = subscript
|
||||
@@ -2229,7 +2228,7 @@ module PrivateDjango {
|
||||
|
|
||||
cookieLookup.flowsTo(subscriptObj)
|
||||
) and
|
||||
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
index.asCfgNode() = subscript.getIndex()
|
||||
)
|
||||
}
|
||||
@@ -2250,7 +2249,7 @@ module PrivateDjango {
|
||||
DataFlow::Node value;
|
||||
|
||||
DjangoResponseHeaderSubscriptWrite() {
|
||||
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead headerLookup |
|
||||
exists(SubscriptNode subscript, DataFlow::AttrRead headerLookup |
|
||||
// To give `this` a value, we need to choose between either LHS or RHS,
|
||||
// and just go with the LHS
|
||||
this.asCfgNode() = subscript
|
||||
@@ -2262,7 +2261,7 @@ module PrivateDjango {
|
||||
|
|
||||
headerLookup.flowsTo(subscriptObj)
|
||||
) and
|
||||
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
index.asCfgNode() = subscript.getIndex()
|
||||
)
|
||||
}
|
||||
@@ -2285,14 +2284,14 @@ module PrivateDjango {
|
||||
DataFlow::Node value;
|
||||
|
||||
DjangoResponseSubscriptWrite() {
|
||||
exists(Cfg::SubscriptNode subscript |
|
||||
exists(SubscriptNode subscript |
|
||||
// To give `this` a value, we need to choose between either LHS or RHS,
|
||||
// and just go with the LHS
|
||||
this.asCfgNode() = subscript
|
||||
|
|
||||
subscript.getObject() =
|
||||
DjangoImpl::DjangoHttp::Response::HttpResponse::instance().asCfgNode() and
|
||||
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
index.asCfgNode() = subscript.getIndex()
|
||||
)
|
||||
}
|
||||
@@ -2427,7 +2426,7 @@ module PrivateDjango {
|
||||
/** Gets a reference to the result of calling the `as_view` classmethod of this class. */
|
||||
private DataFlow::TypeTrackingNode asViewResult(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asCfgNode().(Cfg::CallNode).getFunction() = this.asViewRef().asCfgNode()
|
||||
result.asCfgNode().(CallNode).getFunction() = this.asViewRef().asCfgNode()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = this.asViewResult(t2).track(t2, t))
|
||||
}
|
||||
@@ -2873,9 +2872,7 @@ module PrivateDjango {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
DjangoRedirectViewGetRedirectUrlReturn() {
|
||||
exists(GetRedirectUrlFunction f, Return ret |
|
||||
ret.getScope() = f and node.getNode() = ret.getValue()
|
||||
)
|
||||
node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getRedirectLocation() { result = this }
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
@@ -130,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" }
|
||||
|
||||
@@ -310,11 +309,7 @@ module FastApi {
|
||||
FastApiRouteSetup routeSetup;
|
||||
|
||||
FastApiRequestHandlerReturn() {
|
||||
exists(Function h, Return ret |
|
||||
h = routeSetup.getARequestHandler() and
|
||||
ret.getScope() = h and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
node = routeSetup.getARequestHandler().getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
@@ -443,7 +438,7 @@ module FastApi {
|
||||
DataFlow::Node value;
|
||||
|
||||
HeaderSubscriptWrite() {
|
||||
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead headerLookup |
|
||||
exists(SubscriptNode subscript, DataFlow::AttrRead headerLookup |
|
||||
// To give `this` a value, we need to choose between either LHS or RHS,
|
||||
// and just go with the LHS
|
||||
this.asCfgNode() = subscript
|
||||
@@ -452,7 +447,7 @@ module FastApi {
|
||||
exists(DataFlow::Node subscriptObj | subscriptObj.asCfgNode() = subscript.getObject() |
|
||||
headerLookup.flowsTo(subscriptObj)
|
||||
) and
|
||||
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
index.asCfgNode() = subscript.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.ApiGraphs
|
||||
@@ -52,9 +51,9 @@ module Gradio {
|
||||
// limit only to lists of parameters given to `inputs`.
|
||||
(
|
||||
(
|
||||
call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode
|
||||
call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode
|
||||
or
|
||||
call.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
|
||||
call.getParameter(1).asSink().asCfgNode() instanceof ListNode
|
||||
) and
|
||||
(
|
||||
this = call.getKeywordParameter("inputs").getASubscript().getAValueReachingSink()
|
||||
@@ -76,8 +75,8 @@ module Gradio {
|
||||
exists(GradioInput call |
|
||||
this = call.getParameter(0, "fn").getParameter(_).asSource() and
|
||||
// exclude lists of parameters given to `inputs`
|
||||
not call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode and
|
||||
not call.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
|
||||
not call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode and
|
||||
not call.getParameter(1).asSink().asCfgNode() instanceof ListNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -106,16 +105,16 @@ module Gradio {
|
||||
// handle cases where there are multiple arguments passed as a list to `inputs`
|
||||
(
|
||||
(
|
||||
node.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode
|
||||
node.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode
|
||||
or
|
||||
node.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
|
||||
node.getParameter(1).asSink().asCfgNode() instanceof ListNode
|
||||
) and
|
||||
exists(int i | nodeTo = node.getParameter(0, "fn").getParameter(i).asSource() |
|
||||
nodeFrom.asCfgNode() =
|
||||
node.getKeywordParameter("inputs").asSink().asCfgNode().(Cfg::ListNode).getElement(i)
|
||||
node.getKeywordParameter("inputs").asSink().asCfgNode().(ListNode).getElement(i)
|
||||
or
|
||||
nodeFrom.asCfgNode() =
|
||||
node.getParameter(1).asSink().asCfgNode().(Cfg::ListNode).getElement(i)
|
||||
node.getParameter(1).asSink().asCfgNode().(ListNode).getElement(i)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
@@ -47,7 +46,7 @@ module MarkupSafeModel {
|
||||
|
||||
/** A direct instantiation of `markupsafe.Markup`. */
|
||||
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
||||
override Cfg::CallNode node;
|
||||
override CallNode node;
|
||||
|
||||
ClassInstantiation() { this = classRef().getACall() }
|
||||
}
|
||||
@@ -65,7 +64,7 @@ module MarkupSafeModel {
|
||||
|
||||
/** A string concatenation with a `markupsafe.Markup` involved. */
|
||||
class StringConcat extends Markup::InstanceSource, DataFlow::CfgNode {
|
||||
override Cfg::BinaryExprNode node;
|
||||
override BinaryExprNode node;
|
||||
|
||||
StringConcat() {
|
||||
node.getOp() instanceof Add and
|
||||
@@ -80,7 +79,7 @@ module MarkupSafeModel {
|
||||
|
||||
/** A %-style string format with `markupsafe.Markup` as the format string. */
|
||||
class PercentStringFormat extends Markup::InstanceSource, DataFlow::CfgNode {
|
||||
override Cfg::BinaryExprNode node;
|
||||
override BinaryExprNode node;
|
||||
|
||||
PercentStringFormat() {
|
||||
node.getOp() instanceof Mod and
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
@@ -57,7 +56,7 @@ module Pycurl {
|
||||
{
|
||||
OutgoingRequestCall() {
|
||||
this = setopt().getACall() and
|
||||
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = "URL"
|
||||
this.getArg(0).asCfgNode().(AttrNode).getName() = "URL"
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() {
|
||||
@@ -82,7 +81,7 @@ module Pycurl {
|
||||
private class CurlSslCall extends Http::Client::Request::Range instanceof DataFlow::CallCfgNode {
|
||||
CurlSslCall() {
|
||||
this = setopt().getACall() and
|
||||
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"]
|
||||
this.getArg(0).asCfgNode().(AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"]
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() { none() }
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
@@ -94,7 +93,7 @@ module Pydantic {
|
||||
// be a Pydantic model. So `model[0]` will be an overapproximation, but should not
|
||||
// really cause problems (since we don't expect real code to contain such accesses)
|
||||
nodeFrom = instance() and
|
||||
nodeTo.asCfgNode().(Cfg::SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -166,9 +166,7 @@ module Pyramid {
|
||||
/** A response returned by a view callable. */
|
||||
private class PyramidReturnResponse extends Http::Server::HttpResponse::Range {
|
||||
PyramidReturnResponse() {
|
||||
exists(View::ViewCallable vc, Return ret |
|
||||
ret.getScope() = vc and this.asCfgNode().getNode() = ret.getValue()
|
||||
) and
|
||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() and
|
||||
not this = instance()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ overlay[local?]
|
||||
module;
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
@@ -1247,7 +1246,7 @@ module StdlibPrivate {
|
||||
/** An additional taint step for calls to `os.path.join` */
|
||||
private class OsPathJoinCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(Cfg::CallNode call |
|
||||
exists(CallNode call |
|
||||
nodeTo.asCfgNode() = call and
|
||||
call = OS::OsPath::join().getACall().asCfgNode() and
|
||||
call.getAnArg() = nodeFrom.asCfgNode()
|
||||
@@ -1318,13 +1317,13 @@ module StdlibPrivate {
|
||||
// run, so if we're able to, we only mark the first element as the command
|
||||
// (and not the arguments to the command).
|
||||
//
|
||||
result.asCfgNode() = arg_args.asCfgNode().(Cfg::SequenceNode).getElement(0)
|
||||
result.asCfgNode() = arg_args.asCfgNode().(SequenceNode).getElement(0)
|
||||
or
|
||||
// Either the "args" argument is not a sequence (which is valid) or we where
|
||||
// just not able to figure it out. Simply mark the "args" argument as the
|
||||
// command.
|
||||
//
|
||||
not arg_args.asCfgNode() instanceof Cfg::SequenceNode and
|
||||
not arg_args.asCfgNode() instanceof SequenceNode and
|
||||
result = arg_args
|
||||
)
|
||||
)
|
||||
@@ -1543,7 +1542,7 @@ module StdlibPrivate {
|
||||
* See https://docs.python.org/3/library/functions.html#eval
|
||||
*/
|
||||
private class BuiltinsEvalCall extends CodeExecution::Range, DataFlow::CallCfgNode {
|
||||
override Cfg::CallNode node;
|
||||
override CallNode node;
|
||||
|
||||
BuiltinsEvalCall() { this = API::builtin("eval").getACall() }
|
||||
|
||||
@@ -1924,7 +1923,7 @@ module StdlibPrivate {
|
||||
nodeFrom = instance().getAValueReachableFromSource() and
|
||||
nodeTo = [getvalueRef(), getfirstRef(), getlistRef()].getAValueReachableFromSource()
|
||||
or
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::CallNode).getFunction() and
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(CallNode).getFunction() and
|
||||
(
|
||||
nodeFrom = getvalueRef().getAValueReachableFromSource() and
|
||||
nodeTo = getvalueResult().asSource()
|
||||
@@ -1940,7 +1939,7 @@ module StdlibPrivate {
|
||||
nodeFrom in [
|
||||
instance().getAValueReachableFromSource(), fieldList().getAValueReachableFromSource()
|
||||
] and
|
||||
nodeTo.asCfgNode().(Cfg::SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||
or
|
||||
// Attributes on Field
|
||||
nodeFrom = field().getAValueReachableFromSource() and
|
||||
@@ -2255,8 +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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2338,9 +2337,9 @@ module StdlibPrivate {
|
||||
DataFlow::Node value;
|
||||
|
||||
HeaderWriteSubscript() {
|
||||
exists(Cfg::SubscriptNode subscript |
|
||||
exists(SubscriptNode subscript |
|
||||
this.asCfgNode() = subscript and
|
||||
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
name.asCfgNode() = subscript.getIndex() and
|
||||
subscript.getObject() = instance().asCfgNode()
|
||||
)
|
||||
@@ -2682,7 +2681,7 @@ module StdlibPrivate {
|
||||
or
|
||||
// Data injection
|
||||
// Special handling of the `/` operator
|
||||
exists(Cfg::BinaryExprNode slash, DataFlow::Node pathOperand, DataFlow::TypeTracker t2 |
|
||||
exists(BinaryExprNode slash, DataFlow::Node pathOperand, DataFlow::TypeTracker t2 |
|
||||
slash.getOp() instanceof Div and
|
||||
pathOperand.asCfgNode() = slash.getAnOperand() and
|
||||
pathlibPath(t2).flowsTo(pathOperand) and
|
||||
@@ -2807,7 +2806,7 @@ module StdlibPrivate {
|
||||
pathlibPath().flowsTo(nodeTo) and
|
||||
(
|
||||
// Special handling of the `/` operator
|
||||
exists(Cfg::BinaryExprNode slash, DataFlow::Node pathOperand |
|
||||
exists(BinaryExprNode slash, DataFlow::Node pathOperand |
|
||||
slash.getOp() instanceof Div and
|
||||
pathOperand.asCfgNode() = slash.getAnOperand() and
|
||||
pathlibPath().flowsTo(pathOperand)
|
||||
@@ -4606,9 +4605,9 @@ module StdlibPrivate {
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
exists(Cfg::CallNode c, string name, Cfg::ControlFlowNode n, DataFlow::AttributeContent ac |
|
||||
c.getFunction().(Cfg::NameNode).getId() = "replace" or
|
||||
c.getFunction().(Cfg::AttrNode).getName() = "replace"
|
||||
exists(CallNode c, string name, ControlFlowNode n, DataFlow::AttributeContent ac |
|
||||
c.getFunction().(NameNode).getId() = "replace" or
|
||||
c.getFunction().(AttrNode).getName() = "replace"
|
||||
|
|
||||
n = c.getArgByName(name) and
|
||||
ac.getAttribute() = name and
|
||||
@@ -5152,10 +5151,10 @@ module StdlibPrivate {
|
||||
* See https://docs.python.org/3.9/library/stdtypes.html#str.startswith
|
||||
*/
|
||||
private class StartswithCall extends Path::SafeAccessCheck::Range {
|
||||
StartswithCall() { this.(Cfg::CallNode).getFunction().(Cfg::AttrNode).getName() = "startswith" }
|
||||
StartswithCall() { this.(CallNode).getFunction().(AttrNode).getName() = "startswith" }
|
||||
|
||||
override predicate checks(Cfg::ControlFlowNode node, boolean branch) {
|
||||
node = this.(Cfg::CallNode).getFunction().(Cfg::AttrNode).getObject() and
|
||||
override predicate checks(ControlFlowNode node, boolean branch) {
|
||||
node = this.(CallNode).getFunction().(AttrNode).getObject() and
|
||||
branch = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.security.dataflow.UrlRedirectCustomizations
|
||||
@@ -92,7 +91,7 @@ private module Urllib {
|
||||
* A read of the `netloc` attribute of a parsed URL as returned by `urllib.parse.urlparse`,
|
||||
* which is being checked in a way that is relevant for URL redirection vulnerabilities.
|
||||
*/
|
||||
private predicate netlocCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
|
||||
private predicate netlocCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
exists(DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead netlocRead |
|
||||
urlParseCall = getUrlParseCall() and
|
||||
netlocRead = urlParseCall.getAnAttributeRead("netloc") and
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
@@ -73,9 +72,9 @@ module Tornado {
|
||||
DataFlow::Node value;
|
||||
|
||||
TornadoHeaderSubscriptWrite() {
|
||||
exists(Cfg::SubscriptNode subscript |
|
||||
exists(SubscriptNode subscript |
|
||||
subscript.getObject() = instance().asCfgNode() and
|
||||
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
index.asCfgNode() = subscript.getIndex() and
|
||||
this.asCfgNode() = subscript
|
||||
)
|
||||
@@ -423,7 +422,7 @@ module Tornado {
|
||||
// be able to do something more structured for providing modeling of the members
|
||||
// of a container-object.
|
||||
exists(DataFlow::AttrRead files | files.accesses(instance(), "cookies") |
|
||||
this.asCfgNode().(Cfg::SubscriptNode).getObject() = files.asCfgNode()
|
||||
this.asCfgNode().(SubscriptNode).getObject() = files.asCfgNode()
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(files, "get")
|
||||
)
|
||||
@@ -480,20 +479,20 @@ module Tornado {
|
||||
// routing
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a sequence that defines a number of route rules */
|
||||
Cfg::SequenceNode routeSetupRuleList() {
|
||||
exists(Cfg::CallNode call |
|
||||
SequenceNode routeSetupRuleList() {
|
||||
exists(CallNode call |
|
||||
call = any(TornadoModule::Web::Application::ClassInstantiation c).asCfgNode()
|
||||
|
|
||||
result in [call.getArg(0), call.getArgByName("handlers")]
|
||||
)
|
||||
or
|
||||
exists(Cfg::CallNode call |
|
||||
exists(CallNode call |
|
||||
call.getFunction() = TornadoModule::Web::Application::add_handlers().asCfgNode()
|
||||
|
|
||||
result in [call.getArg(1), call.getArgByName("host_handlers")]
|
||||
)
|
||||
or
|
||||
result = routeSetupRuleList().getElement(_).(Cfg::TupleNode).getElement(1)
|
||||
result = routeSetupRuleList().getElement(_).(TupleNode).getElement(1)
|
||||
}
|
||||
|
||||
/** A tornado route setup. */
|
||||
@@ -516,12 +515,12 @@ module Tornado {
|
||||
|
||||
/** A route setup using a tuple. */
|
||||
private class TornadoTupleRouteSetup extends TornadoRouteSetup, DataFlow::CfgNode {
|
||||
override Cfg::TupleNode node;
|
||||
override TupleNode node;
|
||||
|
||||
TornadoTupleRouteSetup() {
|
||||
node = routeSetupRuleList().getElement(_) and
|
||||
count(node.getElement(_)) = 2 and
|
||||
not node.getElement(1) instanceof Cfg::SequenceNode
|
||||
not node.getElement(1) instanceof SequenceNode
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrlPatternArg() { result.asCfgNode() = node.getElement(0) }
|
||||
|
||||
@@ -182,9 +182,7 @@ private module Twisted {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
TwistedResourceRenderMethodReturn() {
|
||||
exists(TwistedResourceRenderMethod meth, Return ret |
|
||||
ret.getScope() = meth and this.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.ApiGraphs
|
||||
@@ -222,9 +221,9 @@ module Werkzeug {
|
||||
DataFlow::Node value;
|
||||
|
||||
HeaderWriteSubscript() {
|
||||
exists(Cfg::SubscriptNode subscript |
|
||||
exists(SubscriptNode subscript |
|
||||
this.asCfgNode() = subscript and
|
||||
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
name.asCfgNode() = subscript.getIndex() and
|
||||
subscript.getObject() = instance().asCfgNode()
|
||||
)
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
@@ -29,7 +28,7 @@ private module Yaml {
|
||||
* See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
|
||||
*/
|
||||
private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
|
||||
override Cfg::CallNode node;
|
||||
override CallNode node;
|
||||
string func_name;
|
||||
|
||||
YamlLoadCall() {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
@@ -112,7 +111,7 @@ module Yarl {
|
||||
}
|
||||
|
||||
private predicate yarlUrlIsAbsoluteCall(
|
||||
DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch
|
||||
DataFlow::GuardNode g, ControlFlowNode node, boolean branch
|
||||
) {
|
||||
exists(ClassInstantiation instance, DataFlow::MethodCallNode call |
|
||||
call.calls(instance, "is_absolute") and
|
||||
|
||||
@@ -11,7 +11,6 @@ private import semmle.python.dataflow.new.internal.ImportResolution
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.filters.Tests
|
||||
private import semmle.python.Module
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
|
||||
// very much inspired by the draft at https://github.com/github/codeql/pull/5632
|
||||
module NotExposed {
|
||||
@@ -207,7 +206,7 @@ module NotExposed {
|
||||
string relevantName, Location loc
|
||||
) {
|
||||
loc = mod.getLocation() and
|
||||
exists(API::Node relevantClass, Cfg::ControlFlowNode value |
|
||||
exists(API::Node relevantClass, ControlFlowNode value |
|
||||
relevantClass = newOrExistingModeling(spec).getASubclass*() and
|
||||
ImportResolution::module_export(mod, relevantName, def) and
|
||||
value = relevantClass.getAValueReachableFromSource().asCfgNode() and
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -161,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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts as Concepts
|
||||
private import semmle.python.regex
|
||||
@@ -79,7 +78,7 @@ private module FindRegexMode {
|
||||
t.start() and
|
||||
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
|
||||
or
|
||||
exists(Cfg::BinaryExprNode binop, DataFlow::Node operand |
|
||||
exists(BinaryExprNode binop, DataFlow::Node operand |
|
||||
operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
|
||||
operand.asCfgNode() = binop.getAnOperand() and
|
||||
(binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -30,7 +29,7 @@ private class TracebackFunctionCall extends ExceptionInfo, DataFlow::CallCfgNode
|
||||
private class CaughtException extends ExceptionInfo {
|
||||
CaughtException() {
|
||||
this.asExpr() = any(ExceptStmt s).getName() and
|
||||
this.asCfgNode().(Cfg::NameNode).defines(_)
|
||||
this.asCfgNode() = any(EssaNodeDefinition def).getDefiningNode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.data.internal.ApiGraphModels
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
@@ -96,7 +95,7 @@ module ServerSideRequestForgery {
|
||||
class StringConstructionAsFullUrlControlSanitizer extends FullUrlControlSanitizer {
|
||||
StringConstructionAsFullUrlControlSanitizer() {
|
||||
// string concat
|
||||
exists(Cfg::BinaryExprNode add |
|
||||
exists(BinaryExprNode add |
|
||||
add.getOp() instanceof Add and
|
||||
add.getRight() = this.asCfgNode() and
|
||||
not add.getLeft().getNode().(StringLiteral).getText().toLowerCase() in [
|
||||
@@ -105,7 +104,7 @@ module ServerSideRequestForgery {
|
||||
)
|
||||
or
|
||||
// % formatting
|
||||
exists(Cfg::BinaryExprNode fmt |
|
||||
exists(BinaryExprNode fmt |
|
||||
fmt.getOp() instanceof Mod and
|
||||
fmt.getRight() = this.asCfgNode() and
|
||||
// detecting %-formatting is not super easy, so we simplify it to only handle
|
||||
@@ -156,9 +155,7 @@ module ServerSideRequestForgery {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate stringRestriction(
|
||||
DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch
|
||||
) {
|
||||
private predicate stringRestriction(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
exists(DataFlow::MethodCallNode call, DataFlow::Node strNode |
|
||||
call.asCfgNode() = g and strNode.asCfgNode() = node
|
||||
|
|
||||
|
||||
@@ -9,7 +9,6 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
@@ -140,8 +139,8 @@ module TarSlip {
|
||||
* where `<check_path>` is any function matching `"%path"`.
|
||||
* `info` is assumed to be a `TarInfo` instance.
|
||||
*/
|
||||
predicate tarFileInfoSanitizer(DataFlow::GuardNode g, Cfg::ControlFlowNode tarInfo, boolean branch) {
|
||||
exists(Cfg::CallNode call, Cfg::AttrNode attr |
|
||||
predicate tarFileInfoSanitizer(DataFlow::GuardNode g, ControlFlowNode tarInfo, boolean branch) {
|
||||
exists(CallNode call, AttrNode attr |
|
||||
g = call and
|
||||
// We must test the name of the tar info object.
|
||||
attr = call.getAnArg() and
|
||||
@@ -149,9 +148,9 @@ module TarSlip {
|
||||
attr.getObject() = tarInfo
|
||||
|
|
||||
// The assumption that any test that matches %path is a sanitizer might be too broad.
|
||||
call.getAChild*().(Cfg::AttrNode).getName().matches("%path")
|
||||
call.getAChild*().(AttrNode).getName().matches("%path")
|
||||
or
|
||||
call.getAChild*().(Cfg::NameNode).getId().matches("%path")
|
||||
call.getAChild*().(NameNode).getId().matches("%path")
|
||||
) and
|
||||
branch = false
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
@@ -112,7 +111,7 @@ module UrlRedirect {
|
||||
// Url redirection is a problem only if the user controls the prefix of the URL.
|
||||
// TODO: This is a copy of the taint-sanitizer from the old points-to query, which doesn't
|
||||
// cover formatting.
|
||||
exists(Cfg::BinaryExprNode string_concat | string_concat.getOp() instanceof Add |
|
||||
exists(BinaryExprNode string_concat | string_concat.getOp() instanceof Add |
|
||||
string_concat.getRight() = this.asCfgNode()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user