mirror of
https://github.com/github/codeql.git
synced 2026-06-06 14:07:06 +02:00
Compare commits
33 Commits
yoff/pytho
...
yoff/pytho
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b783ed69c5 | ||
|
|
5b9803e03c | ||
|
|
b2ff09f70a | ||
|
|
4aee0b3c87 | ||
|
|
a2295e7216 | ||
|
|
0623acc7f5 | ||
|
|
dc5aa8e0f5 | ||
|
|
db1e5035b4 | ||
|
|
7a3f546587 | ||
|
|
821325b7e5 | ||
|
|
4d2296d4f0 | ||
|
|
aaa3b363e1 | ||
|
|
9dbe9adb00 | ||
|
|
703cea2b65 | ||
|
|
e6e8e3d005 | ||
|
|
adc9b7714b | ||
|
|
e706c5f444 | ||
|
|
8a1e6d4f64 | ||
|
|
1a747dd8be | ||
|
|
28bb1a6870 | ||
|
|
45b1253b23 | ||
|
|
c99dab1d71 | ||
|
|
f3e3647209 | ||
|
|
8d099cbe38 | ||
|
|
9618e9b35c | ||
|
|
d2f474d998 | ||
|
|
caae5a8bf1 | ||
|
|
09371339d7 | ||
|
|
7718fe40a0 | ||
|
|
aeb82858d7 | ||
|
|
c08cf81665 | ||
|
|
e06158629e | ||
|
|
3e09961662 |
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* More logging functions are now recognized as not returning or panicking.
|
||||||
@@ -413,17 +413,13 @@ private class ExternalLoggerCall extends LoggerCall::Range, DataFlow::CallNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private class HeuristicLoggerFunction extends Method {
|
||||||
* A call to an interface that looks like a logger. It is common to use a
|
string logFunctionPrefix;
|
||||||
* locally-defined interface for logging to make it easy to changing logging
|
|
||||||
* library.
|
HeuristicLoggerFunction() {
|
||||||
*/
|
exists(string tp, string name |
|
||||||
private class HeuristicLoggerCall extends LoggerCall::Range, DataFlow::CallNode {
|
this.hasQualifiedName(_, tp, name) and
|
||||||
HeuristicLoggerCall() {
|
this.getReceiverBaseType().getUnderlyingType() instanceof InterfaceType
|
||||||
exists(Method m, string tp, string logFunctionPrefix, string name |
|
|
||||||
m = this.getTarget() and
|
|
||||||
m.hasQualifiedName(_, tp, name) and
|
|
||||||
m.getReceiverBaseType().getUnderlyingType() instanceof InterfaceType
|
|
||||||
|
|
|
|
||||||
tp.regexpMatch(".*[lL]ogger") and
|
tp.regexpMatch(".*[lL]ogger") and
|
||||||
logFunctionPrefix =
|
logFunctionPrefix =
|
||||||
@@ -435,6 +431,19 @@ private class HeuristicLoggerCall extends LoggerCall::Range, DataFlow::CallNode
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate mayReturnNormally() { logFunctionPrefix != "Fatal" }
|
||||||
|
|
||||||
|
override predicate mustPanic() { logFunctionPrefix = "Panic" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to an interface that looks like a logger. It is common to use a
|
||||||
|
* locally-defined interface for logging to make it easy to change logging
|
||||||
|
* library.
|
||||||
|
*/
|
||||||
|
private class HeuristicLoggerCall extends LoggerCall::Range, DataFlow::CallNode {
|
||||||
|
HeuristicLoggerCall() { this.getTarget() instanceof HeuristicLoggerFunction }
|
||||||
|
|
||||||
override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
|
override DataFlow::Node getAMessageComponent() { result = this.getASyntacticArgument() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,17 +12,37 @@ import go
|
|||||||
* forks.
|
* forks.
|
||||||
*/
|
*/
|
||||||
module Glog {
|
module Glog {
|
||||||
|
/** Gets a package name for `glog` or `klog` (which is a fork). */
|
||||||
|
string packagePath() {
|
||||||
|
result =
|
||||||
|
package([
|
||||||
|
"github.com/golang/glog", "gopkg.in/glog", "k8s.io/klog", "github.com/barakmich/glog"
|
||||||
|
], "")
|
||||||
|
}
|
||||||
|
|
||||||
private class GlogFunction extends Function {
|
private class GlogFunction extends Function {
|
||||||
int firstPrintedArg;
|
int firstPrintedArg;
|
||||||
|
string format;
|
||||||
|
string level;
|
||||||
|
|
||||||
GlogFunction() {
|
GlogFunction() {
|
||||||
exists(string pkg, string fn, string level |
|
exists(string pkg, string context, int nContextArgs, string depth, int nDepthArgs, string fn |
|
||||||
pkg = package(["github.com/golang/glog", "gopkg.in/glog", "k8s.io/klog"], "") and
|
pkg = packagePath() and
|
||||||
level = ["Error", "Exit", "Fatal", "Info", "Warning"] and
|
level = ["Error", "Exit", "Fatal", "Info", "Warning"] and
|
||||||
(
|
(
|
||||||
fn = level + ["", "f", "ln"] and firstPrintedArg = 0
|
context = "" and nContextArgs = 0
|
||||||
or
|
or
|
||||||
fn = level + "Depth" and firstPrintedArg = 1
|
context = "Context" and nContextArgs = 1
|
||||||
|
) and
|
||||||
|
(
|
||||||
|
depth = "" and nDepthArgs = 0
|
||||||
|
or
|
||||||
|
depth = "Depth" and nDepthArgs = 1
|
||||||
|
) and
|
||||||
|
format = ["", "f", "ln"] and
|
||||||
|
(
|
||||||
|
fn = level + context + depth + format and
|
||||||
|
firstPrintedArg = nContextArgs + nDepthArgs
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
this.hasQualifiedName(pkg, fn)
|
this.hasQualifiedName(pkg, fn)
|
||||||
@@ -35,10 +55,15 @@ module Glog {
|
|||||||
* Gets the index of the first argument that may be output, including a format string if one is present.
|
* Gets the index of the first argument that may be output, including a format string if one is present.
|
||||||
*/
|
*/
|
||||||
int getFirstPrintedArg() { result = firstPrintedArg }
|
int getFirstPrintedArg() { result = firstPrintedArg }
|
||||||
|
|
||||||
|
/** Holds if this function takes a format string. */
|
||||||
|
predicate formatter() { format = "f" }
|
||||||
|
|
||||||
|
override predicate mayReturnNormally() { level != "Fatal" and level != "Exit" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StringFormatter extends StringOps::Formatting::Range instanceof GlogFunction {
|
private class StringFormatter extends StringOps::Formatting::Range instanceof GlogFunction {
|
||||||
StringFormatter() { this.getName().matches("%f") }
|
StringFormatter() { this.formatter() }
|
||||||
|
|
||||||
override int getFormatStringIndex() { result = super.getFirstPrintedArg() }
|
override int getFormatStringIndex() { result = super.getFirstPrintedArg() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ module Logrus {
|
|||||||
this.(Method).hasQualifiedName(packagePath(), ["Entry", "Logger"], name)
|
this.(Method).hasQualifiedName(packagePath(), ["Entry", "Logger"], name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate mayReturnNormally() {
|
||||||
|
not exists(string level, string suffix | level = ["Fatal", "Panic"] |
|
||||||
|
this.getName() = level + suffix
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StringFormatters extends StringOps::Formatting::Range instanceof LogFunction {
|
private class StringFormatters extends StringOps::Formatting::Range instanceof LogFunction {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ module Zap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A Zap logging function which always panics. */
|
/** A Zap logging function which always panics. */
|
||||||
private class FatalLogMethod extends Method {
|
private class FatalLogMethod extends ZapFunction {
|
||||||
FatalLogMethod() {
|
FatalLogMethod() {
|
||||||
this.hasQualifiedName(packagePath(), "Logger", "Fatal")
|
this.hasQualifiedName(packagePath(), "Logger", "Fatal")
|
||||||
or
|
or
|
||||||
@@ -58,7 +58,7 @@ module Zap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A Zap logging function which always panics. */
|
/** A Zap logging function which always panics. */
|
||||||
private class MustPanicLogMethod extends Method {
|
private class MustPanicLogMethod extends ZapFunction {
|
||||||
MustPanicLogMethod() {
|
MustPanicLogMethod() {
|
||||||
this.hasQualifiedName(packagePath(), "Logger", "Panic")
|
this.hasQualifiedName(packagePath(), "Logger", "Panic")
|
||||||
or
|
or
|
||||||
|
|||||||
@@ -29,18 +29,37 @@ module Log {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class LogFormatter extends StringOps::Formatting::Range instanceof LogFunction {
|
private class LogFormatter extends StringOps::Formatting::Range instanceof LogFunction {
|
||||||
LogFormatter() { this.getName() = ["Fatalf", "Panicf", "Printf"] }
|
LogFormatter() { this.getName() = ["Fatalf", "Panicf", "Printf", "Panic", "Panicf", "Panicln"] }
|
||||||
|
|
||||||
override int getFormatStringIndex() { result = 0 }
|
override int getFormatStringIndex() { result = 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A fatal log function, which calls `os.Exit`. */
|
/** A fatal log function, which calls `os.Exit`. */
|
||||||
private class FatalLogFunction extends Function {
|
private class FatalLogFunction extends Function {
|
||||||
FatalLogFunction() { this.hasQualifiedName("log", ["Fatal", "Fatalf", "Fatalln"]) }
|
FatalLogFunction() {
|
||||||
|
exists(string fn | fn = ["Fatal", "Fatalf", "Fatalln"] |
|
||||||
|
this.hasQualifiedName("log", fn)
|
||||||
|
or
|
||||||
|
this.(Method).hasQualifiedName("log", "Logger", fn)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate mayReturnNormally() { none() }
|
override predicate mayReturnNormally() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A log function which must panic. */
|
||||||
|
private class PanicLogFunction extends Function {
|
||||||
|
PanicLogFunction() {
|
||||||
|
exists(string fn | fn = ["Panic", "Panicf", "Panicln"] |
|
||||||
|
this.hasQualifiedName("log", fn)
|
||||||
|
or
|
||||||
|
this.(Method).hasQualifiedName("log", "Logger", fn)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate mustPanic() { any() }
|
||||||
|
}
|
||||||
|
|
||||||
// These models are not implemented using Models-as-Data because they represent reverse flow.
|
// These models are not implemented using Models-as-Data because they represent reverse flow.
|
||||||
private class FunctionModels extends TaintTracking::FunctionModel {
|
private class FunctionModels extends TaintTracking::FunctionModel {
|
||||||
FunctionInput inp;
|
FunctionInput inp;
|
||||||
@@ -63,30 +82,6 @@ module Log {
|
|||||||
FunctionOutput outp;
|
FunctionOutput outp;
|
||||||
|
|
||||||
MethodModels() {
|
MethodModels() {
|
||||||
// signature: func (*Logger) Fatal(v ...interface{})
|
|
||||||
this.hasQualifiedName("log", "Logger", "Fatal") and
|
|
||||||
(inp.isParameter(_) and outp.isReceiver())
|
|
||||||
or
|
|
||||||
// signature: func (*Logger) Fatalf(format string, v ...interface{})
|
|
||||||
this.hasQualifiedName("log", "Logger", "Fatalf") and
|
|
||||||
(inp.isParameter(_) and outp.isReceiver())
|
|
||||||
or
|
|
||||||
// signature: func (*Logger) Fatalln(v ...interface{})
|
|
||||||
this.hasQualifiedName("log", "Logger", "Fatalln") and
|
|
||||||
(inp.isParameter(_) and outp.isReceiver())
|
|
||||||
or
|
|
||||||
// signature: func (*Logger) Panic(v ...interface{})
|
|
||||||
this.hasQualifiedName("log", "Logger", "Panic") and
|
|
||||||
(inp.isParameter(_) and outp.isReceiver())
|
|
||||||
or
|
|
||||||
// signature: func (*Logger) Panicf(format string, v ...interface{})
|
|
||||||
this.hasQualifiedName("log", "Logger", "Panicf") and
|
|
||||||
(inp.isParameter(_) and outp.isReceiver())
|
|
||||||
or
|
|
||||||
// signature: func (*Logger) Panicln(v ...interface{})
|
|
||||||
this.hasQualifiedName("log", "Logger", "Panicln") and
|
|
||||||
(inp.isParameter(_) and outp.isReceiver())
|
|
||||||
or
|
|
||||||
// signature: func (*Logger) Print(v ...interface{})
|
// signature: func (*Logger) Print(v ...interface{})
|
||||||
this.hasQualifiedName("log", "Logger", "Print") and
|
this.hasQualifiedName("log", "Logger", "Print") and
|
||||||
(inp.isParameter(_) and outp.isReceiver())
|
(inp.isParameter(_) and outp.isReceiver())
|
||||||
|
|||||||
@@ -1,54 +1,181 @@
|
|||||||
//go:generate depstubber -vendor github.com/golang/glog "" Error,ErrorDepth,Errorf,Errorln,Exit,ExitDepth,Exitf,Exitln,Fatal,FatalDepth,Fatalf,Fatalln,Info,InfoDepth,Infof,Infoln,Warning,WarningDepth,Warningf,Warningln
|
//go:generate depstubber -vendor github.com/golang/glog Level,Verbose Error,ErrorContext,ErrorContextDepth,ErrorContextDepthf,ErrorContextf,ErrorDepth,ErrorDepthf,Errorf,Errorln,Exit,ExitContext,ExitContextDepth,ExitContextDepthf,ExitContextf,ExitDepth,ExitDepthf,Exitf,Exitln,Fatal,FatalContext,FatalContextDepth,FatalContextDepthf,FatalContextf,FatalDepth,FatalDepthf,Fatalf,Fatalln,Info,InfoContext,InfoContextDepth,InfoContextDepthf,InfoContextf,InfoDepth,InfoDepthf,Infof,Infoln,V,VDepth,Warning,WarningContext,WarningContextDepth,WarningContextDepthf,WarningContextf,WarningDepth,WarningDepthf,Warningf,Warningln
|
||||||
//go:generate depstubber -vendor k8s.io/klog "" Error,ErrorDepth,Errorf,Errorln,Exit,ExitDepth,Exitf,Exitln,Fatal,FatalDepth,Fatalf,Fatalln,Info,InfoDepth,Infof,Infoln,Warning,WarningDepth,Warningf,Warningln
|
//go:generate depstubber -vendor k8s.io/klog Level,Verbose Error,ErrorDepth,Errorf,Errorln,Exit,ExitDepth,Exitf,Exitln,Fatal,FatalDepth,Fatalf,Fatalln,Info,InfoDepth,Infof,Infoln,V,Warning,WarningDepth,Warningf,Warningln
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func glogTest() {
|
func glogTest(selector int) {
|
||||||
glog.Error(text) // $ logger=text
|
ctx := context.Background()
|
||||||
glog.ErrorDepth(0, text) // $ logger=text
|
|
||||||
glog.Errorf(fmt, text) // $ logger=fmt logger=text
|
glog.Error(text) // $ logger=text
|
||||||
glog.Errorln(text) // $ logger=text
|
glog.ErrorContext(ctx, text) // $ logger=text
|
||||||
glog.Exit(text) // $ logger=text
|
glog.ErrorContextDepth(ctx, 0, text) // $ logger=text
|
||||||
glog.ExitDepth(0, text) // $ logger=text
|
glog.ErrorContextDepthf(ctx, 0, fmt, text) // $ logger=fmt logger=text
|
||||||
glog.Exitf(fmt, text) // $ logger=fmt logger=text
|
glog.ErrorContextf(ctx, fmt, text) // $ logger=fmt logger=text
|
||||||
glog.Exitln(text) // $ logger=text
|
glog.ErrorDepth(0, text) // $ logger=text
|
||||||
glog.Fatal(text) // $ logger=text
|
glog.ErrorDepthf(0, fmt, text) // $ logger=fmt logger=text
|
||||||
glog.FatalDepth(0, text) // $ logger=text
|
glog.Errorf(fmt, text) // $ logger=fmt logger=text
|
||||||
glog.Fatalf(fmt, text) // $ logger=fmt logger=text
|
glog.Errorln(text) // $ logger=text
|
||||||
glog.Fatalln(text) // $ logger=text
|
if selector == 1 {
|
||||||
glog.Info(text) // $ logger=text
|
glog.Exit(text) // $ logger=text
|
||||||
glog.InfoDepth(0, text) // $ logger=text
|
}
|
||||||
glog.Infof(fmt, text) // $ logger=fmt logger=text
|
if selector == 2 {
|
||||||
glog.Infoln(text) // $ logger=text
|
glog.ExitContext(ctx, text) // $ logger=text
|
||||||
glog.Warning(text) // $ logger=text
|
}
|
||||||
glog.WarningDepth(0, text) // $ logger=text
|
if selector == 3 {
|
||||||
glog.Warningf(fmt, text) // $ logger=fmt logger=text
|
glog.ExitContextDepth(ctx, 0, text) // $ logger=text
|
||||||
glog.Warningln(text) // $ logger=text
|
}
|
||||||
|
if selector == 4 {
|
||||||
|
glog.ExitContextDepthf(ctx, 0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 5 {
|
||||||
|
glog.ExitContextf(ctx, fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 6 {
|
||||||
|
glog.ExitDepth(0, text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 7 {
|
||||||
|
glog.ExitDepthf(0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 8 {
|
||||||
|
glog.Exitf(fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 9 {
|
||||||
|
glog.Exitln(text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 10 {
|
||||||
|
glog.Fatal(text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 11 {
|
||||||
|
glog.FatalContext(ctx, text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 12 {
|
||||||
|
glog.FatalContextDepth(ctx, 0, text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 13 {
|
||||||
|
glog.FatalContextDepthf(ctx, 0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 14 {
|
||||||
|
glog.FatalContextf(ctx, fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 15 {
|
||||||
|
glog.FatalDepth(0, text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 16 {
|
||||||
|
glog.FatalDepthf(0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 17 {
|
||||||
|
glog.Fatalf(fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 18 {
|
||||||
|
glog.Fatalln(text) // $ logger=text
|
||||||
|
}
|
||||||
|
glog.Info(text) // $ logger=text
|
||||||
|
glog.InfoContext(ctx, text) // $ logger=text
|
||||||
|
glog.InfoContextDepth(ctx, 0, text) // $ logger=text
|
||||||
|
glog.InfoContextDepthf(ctx, 0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.InfoContextf(ctx, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.InfoDepth(0, text) // $ logger=text
|
||||||
|
glog.InfoDepthf(0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.Infof(fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.Infoln(text) // $ logger=text
|
||||||
|
glog.Warning(text) // $ logger=text
|
||||||
|
glog.WarningContext(ctx, text) // $ logger=text
|
||||||
|
glog.WarningContextDepth(ctx, 0, text) // $ logger=text
|
||||||
|
glog.WarningContextDepthf(ctx, 0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.WarningContextf(ctx, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.WarningDepth(0, text) // $ logger=text
|
||||||
|
glog.WarningDepthf(0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.Warningf(fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.Warningln(text) // $ logger=text
|
||||||
|
|
||||||
|
glog.V(0).Info(text) // $ logger=text
|
||||||
|
glog.V(0).InfoContext(ctx, text) // $ logger=text
|
||||||
|
glog.V(0).InfoContextDepth(ctx, 0, text) // $ logger=text
|
||||||
|
glog.V(0).InfoContextDepthf(ctx, 0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.V(0).InfoContextf(ctx, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.V(0).InfoDepth(0, text) // $ logger=text
|
||||||
|
glog.V(0).InfoDepthf(0, fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.V(0).Infof(fmt, text) // $ logger=fmt logger=text
|
||||||
|
glog.V(0).Infoln(text) // $ logger=text
|
||||||
|
glog.VDepth(0, 0).Info(text) // $ logger=text
|
||||||
|
|
||||||
// components corresponding to the format specifier "%T" are not considered vulnerable
|
// components corresponding to the format specifier "%T" are not considered vulnerable
|
||||||
glog.Errorf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
glog.ErrorContextDepthf(ctx, 0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
glog.Exitf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
glog.ErrorContextf(ctx, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
glog.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
glog.ErrorDepthf(0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
glog.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
glog.Errorf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
glog.Warningf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
if selector == 19 {
|
||||||
|
glog.ExitContextDepthf(ctx, 0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
if selector == 20 {
|
||||||
|
glog.ExitContextf(ctx, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
if selector == 21 {
|
||||||
|
glog.ExitDepthf(0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
if selector == 22 {
|
||||||
|
glog.Exitf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
if selector == 23 {
|
||||||
|
glog.FatalContextDepthf(ctx, 0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
if selector == 24 {
|
||||||
|
glog.FatalContextf(ctx, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
if selector == 25 {
|
||||||
|
glog.FatalDepthf(0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
if selector == 26 {
|
||||||
|
glog.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
glog.InfoContextDepthf(ctx, 0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.InfoContextf(ctx, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.InfoDepthf(0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.WarningContextDepthf(ctx, 0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.WarningContextf(ctx, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.WarningDepthf(0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.Warningf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.V(0).InfoContextDepthf(ctx, 0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.V(0).InfoContextf(ctx, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.V(0).InfoDepthf(0, "%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
glog.V(0).Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
|
||||||
klog.Error(text) // $ logger=text
|
klog.Error(text) // $ logger=text
|
||||||
klog.ErrorDepth(0, text) // $ logger=text
|
klog.ErrorDepth(0, text) // $ logger=text
|
||||||
klog.Errorf(fmt, text) // $ logger=fmt logger=text
|
klog.Errorf(fmt, text) // $ logger=fmt logger=text
|
||||||
klog.Errorln(text) // $ logger=text
|
klog.Errorln(text) // $ logger=text
|
||||||
klog.Exit(text) // $ logger=text
|
if selector == 27 {
|
||||||
klog.ExitDepth(0, text) // $ logger=text
|
klog.Exit(text) // $ logger=text
|
||||||
klog.Exitf(fmt, text) // $ logger=fmt logger=text
|
}
|
||||||
klog.Exitln(text) // $ logger=text
|
if selector == 28 {
|
||||||
klog.Fatal(text) // $ logger=text
|
klog.ExitDepth(0, text) // $ logger=text
|
||||||
klog.FatalDepth(0, text) // $ logger=text
|
}
|
||||||
klog.Fatalf(fmt, text) // $ logger=fmt logger=text
|
if selector == 29 {
|
||||||
klog.Fatalln(text) // $ logger=text
|
klog.Exitf(fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 30 {
|
||||||
|
klog.Exitln(text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 31 {
|
||||||
|
klog.Fatal(text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 32 {
|
||||||
|
klog.FatalDepth(0, text) // $ logger=text
|
||||||
|
}
|
||||||
|
if selector == 33 {
|
||||||
|
klog.Fatalf(fmt, text) // $ logger=fmt logger=text
|
||||||
|
}
|
||||||
|
if selector == 34 {
|
||||||
|
klog.Fatalln(text) // $ logger=text
|
||||||
|
}
|
||||||
klog.Info(text) // $ logger=text
|
klog.Info(text) // $ logger=text
|
||||||
klog.InfoDepth(0, text) // $ logger=text
|
klog.InfoDepth(0, text) // $ logger=text
|
||||||
klog.Infof(fmt, text) // $ logger=fmt logger=text
|
klog.Infof(fmt, text) // $ logger=fmt logger=text
|
||||||
@@ -57,11 +184,19 @@ func glogTest() {
|
|||||||
klog.WarningDepth(0, text) // $ logger=text
|
klog.WarningDepth(0, text) // $ logger=text
|
||||||
klog.Warningf(fmt, text) // $ logger=fmt logger=text
|
klog.Warningf(fmt, text) // $ logger=fmt logger=text
|
||||||
klog.Warningln(text) // $ logger=text
|
klog.Warningln(text) // $ logger=text
|
||||||
|
klog.V(0).Info(text) // $ logger=text
|
||||||
|
klog.V(0).Infof(fmt, text) // $ logger=fmt logger=text
|
||||||
|
klog.V(0).Infoln(text) // $ logger=text
|
||||||
|
|
||||||
// components corresponding to the format specifier "%T" are not considered vulnerable
|
// components corresponding to the format specifier "%T" are not considered vulnerable
|
||||||
klog.Errorf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
klog.Errorf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
klog.Exitf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
if selector == 35 {
|
||||||
klog.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
klog.Exitf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
klog.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
}
|
||||||
klog.Warningf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
if selector == 36 {
|
||||||
|
klog.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
}
|
||||||
|
klog.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
klog.Warningf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
|
klog.V(0).Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module codeql-go-tests/concepts/loggercall
|
|||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
github.com/golang/glog v1.2.5
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.7.0
|
||||||
k8s.io/klog v1.0.0
|
k8s.io/klog v1.0.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ const text = "test"
|
|||||||
var v []byte
|
var v []byte
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
glogTest(len(v))
|
||||||
stdlib()
|
stdlib()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,47 +2,125 @@
|
|||||||
// This is a simple stub for github.com/golang/glog, strictly for use in testing.
|
// This is a simple stub for github.com/golang/glog, strictly for use in testing.
|
||||||
|
|
||||||
// See the LICENSE file for information about the licensing of the original library.
|
// See the LICENSE file for information about the licensing of the original library.
|
||||||
// Source: github.com/golang/glog (exports: ; functions: Error,ErrorDepth,Errorf,Errorln,Exit,ExitDepth,Exitf,Exitln,Fatal,FatalDepth,Fatalf,Fatalln,Info,InfoDepth,Infof,Infoln,Warning,WarningDepth,Warningf,Warningln)
|
// Source: github.com/golang/glog (exports: Level,Verbose; functions: Error,ErrorContext,ErrorContextDepth,ErrorContextDepthf,ErrorContextf,ErrorDepth,ErrorDepthf,Errorf,Errorln,Exit,ExitContext,ExitContextDepth,ExitContextDepthf,ExitContextf,ExitDepth,ExitDepthf,Exitf,Exitln,Fatal,FatalContext,FatalContextDepth,FatalContextDepthf,FatalContextf,FatalDepth,FatalDepthf,Fatalf,Fatalln,Info,InfoContext,InfoContextDepth,InfoContextDepthf,InfoContextf,InfoDepth,InfoDepthf,Infof,Infoln,V,VDepth,Warning,WarningContext,WarningContextDepth,WarningContextDepthf,WarningContextf,WarningDepth,WarningDepthf,Warningf,Warningln)
|
||||||
|
|
||||||
// Package glog is a stub of github.com/golang/glog, generated by depstubber.
|
// Package glog is a stub of github.com/golang/glog, generated by depstubber.
|
||||||
package glog
|
package glog
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type Level int32
|
||||||
|
|
||||||
|
type Verbose bool
|
||||||
|
|
||||||
func Error(_ ...interface{}) {}
|
func Error(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func ErrorContext(_ context.Context, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func ErrorContextDepth(_ context.Context, _ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func ErrorContextDepthf(_ context.Context, _ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func ErrorContextf(_ context.Context, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func ErrorDepth(_ int, _ ...interface{}) {}
|
func ErrorDepth(_ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func ErrorDepthf(_ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Errorf(_ string, _ ...interface{}) {}
|
func Errorf(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Errorln(_ ...interface{}) {}
|
func Errorln(_ ...interface{}) {}
|
||||||
|
|
||||||
func Exit(_ ...interface{}) {}
|
func Exit(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func ExitContext(_ context.Context, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func ExitContextDepth(_ context.Context, _ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func ExitContextDepthf(_ context.Context, _ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func ExitContextf(_ context.Context, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func ExitDepth(_ int, _ ...interface{}) {}
|
func ExitDepth(_ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func ExitDepthf(_ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Exitf(_ string, _ ...interface{}) {}
|
func Exitf(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Exitln(_ ...interface{}) {}
|
func Exitln(_ ...interface{}) {}
|
||||||
|
|
||||||
func Fatal(_ ...interface{}) {}
|
func Fatal(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func FatalContext(_ context.Context, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func FatalContextDepth(_ context.Context, _ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func FatalContextDepthf(_ context.Context, _ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func FatalContextf(_ context.Context, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func FatalDepth(_ int, _ ...interface{}) {}
|
func FatalDepth(_ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func FatalDepthf(_ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Fatalf(_ string, _ ...interface{}) {}
|
func Fatalf(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Fatalln(_ ...interface{}) {}
|
func Fatalln(_ ...interface{}) {}
|
||||||
|
|
||||||
func Info(_ ...interface{}) {}
|
func Info(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func InfoContext(_ context.Context, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func InfoContextDepth(_ context.Context, _ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func InfoContextDepthf(_ context.Context, _ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func InfoContextf(_ context.Context, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func InfoDepth(_ int, _ ...interface{}) {}
|
func InfoDepth(_ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func InfoDepthf(_ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Infof(_ string, _ ...interface{}) {}
|
func Infof(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Infoln(_ ...interface{}) {}
|
func Infoln(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func V(_ Level) Verbose { return false }
|
||||||
|
|
||||||
|
func VDepth(_ int, _ Level) Verbose { return false }
|
||||||
|
|
||||||
func Warning(_ ...interface{}) {}
|
func Warning(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func WarningContext(_ context.Context, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func WarningContextDepth(_ context.Context, _ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func WarningContextDepthf(_ context.Context, _ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func WarningContextf(_ context.Context, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func WarningDepth(_ int, _ ...interface{}) {}
|
func WarningDepth(_ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func WarningDepthf(_ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Warningf(_ string, _ ...interface{}) {}
|
func Warningf(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Warningln(_ ...interface{}) {}
|
func Warningln(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) Info(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) InfoContext(_ context.Context, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) InfoContextDepth(_ context.Context, _ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) InfoContextDepthf(_ context.Context, _ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) InfoContextf(_ context.Context, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) InfoDepth(_ int, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) InfoDepthf(_ int, _ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) Infof(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) Infoln(_ ...interface{}) {}
|
||||||
|
|||||||
14
go/ql/test/library-tests/semmle/go/concepts/LoggerCall/vendor/k8s.io/klog/stub.go
generated
vendored
14
go/ql/test/library-tests/semmle/go/concepts/LoggerCall/vendor/k8s.io/klog/stub.go
generated
vendored
@@ -2,11 +2,15 @@
|
|||||||
// This is a simple stub for k8s.io/klog, strictly for use in testing.
|
// This is a simple stub for k8s.io/klog, strictly for use in testing.
|
||||||
|
|
||||||
// See the LICENSE file for information about the licensing of the original library.
|
// See the LICENSE file for information about the licensing of the original library.
|
||||||
// Source: k8s.io/klog (exports: ; functions: Error,ErrorDepth,Errorf,Errorln,Exit,ExitDepth,Exitf,Exitln,Fatal,FatalDepth,Fatalf,Fatalln,Info,InfoDepth,Infof,Infoln,Warning,WarningDepth,Warningf,Warningln)
|
// Source: k8s.io/klog (exports: Level,Verbose; functions: Error,ErrorDepth,Errorf,Errorln,Exit,ExitDepth,Exitf,Exitln,Fatal,FatalDepth,Fatalf,Fatalln,Info,InfoDepth,Infof,Infoln,V,Warning,WarningDepth,Warningf,Warningln)
|
||||||
|
|
||||||
// Package klog is a stub of k8s.io/klog, generated by depstubber.
|
// Package klog is a stub of k8s.io/klog, generated by depstubber.
|
||||||
package klog
|
package klog
|
||||||
|
|
||||||
|
type Level int32
|
||||||
|
|
||||||
|
type Verbose bool
|
||||||
|
|
||||||
func Error(_ ...interface{}) {}
|
func Error(_ ...interface{}) {}
|
||||||
|
|
||||||
func ErrorDepth(_ int, _ ...interface{}) {}
|
func ErrorDepth(_ int, _ ...interface{}) {}
|
||||||
@@ -39,6 +43,8 @@ func Infof(_ string, _ ...interface{}) {}
|
|||||||
|
|
||||||
func Infoln(_ ...interface{}) {}
|
func Infoln(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func V(_ Level) Verbose { return false }
|
||||||
|
|
||||||
func Warning(_ ...interface{}) {}
|
func Warning(_ ...interface{}) {}
|
||||||
|
|
||||||
func WarningDepth(_ int, _ ...interface{}) {}
|
func WarningDepth(_ int, _ ...interface{}) {}
|
||||||
@@ -46,3 +52,9 @@ func WarningDepth(_ int, _ ...interface{}) {}
|
|||||||
func Warningf(_ string, _ ...interface{}) {}
|
func Warningf(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func Warningln(_ ...interface{}) {}
|
func Warningln(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) Info(_ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) Infof(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (_ Verbose) Infoln(_ ...interface{}) {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
# github.com/golang/glog v1.2.5
|
||||||
## explicit
|
## explicit
|
||||||
github.com/golang/glog
|
github.com/golang/glog
|
||||||
# github.com/sirupsen/logrus v1.7.0
|
# github.com/sirupsen/logrus v1.7.0
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
| file://:0:0:0:0 | Exit | package os |
|
| file://:0:0:0:0 | Exit | os.Exit |
|
||||||
| file://:0:0:0:0 | Fatal | package log |
|
| file://:0:0:0:0 | Fatal | log.Fatal |
|
||||||
| file://:0:0:0:0 | Fatalf | package log |
|
| file://:0:0:0:0 | Fatal | log.Logger.Fatal |
|
||||||
| file://:0:0:0:0 | Fatalln | package log |
|
| file://:0:0:0:0 | Fatalf | log.Fatalf |
|
||||||
| noretfunctions.go:8:6:8:12 | isNoRet | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph |
|
| file://:0:0:0:0 | Fatalf | log.Logger.Fatalf |
|
||||||
| noretfunctions.go:20:6:20:22 | noRetUsesLogFatal | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph |
|
| file://:0:0:0:0 | Fatalln | log.Fatalln |
|
||||||
| noretfunctions.go:24:6:24:23 | noRetUsesLogFatalf | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph |
|
| file://:0:0:0:0 | Fatalln | log.Logger.Fatalln |
|
||||||
| stmts7.go:10:6:10:15 | canRecover | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph |
|
| file://:0:0:0:0 | Panic | log.Logger.Panic |
|
||||||
| stmts.go:10:6:10:10 | test5 | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph |
|
| file://:0:0:0:0 | Panic | log.Panic |
|
||||||
| stmts.go:46:6:46:10 | test6 | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph |
|
| file://:0:0:0:0 | Panicf | log.Logger.Panicf |
|
||||||
| stmts.go:112:6:112:10 | test9 | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph |
|
| file://:0:0:0:0 | Panicf | log.Panicf |
|
||||||
|
| file://:0:0:0:0 | Panicln | log.Logger.Panicln |
|
||||||
|
| file://:0:0:0:0 | Panicln | log.Panicln |
|
||||||
|
| file://:0:0:0:0 | panic | panic |
|
||||||
|
| noretfunctions.go:8:6:8:12 | isNoRet | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.isNoRet |
|
||||||
|
| noretfunctions.go:20:6:20:22 | noRetUsesLogFatal | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.noRetUsesLogFatal |
|
||||||
|
| noretfunctions.go:24:6:24:23 | noRetUsesLogFatalf | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.noRetUsesLogFatalf |
|
||||||
|
| stmts7.go:10:6:10:15 | canRecover | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.canRecover |
|
||||||
|
| stmts.go:10:6:10:10 | test5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test5 |
|
||||||
|
| stmts.go:46:6:46:10 | test6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test6 |
|
||||||
|
| stmts.go:112:6:112:10 | test9 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test9 |
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ import go
|
|||||||
|
|
||||||
from Function f
|
from Function f
|
||||||
where not f.mayReturnNormally()
|
where not f.mayReturnNormally()
|
||||||
select f, f.getPackage()
|
select f, f.getQualifiedName()
|
||||||
|
|||||||
@@ -15,62 +15,6 @@ func TaintStepTest_LogNew_B0I0O0(sourceCQL interface{}) interface{} {
|
|||||||
return intoWriter414
|
return intoWriter414
|
||||||
}
|
}
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerFatal_B0I0O0(sourceCQL interface{}) interface{} {
|
|
||||||
fromInterface518 := sourceCQL.(interface{})
|
|
||||||
var intoLogger650 log.Logger
|
|
||||||
intoLogger650.Fatal(fromInterface518)
|
|
||||||
return intoLogger650
|
|
||||||
}
|
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerFatalf_B0I0O0(sourceCQL interface{}) interface{} {
|
|
||||||
fromString784 := sourceCQL.(string)
|
|
||||||
var intoLogger957 log.Logger
|
|
||||||
intoLogger957.Fatalf(fromString784, nil)
|
|
||||||
return intoLogger957
|
|
||||||
}
|
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerFatalf_B0I1O0(sourceCQL interface{}) interface{} {
|
|
||||||
fromInterface520 := sourceCQL.(interface{})
|
|
||||||
var intoLogger443 log.Logger
|
|
||||||
intoLogger443.Fatalf("", fromInterface520)
|
|
||||||
return intoLogger443
|
|
||||||
}
|
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerFatalln_B0I0O0(sourceCQL interface{}) interface{} {
|
|
||||||
fromInterface127 := sourceCQL.(interface{})
|
|
||||||
var intoLogger483 log.Logger
|
|
||||||
intoLogger483.Fatalln(fromInterface127)
|
|
||||||
return intoLogger483
|
|
||||||
}
|
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerPanic_B0I0O0(sourceCQL interface{}) interface{} {
|
|
||||||
fromInterface989 := sourceCQL.(interface{})
|
|
||||||
var intoLogger982 log.Logger
|
|
||||||
intoLogger982.Panic(fromInterface989)
|
|
||||||
return intoLogger982
|
|
||||||
}
|
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerPanicf_B0I0O0(sourceCQL interface{}) interface{} {
|
|
||||||
fromString417 := sourceCQL.(string)
|
|
||||||
var intoLogger584 log.Logger
|
|
||||||
intoLogger584.Panicf(fromString417, nil)
|
|
||||||
return intoLogger584
|
|
||||||
}
|
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerPanicf_B0I1O0(sourceCQL interface{}) interface{} {
|
|
||||||
fromInterface991 := sourceCQL.(interface{})
|
|
||||||
var intoLogger881 log.Logger
|
|
||||||
intoLogger881.Panicf("", fromInterface991)
|
|
||||||
return intoLogger881
|
|
||||||
}
|
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerPanicln_B0I0O0(sourceCQL interface{}) interface{} {
|
|
||||||
fromInterface186 := sourceCQL.(interface{})
|
|
||||||
var intoLogger284 log.Logger
|
|
||||||
intoLogger284.Panicln(fromInterface186)
|
|
||||||
return intoLogger284
|
|
||||||
}
|
|
||||||
|
|
||||||
func TaintStepTest_LogLoggerPrint_B0I0O0(sourceCQL interface{}) interface{} {
|
func TaintStepTest_LogLoggerPrint_B0I0O0(sourceCQL interface{}) interface{} {
|
||||||
fromInterface908 := sourceCQL.(interface{})
|
fromInterface908 := sourceCQL.(interface{})
|
||||||
var intoLogger137 log.Logger
|
var intoLogger137 log.Logger
|
||||||
@@ -125,46 +69,6 @@ func RunAllTaints_Log() {
|
|||||||
out := TaintStepTest_LogNew_B0I0O0(source)
|
out := TaintStepTest_LogNew_B0I0O0(source)
|
||||||
sink(0, out)
|
sink(0, out)
|
||||||
}
|
}
|
||||||
{
|
|
||||||
source := newSource(1)
|
|
||||||
out := TaintStepTest_LogLoggerFatal_B0I0O0(source)
|
|
||||||
sink(1, out)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
source := newSource(2)
|
|
||||||
out := TaintStepTest_LogLoggerFatalf_B0I0O0(source)
|
|
||||||
sink(2, out)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
source := newSource(3)
|
|
||||||
out := TaintStepTest_LogLoggerFatalf_B0I1O0(source)
|
|
||||||
sink(3, out)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
source := newSource(4)
|
|
||||||
out := TaintStepTest_LogLoggerFatalln_B0I0O0(source)
|
|
||||||
sink(4, out)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
source := newSource(5)
|
|
||||||
out := TaintStepTest_LogLoggerPanic_B0I0O0(source)
|
|
||||||
sink(5, out)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
source := newSource(6)
|
|
||||||
out := TaintStepTest_LogLoggerPanicf_B0I0O0(source)
|
|
||||||
sink(6, out)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
source := newSource(7)
|
|
||||||
out := TaintStepTest_LogLoggerPanicf_B0I1O0(source)
|
|
||||||
sink(7, out)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
source := newSource(8)
|
|
||||||
out := TaintStepTest_LogLoggerPanicln_B0I0O0(source)
|
|
||||||
sink(8, out)
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
source := newSource(9)
|
source := newSource(9)
|
||||||
out := TaintStepTest_LogLoggerPrint_B0I0O0(source)
|
out := TaintStepTest_LogLoggerPrint_B0I0O0(source)
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ reverseRead
|
|||||||
| LogInjection.go:33:14:33:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:33:14:33:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
| LogInjection.go:34:18:34:20 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:34:18:34:20 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
| LogInjection.go:35:14:35:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:35:14:35:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
| LogInjection.go:447:14:447:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:551:14:551:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
| LogInjection.go:455:14:455:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:559:14:559:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
| LogInjection.go:463:14:463:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:567:14:567:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
| LogInjection.go:498:14:498:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:602:14:602:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
| LogInjection.go:499:14:499:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:603:14:603:16 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
| LogInjection.go:724:12:724:14 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
| LogInjection.go:828:12:828:14 | implicit dereference | Origin of readStep is missing a PostUpdateNode. |
|
||||||
|
|||||||
@@ -49,22 +49,22 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
log.Printf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
log.Printf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
||||||
log.Println("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
log.Println("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
|
|
||||||
if testFlag == "true" {
|
if testFlag == "1" {
|
||||||
log.Fatal("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
log.Fatal("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
}
|
}
|
||||||
if testFlag == "true" {
|
if testFlag == "2" {
|
||||||
log.Fatalf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
log.Fatalf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
||||||
}
|
}
|
||||||
if testFlag == "true" {
|
if testFlag == "3" {
|
||||||
log.Fatalln("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
log.Fatalln("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
}
|
}
|
||||||
if testFlag == "true" {
|
if testFlag == "4" {
|
||||||
log.Panic("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
log.Panic("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
}
|
}
|
||||||
if testFlag == "true" {
|
if testFlag == "5" {
|
||||||
log.Panicf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
log.Panicf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
||||||
}
|
}
|
||||||
if testFlag == "true" {
|
if testFlag == "6" {
|
||||||
log.Panicln("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
log.Panicln("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +72,24 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
logger.Print("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
logger.Print("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
logger.Printf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
logger.Printf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
||||||
logger.Println("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
logger.Println("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
logger.Fatal("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
if testFlag == "7" {
|
||||||
logger.Fatalf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
logger.Fatal("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
logger.Fatalln("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
}
|
||||||
logger.Panic("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
if testFlag == "8" {
|
||||||
logger.Panicf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
logger.Fatalf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
||||||
logger.Panicln("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
}
|
||||||
|
if testFlag == "9" {
|
||||||
|
logger.Fatalln("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
|
}
|
||||||
|
if testFlag == "10" {
|
||||||
|
logger.Panic("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
|
}
|
||||||
|
if testFlag == "11" {
|
||||||
|
logger.Panicf(formatString, username, password) // $ hasTaintFlow="formatString" hasTaintFlow="username" hasTaintFlow="password"
|
||||||
|
}
|
||||||
|
if testFlag == "12" {
|
||||||
|
logger.Panicln("user is logged in:", username, password) // $ hasTaintFlow="username" hasTaintFlow="password"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// k8s.io/klog
|
// k8s.io/klog
|
||||||
{
|
{
|
||||||
@@ -91,12 +103,24 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
klog.Error(username) // $ hasTaintFlow="username"
|
klog.Error(username) // $ hasTaintFlow="username"
|
||||||
klog.Errorf(username) // $ hasTaintFlow="username"
|
klog.Errorf(username) // $ hasTaintFlow="username"
|
||||||
klog.Errorln(username) // $ hasTaintFlow="username"
|
klog.Errorln(username) // $ hasTaintFlow="username"
|
||||||
klog.Fatal(username) // $ hasTaintFlow="username"
|
if testFlag == "77" {
|
||||||
klog.Fatalf(username) // $ hasTaintFlow="username"
|
klog.Fatal(username) // $ hasTaintFlow="username"
|
||||||
klog.Fatalln(username) // $ hasTaintFlow="username"
|
}
|
||||||
klog.Exit(username) // $ hasTaintFlow="username"
|
if testFlag == "78" {
|
||||||
klog.Exitf(username) // $ hasTaintFlow="username"
|
klog.Fatalf(username) // $ hasTaintFlow="username"
|
||||||
klog.Exitln(username) // $ hasTaintFlow="username"
|
}
|
||||||
|
if testFlag == "79" {
|
||||||
|
klog.Fatalln(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "80" {
|
||||||
|
klog.Exit(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "81" {
|
||||||
|
klog.Exitf(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "82" {
|
||||||
|
klog.Exitln(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// astaxie/beego
|
// astaxie/beego
|
||||||
{
|
{
|
||||||
@@ -161,14 +185,30 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
glog.ErrorDepth(0, username) // $ hasTaintFlow="username"
|
glog.ErrorDepth(0, username) // $ hasTaintFlow="username"
|
||||||
glog.Errorf(username) // $ hasTaintFlow="username"
|
glog.Errorf(username) // $ hasTaintFlow="username"
|
||||||
glog.Errorln(username) // $ hasTaintFlow="username"
|
glog.Errorln(username) // $ hasTaintFlow="username"
|
||||||
glog.Fatal(username) // $ hasTaintFlow="username"
|
if testFlag == "83" {
|
||||||
glog.FatalDepth(0, username) // $ hasTaintFlow="username"
|
glog.Fatal(username) // $ hasTaintFlow="username"
|
||||||
glog.Fatalf(username) // $ hasTaintFlow="username"
|
}
|
||||||
glog.Fatalln(username) // $ hasTaintFlow="username"
|
if testFlag == "84" {
|
||||||
glog.Exit(username) // $ hasTaintFlow="username"
|
glog.FatalDepth(0, username) // $ hasTaintFlow="username"
|
||||||
glog.ExitDepth(0, username) // $ hasTaintFlow="username"
|
}
|
||||||
glog.Exitf(username) // $ hasTaintFlow="username"
|
if testFlag == "85" {
|
||||||
glog.Exitln(username) // $ hasTaintFlow="username"
|
glog.Fatalf(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "86" {
|
||||||
|
glog.Fatalln(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "87" {
|
||||||
|
glog.Exit(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "88" {
|
||||||
|
glog.ExitDepth(0, username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "89" {
|
||||||
|
glog.Exitf(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "90" {
|
||||||
|
glog.Exitln(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// sirupsen/logrus
|
// sirupsen/logrus
|
||||||
@@ -179,26 +219,42 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
logger := logrus.New()
|
logger := logrus.New()
|
||||||
entry := logrus.NewEntry(logger)
|
entry := logrus.NewEntry(logger)
|
||||||
|
|
||||||
logrus.Debug(username) // $ hasTaintFlow="username"
|
logrus.Debug(username) // $ hasTaintFlow="username"
|
||||||
logrus.Debugf(username, "") // $ hasTaintFlow="username"
|
logrus.Debugf(username, "") // $ hasTaintFlow="username"
|
||||||
logrus.Debugf("", username) // $ hasTaintFlow="username"
|
logrus.Debugf("", username) // $ hasTaintFlow="username"
|
||||||
logrus.Debugln(username) // $ hasTaintFlow="username"
|
logrus.Debugln(username) // $ hasTaintFlow="username"
|
||||||
logrus.Error(username) // $ hasTaintFlow="username"
|
logrus.Error(username) // $ hasTaintFlow="username"
|
||||||
logrus.Errorf(username, "") // $ hasTaintFlow="username"
|
logrus.Errorf(username, "") // $ hasTaintFlow="username"
|
||||||
logrus.Errorf("", username) // $ hasTaintFlow="username"
|
logrus.Errorf("", username) // $ hasTaintFlow="username"
|
||||||
logrus.Errorln(username) // $ hasTaintFlow="username"
|
logrus.Errorln(username) // $ hasTaintFlow="username"
|
||||||
logrus.Fatal(username) // $ hasTaintFlow="username"
|
if testFlag == "13" {
|
||||||
logrus.Fatalf(username, "") // $ hasTaintFlow="username"
|
logrus.Fatal(username) // $ hasTaintFlow="username"
|
||||||
logrus.Fatalf("", username) // $ hasTaintFlow="username"
|
}
|
||||||
logrus.Fatalln(username) // $ hasTaintFlow="username"
|
if testFlag == "14" {
|
||||||
logrus.Info(username) // $ hasTaintFlow="username"
|
logrus.Fatalf(username, "") // $ hasTaintFlow="username"
|
||||||
logrus.Infof(username, "") // $ hasTaintFlow="username"
|
}
|
||||||
logrus.Infof("", username) // $ hasTaintFlow="username"
|
if testFlag == "15" {
|
||||||
logrus.Infoln(username) // $ hasTaintFlow="username"
|
logrus.Fatalf("", username) // $ hasTaintFlow="username"
|
||||||
logrus.Panic(username) // $ hasTaintFlow="username"
|
}
|
||||||
logrus.Panicf(username, "") // $ hasTaintFlow="username"
|
if testFlag == "16" {
|
||||||
logrus.Panicf("", username) // $ hasTaintFlow="username"
|
logrus.Fatalln(username) // $ hasTaintFlow="username"
|
||||||
logrus.Panicln(username) // $ hasTaintFlow="username"
|
}
|
||||||
|
logrus.Info(username) // $ hasTaintFlow="username"
|
||||||
|
logrus.Infof(username, "") // $ hasTaintFlow="username"
|
||||||
|
logrus.Infof("", username) // $ hasTaintFlow="username"
|
||||||
|
logrus.Infoln(username) // $ hasTaintFlow="username"
|
||||||
|
if testFlag == "17" {
|
||||||
|
logrus.Panic(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "18" {
|
||||||
|
logrus.Panicf(username, "") // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "19" {
|
||||||
|
logrus.Panicf("", username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "20" {
|
||||||
|
logrus.Panicln(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
logrus.Print(username) // $ hasTaintFlow="username"
|
logrus.Print(username) // $ hasTaintFlow="username"
|
||||||
logrus.Printf(username, "") // $ hasTaintFlow="username"
|
logrus.Printf(username, "") // $ hasTaintFlow="username"
|
||||||
logrus.Printf("", username) // $ hasTaintFlow="username"
|
logrus.Printf("", username) // $ hasTaintFlow="username"
|
||||||
@@ -220,30 +276,46 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
logrus.WithField("", username) // $ hasTaintFlow="username"
|
logrus.WithField("", username) // $ hasTaintFlow="username"
|
||||||
logrus.WithFields(fields) // $ hasTaintFlow="fields"
|
logrus.WithFields(fields) // $ hasTaintFlow="fields"
|
||||||
|
|
||||||
entry.Debug(username) // $ hasTaintFlow="username"
|
entry.Debug(username) // $ hasTaintFlow="username"
|
||||||
entry.Debugf(username, "") // $ hasTaintFlow="username"
|
entry.Debugf(username, "") // $ hasTaintFlow="username"
|
||||||
entry.Debugf("", username) // $ hasTaintFlow="username"
|
entry.Debugf("", username) // $ hasTaintFlow="username"
|
||||||
entry.Debugln(username) // $ hasTaintFlow="username"
|
entry.Debugln(username) // $ hasTaintFlow="username"
|
||||||
entry.Error(username) // $ hasTaintFlow="username"
|
entry.Error(username) // $ hasTaintFlow="username"
|
||||||
entry.Errorf(username, "") // $ hasTaintFlow="username"
|
entry.Errorf(username, "") // $ hasTaintFlow="username"
|
||||||
entry.Errorf("", username) // $ hasTaintFlow="username"
|
entry.Errorf("", username) // $ hasTaintFlow="username"
|
||||||
entry.Errorln(username) // $ hasTaintFlow="username"
|
entry.Errorln(username) // $ hasTaintFlow="username"
|
||||||
entry.Fatal(username) // $ hasTaintFlow="username"
|
if testFlag == "21" {
|
||||||
entry.Fatalf(username, "") // $ hasTaintFlow="username"
|
entry.Fatal(username) // $ hasTaintFlow="username"
|
||||||
entry.Fatalf("", username) // $ hasTaintFlow="username"
|
}
|
||||||
entry.Fatalln(username) // $ hasTaintFlow="username"
|
if testFlag == "22" {
|
||||||
entry.Info(username) // $ hasTaintFlow="username"
|
entry.Fatalf(username, "") // $ hasTaintFlow="username"
|
||||||
entry.Infof(username, "") // $ hasTaintFlow="username"
|
}
|
||||||
entry.Infof("", username) // $ hasTaintFlow="username"
|
if testFlag == "23" {
|
||||||
entry.Infoln(username) // $ hasTaintFlow="username"
|
entry.Fatalf("", username) // $ hasTaintFlow="username"
|
||||||
entry.Log(0, username) // $ hasTaintFlow="username"
|
}
|
||||||
entry.Logf(0, username, "") // $ hasTaintFlow="username"
|
if testFlag == "24" {
|
||||||
entry.Logf(0, "", username) // $ hasTaintFlow="username"
|
entry.Fatalln(username) // $ hasTaintFlow="username"
|
||||||
entry.Logln(0, username) // $ hasTaintFlow="username"
|
}
|
||||||
entry.Panic(username) // $ hasTaintFlow="username"
|
entry.Info(username) // $ hasTaintFlow="username"
|
||||||
entry.Panicf(username, "") // $ hasTaintFlow="username"
|
entry.Infof(username, "") // $ hasTaintFlow="username"
|
||||||
entry.Panicf("", username) // $ hasTaintFlow="username"
|
entry.Infof("", username) // $ hasTaintFlow="username"
|
||||||
entry.Panicln(username) // $ hasTaintFlow="username"
|
entry.Infoln(username) // $ hasTaintFlow="username"
|
||||||
|
entry.Log(0, username) // $ hasTaintFlow="username"
|
||||||
|
entry.Logf(0, username, "") // $ hasTaintFlow="username"
|
||||||
|
entry.Logf(0, "", username) // $ hasTaintFlow="username"
|
||||||
|
entry.Logln(0, username) // $ hasTaintFlow="username"
|
||||||
|
if testFlag == "25" {
|
||||||
|
entry.Panic(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "26" {
|
||||||
|
entry.Panicf(username, "") // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "27" {
|
||||||
|
entry.Panicf("", username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "28" {
|
||||||
|
entry.Panicln(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
entry.Print(username) // $ hasTaintFlow="username"
|
entry.Print(username) // $ hasTaintFlow="username"
|
||||||
entry.Printf(username, "") // $ hasTaintFlow="username"
|
entry.Printf(username, "") // $ hasTaintFlow="username"
|
||||||
entry.Printf("", username) // $ hasTaintFlow="username"
|
entry.Printf("", username) // $ hasTaintFlow="username"
|
||||||
@@ -265,30 +337,46 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
entry.WithField("", username) // $ hasTaintFlow="username"
|
entry.WithField("", username) // $ hasTaintFlow="username"
|
||||||
entry.WithFields(fields) // $ hasTaintFlow="fields"
|
entry.WithFields(fields) // $ hasTaintFlow="fields"
|
||||||
|
|
||||||
logger.Debug(username) // $ hasTaintFlow="username"
|
logger.Debug(username) // $ hasTaintFlow="username"
|
||||||
logger.Debugf(username, "") // $ hasTaintFlow="username"
|
logger.Debugf(username, "") // $ hasTaintFlow="username"
|
||||||
logger.Debugf("", username) // $ hasTaintFlow="username"
|
logger.Debugf("", username) // $ hasTaintFlow="username"
|
||||||
logger.Debugln(username) // $ hasTaintFlow="username"
|
logger.Debugln(username) // $ hasTaintFlow="username"
|
||||||
logger.Error(username) // $ hasTaintFlow="username"
|
logger.Error(username) // $ hasTaintFlow="username"
|
||||||
logger.Errorf(username, "") // $ hasTaintFlow="username"
|
logger.Errorf(username, "") // $ hasTaintFlow="username"
|
||||||
logger.Errorf("", username) // $ hasTaintFlow="username"
|
logger.Errorf("", username) // $ hasTaintFlow="username"
|
||||||
logger.Errorln(username) // $ hasTaintFlow="username"
|
logger.Errorln(username) // $ hasTaintFlow="username"
|
||||||
logger.Fatal(username) // $ hasTaintFlow="username"
|
if testFlag == "29" {
|
||||||
logger.Fatalf(username, "") // $ hasTaintFlow="username"
|
logger.Fatal(username) // $ hasTaintFlow="username"
|
||||||
logger.Fatalf("", username) // $ hasTaintFlow="username"
|
}
|
||||||
logger.Fatalln(username) // $ hasTaintFlow="username"
|
if testFlag == "30" {
|
||||||
logger.Info(username) // $ hasTaintFlow="username"
|
logger.Fatalf(username, "") // $ hasTaintFlow="username"
|
||||||
logger.Infof(username, "") // $ hasTaintFlow="username"
|
}
|
||||||
logger.Infof("", username) // $ hasTaintFlow="username"
|
if testFlag == "31" {
|
||||||
logger.Infoln(username) // $ hasTaintFlow="username"
|
logger.Fatalf("", username) // $ hasTaintFlow="username"
|
||||||
logger.Log(0, username) // $ hasTaintFlow="username"
|
}
|
||||||
logger.Logf(0, username, "") // $ hasTaintFlow="username"
|
if testFlag == "32" {
|
||||||
logger.Logf(0, "", username) // $ hasTaintFlow="username"
|
logger.Fatalln(username) // $ hasTaintFlow="username"
|
||||||
logger.Logln(0, username) // $ hasTaintFlow="username"
|
}
|
||||||
logger.Panic(username) // $ hasTaintFlow="username"
|
logger.Info(username) // $ hasTaintFlow="username"
|
||||||
logger.Panicf(username, "") // $ hasTaintFlow="username"
|
logger.Infof(username, "") // $ hasTaintFlow="username"
|
||||||
logger.Panicf("", username) // $ hasTaintFlow="username"
|
logger.Infof("", username) // $ hasTaintFlow="username"
|
||||||
logger.Panicln(username) // $ hasTaintFlow="username"
|
logger.Infoln(username) // $ hasTaintFlow="username"
|
||||||
|
logger.Log(0, username) // $ hasTaintFlow="username"
|
||||||
|
logger.Logf(0, username, "") // $ hasTaintFlow="username"
|
||||||
|
logger.Logf(0, "", username) // $ hasTaintFlow="username"
|
||||||
|
logger.Logln(0, username) // $ hasTaintFlow="username"
|
||||||
|
if testFlag == "33" {
|
||||||
|
logger.Panic(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "34" {
|
||||||
|
logger.Panicf(username, "") // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "35" {
|
||||||
|
logger.Panicf("", username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "36" {
|
||||||
|
logger.Panicln(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
logger.Print(username) // $ hasTaintFlow="username"
|
logger.Print(username) // $ hasTaintFlow="username"
|
||||||
logger.Printf(username, "") // $ hasTaintFlow="username"
|
logger.Printf(username, "") // $ hasTaintFlow="username"
|
||||||
logger.Printf("", username) // $ hasTaintFlow="username"
|
logger.Printf("", username) // $ hasTaintFlow="username"
|
||||||
@@ -311,26 +399,42 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
logger.WithFields(fields) // $ hasTaintFlow="fields"
|
logger.WithFields(fields) // $ hasTaintFlow="fields"
|
||||||
|
|
||||||
var fieldlogger logrus.FieldLogger = entry
|
var fieldlogger logrus.FieldLogger = entry
|
||||||
fieldlogger.Debug(username) // $ hasTaintFlow="username"
|
fieldlogger.Debug(username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Debugf(username, "") // $ hasTaintFlow="username"
|
fieldlogger.Debugf(username, "") // $ hasTaintFlow="username"
|
||||||
fieldlogger.Debugf("", username) // $ hasTaintFlow="username"
|
fieldlogger.Debugf("", username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Debugln(username) // $ hasTaintFlow="username"
|
fieldlogger.Debugln(username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Error(username) // $ hasTaintFlow="username"
|
fieldlogger.Error(username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Errorf(username, "") // $ hasTaintFlow="username"
|
fieldlogger.Errorf(username, "") // $ hasTaintFlow="username"
|
||||||
fieldlogger.Errorf("", username) // $ hasTaintFlow="username"
|
fieldlogger.Errorf("", username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Errorln(username) // $ hasTaintFlow="username"
|
fieldlogger.Errorln(username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Fatal(username) // $ hasTaintFlow="username"
|
if testFlag == "37" {
|
||||||
fieldlogger.Fatalf(username, "") // $ hasTaintFlow="username"
|
fieldlogger.Fatal(username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Fatalf("", username) // $ hasTaintFlow="username"
|
}
|
||||||
fieldlogger.Fatalln(username) // $ hasTaintFlow="username"
|
if testFlag == "38" {
|
||||||
fieldlogger.Info(username) // $ hasTaintFlow="username"
|
fieldlogger.Fatalf(username, "") // $ hasTaintFlow="username"
|
||||||
fieldlogger.Infof(username, "") // $ hasTaintFlow="username"
|
}
|
||||||
fieldlogger.Infof("", username) // $ hasTaintFlow="username"
|
if testFlag == "39" {
|
||||||
fieldlogger.Infoln(username) // $ hasTaintFlow="username"
|
fieldlogger.Fatalf("", username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Panic(username) // $ hasTaintFlow="username"
|
}
|
||||||
fieldlogger.Panicf(username, "") // $ hasTaintFlow="username"
|
if testFlag == "40" {
|
||||||
fieldlogger.Panicf("", username) // $ hasTaintFlow="username"
|
fieldlogger.Fatalln(username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Panicln(username) // $ hasTaintFlow="username"
|
}
|
||||||
|
fieldlogger.Info(username) // $ hasTaintFlow="username"
|
||||||
|
fieldlogger.Infof(username, "") // $ hasTaintFlow="username"
|
||||||
|
fieldlogger.Infof("", username) // $ hasTaintFlow="username"
|
||||||
|
fieldlogger.Infoln(username) // $ hasTaintFlow="username"
|
||||||
|
if testFlag == "41" {
|
||||||
|
fieldlogger.Panic(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "42" {
|
||||||
|
fieldlogger.Panicf(username, "") // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "43" {
|
||||||
|
fieldlogger.Panicf("", username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
|
if testFlag == "44" {
|
||||||
|
fieldlogger.Panicln(username) // $ hasTaintFlow="username"
|
||||||
|
}
|
||||||
fieldlogger.Print(username) // $ hasTaintFlow="username"
|
fieldlogger.Print(username) // $ hasTaintFlow="username"
|
||||||
fieldlogger.Printf(username, "") // $ hasTaintFlow="username"
|
fieldlogger.Printf(username, "") // $ hasTaintFlow="username"
|
||||||
fieldlogger.Printf("", username) // $ hasTaintFlow="username"
|
fieldlogger.Printf("", username) // $ hasTaintFlow="username"
|
||||||
@@ -366,11 +470,11 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
logger.DPanic(username) // $ hasTaintFlow="username"
|
logger.DPanic(username) // $ hasTaintFlow="username"
|
||||||
logger.Debug(username) // $ hasTaintFlow="username"
|
logger.Debug(username) // $ hasTaintFlow="username"
|
||||||
logger.Error(username) // $ hasTaintFlow="username"
|
logger.Error(username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "45" {
|
||||||
logger.Fatal(username) // $ hasTaintFlow="username"
|
logger.Fatal(username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
logger.Info(username) // $ hasTaintFlow="username"
|
logger.Info(username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "46" {
|
||||||
logger.Panic(username) // $ hasTaintFlow="username"
|
logger.Panic(username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
logger.Warn(username) // $ hasTaintFlow="username"
|
logger.Warn(username) // $ hasTaintFlow="username"
|
||||||
@@ -382,33 +486,33 @@ func handler(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
sLogger.DPanic(username) // $ hasTaintFlow="username"
|
sLogger.DPanic(username) // $ hasTaintFlow="username"
|
||||||
sLogger.Debug(username) // $ hasTaintFlow="username"
|
sLogger.Debug(username) // $ hasTaintFlow="username"
|
||||||
sLogger.Error(username) // $ hasTaintFlow="username"
|
sLogger.Error(username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "47" {
|
||||||
sLogger.Fatal(username) // $ hasTaintFlow="username"
|
sLogger.Fatal(username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
sLogger.Info(username) // $ hasTaintFlow="username"
|
sLogger.Info(username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "48" {
|
||||||
sLogger.Panic(username) // $ hasTaintFlow="username"
|
sLogger.Panic(username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
sLogger.Warn(username) // $ hasTaintFlow="username"
|
sLogger.Warn(username) // $ hasTaintFlow="username"
|
||||||
sLogger.DPanicf(username) // $ hasTaintFlow="username"
|
sLogger.DPanicf(username) // $ hasTaintFlow="username"
|
||||||
sLogger.Debugf(username) // $ hasTaintFlow="username"
|
sLogger.Debugf(username) // $ hasTaintFlow="username"
|
||||||
sLogger.Errorf(username) // $ hasTaintFlow="username"
|
sLogger.Errorf(username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "49" {
|
||||||
sLogger.Fatalf(username) // $ hasTaintFlow="username"
|
sLogger.Fatalf(username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
sLogger.Infof(username) // $ hasTaintFlow="username"
|
sLogger.Infof(username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "50" {
|
||||||
sLogger.Panicf(username) // $ hasTaintFlow="username"
|
sLogger.Panicf(username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
sLogger.Warnf(username) // $ hasTaintFlow="username"
|
sLogger.Warnf(username) // $ hasTaintFlow="username"
|
||||||
sLogger.DPanicw(username) // $ hasTaintFlow="username"
|
sLogger.DPanicw(username) // $ hasTaintFlow="username"
|
||||||
sLogger.Debugw(username) // $ hasTaintFlow="username"
|
sLogger.Debugw(username) // $ hasTaintFlow="username"
|
||||||
sLogger.Errorw(username) // $ hasTaintFlow="username"
|
sLogger.Errorw(username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "51" {
|
||||||
sLogger.Fatalw(username) // $ hasTaintFlow="username"
|
sLogger.Fatalw(username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
sLogger.Infow(username) // $ hasTaintFlow="username"
|
sLogger.Infow(username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "52" {
|
||||||
sLogger.Panicw(username) // $ hasTaintFlow="username"
|
sLogger.Panicw(username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
sLogger.Warnw(username) // $ hasTaintFlow="username"
|
sLogger.Warnw(username) // $ hasTaintFlow="username"
|
||||||
@@ -515,10 +619,10 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
verbose.Infof("user %q logged in.\n", username)
|
verbose.Infof("user %q logged in.\n", username)
|
||||||
klog.Infof("user %q logged in.\n", username)
|
klog.Infof("user %q logged in.\n", username)
|
||||||
klog.Errorf("user %q logged in.\n", username)
|
klog.Errorf("user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "53" {
|
||||||
klog.Fatalf("user %q logged in.\n", username)
|
klog.Fatalf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
if testFlag == " true" {
|
if testFlag == "54" {
|
||||||
klog.Exitf("user %q logged in.\n", username)
|
klog.Exitf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -534,10 +638,10 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
|
|
||||||
glog.Infof("user %q logged in.\n", username)
|
glog.Infof("user %q logged in.\n", username)
|
||||||
glog.Errorf("user %q logged in.\n", username)
|
glog.Errorf("user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "55" {
|
||||||
glog.Fatalf("user %q logged in.\n", username)
|
glog.Fatalf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
if testFlag == " true" {
|
if testFlag == "56" {
|
||||||
glog.Exitf("user %q logged in.\n", username)
|
glog.Exitf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,11 +649,11 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
{
|
{
|
||||||
logrus.Debugf("user %q logged in.\n", username)
|
logrus.Debugf("user %q logged in.\n", username)
|
||||||
logrus.Errorf("user %q logged in.\n", username)
|
logrus.Errorf("user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "57" {
|
||||||
logrus.Fatalf("user %q logged in.\n", username)
|
logrus.Fatalf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
logrus.Infof("user %q logged in.\n", username)
|
logrus.Infof("user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "58" {
|
||||||
logrus.Panicf("user %q logged in.\n", username)
|
logrus.Panicf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
logrus.Printf("user %q logged in.\n", username)
|
logrus.Printf("user %q logged in.\n", username)
|
||||||
@@ -561,12 +665,12 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
entry := logrus.WithFields(fields)
|
entry := logrus.WithFields(fields)
|
||||||
entry.Debugf("user %q logged in.\n", username)
|
entry.Debugf("user %q logged in.\n", username)
|
||||||
entry.Errorf("user %q logged in.\n", username)
|
entry.Errorf("user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "59" {
|
||||||
entry.Fatalf("user %q logged in.\n", username)
|
entry.Fatalf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
entry.Infof("user %q logged in.\n", username)
|
entry.Infof("user %q logged in.\n", username)
|
||||||
entry.Logf(0, "user %q logged in.\n", username)
|
entry.Logf(0, "user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "60" {
|
||||||
entry.Panicf("user %q logged in.\n", username)
|
entry.Panicf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
entry.Printf("user %q logged in.\n", username)
|
entry.Printf("user %q logged in.\n", username)
|
||||||
@@ -577,12 +681,12 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
logger := entry.Logger
|
logger := entry.Logger
|
||||||
logger.Debugf("user %q logged in.\n", username)
|
logger.Debugf("user %q logged in.\n", username)
|
||||||
logger.Errorf("user %q logged in.\n", username)
|
logger.Errorf("user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "61" {
|
||||||
logger.Fatalf("user %q logged in.\n", username)
|
logger.Fatalf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
logger.Infof("user %q logged in.\n", username)
|
logger.Infof("user %q logged in.\n", username)
|
||||||
logger.Logf(0, "user %q logged in.\n", username)
|
logger.Logf(0, "user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "62" {
|
||||||
logger.Panicf("user %q logged in.\n", username)
|
logger.Panicf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
logger.Printf("user %q logged in.\n", username)
|
logger.Printf("user %q logged in.\n", username)
|
||||||
@@ -603,11 +707,11 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
sLogger.DPanicf("user %q logged in.\n", username)
|
sLogger.DPanicf("user %q logged in.\n", username)
|
||||||
sLogger.Debugf("user %q logged in.\n", username)
|
sLogger.Debugf("user %q logged in.\n", username)
|
||||||
sLogger.Errorf("user %q logged in.\n", username)
|
sLogger.Errorf("user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "63" {
|
||||||
sLogger.Fatalf("user %q logged in.\n", username)
|
sLogger.Fatalf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
sLogger.Infof("user %q logged in.\n", username)
|
sLogger.Infof("user %q logged in.\n", username)
|
||||||
if testFlag == " true" {
|
if testFlag == "64" {
|
||||||
sLogger.Panicf("user %q logged in.\n", username)
|
sLogger.Panicf("user %q logged in.\n", username)
|
||||||
}
|
}
|
||||||
sLogger.Warnf("user %q logged in.\n", username)
|
sLogger.Warnf("user %q logged in.\n", username)
|
||||||
@@ -620,10 +724,10 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
verbose.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
verbose.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
klog.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
klog.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
klog.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
klog.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "65" {
|
||||||
klog.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
klog.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
if testFlag == " true" {
|
if testFlag == "66" {
|
||||||
klog.Exitf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
klog.Exitf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -639,10 +743,10 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
|
|
||||||
glog.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
glog.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
glog.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
glog.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "67" {
|
||||||
glog.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
glog.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
if testFlag == " true" {
|
if testFlag == "68" {
|
||||||
glog.Exitf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
glog.Exitf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -650,11 +754,11 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
{
|
{
|
||||||
logrus.Debugf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logrus.Debugf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
logrus.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logrus.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "69" {
|
||||||
logrus.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logrus.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
logrus.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logrus.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "70" {
|
||||||
logrus.Panicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logrus.Panicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
logrus.Printf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logrus.Printf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
@@ -666,12 +770,12 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
entry := logrus.WithFields(fields)
|
entry := logrus.WithFields(fields)
|
||||||
entry.Debugf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
entry.Debugf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
entry.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
entry.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "71" {
|
||||||
entry.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
entry.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
entry.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
entry.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
entry.Logf(0, "user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
entry.Logf(0, "user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "72" {
|
||||||
entry.Panicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
entry.Panicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
entry.Printf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
entry.Printf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
@@ -682,12 +786,12 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
logger := entry.Logger
|
logger := entry.Logger
|
||||||
logger.Debugf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logger.Debugf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
logger.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logger.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "73" {
|
||||||
logger.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logger.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
logger.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logger.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
logger.Logf(0, "user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logger.Logf(0, "user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "74" {
|
||||||
logger.Panicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logger.Panicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
logger.Printf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
logger.Printf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
@@ -708,11 +812,11 @@ func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
|||||||
sLogger.DPanicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
sLogger.DPanicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
sLogger.Debugf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
sLogger.Debugf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
sLogger.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
sLogger.Errorf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "75" {
|
||||||
sLogger.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
sLogger.Fatalf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
sLogger.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
sLogger.Infof("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
if testFlag == " true" {
|
if testFlag == "76" {
|
||||||
sLogger.Panicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
sLogger.Panicf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
}
|
}
|
||||||
sLogger.Warnf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
sLogger.Warnf("user %#q logged in.\n", username) // $ hasTaintFlow="username"
|
||||||
|
|||||||
@@ -37,22 +37,22 @@
|
|||||||
| passwords.go:26:14:26:23 | selection of password | passwords.go:26:14:26:23 | selection of password | passwords.go:26:14:26:23 | selection of password | $@ flows to a logging call. | passwords.go:26:14:26:23 | selection of password | Sensitive data returned by an access to password |
|
| passwords.go:26:14:26:23 | selection of password | passwords.go:26:14:26:23 | selection of password | passwords.go:26:14:26:23 | selection of password | $@ flows to a logging call. | passwords.go:26:14:26:23 | selection of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:27:14:27:26 | call to getPassword | passwords.go:27:14:27:26 | call to getPassword | passwords.go:27:14:27:26 | call to getPassword | $@ flows to a logging call. | passwords.go:27:14:27:26 | call to getPassword | Sensitive data returned by a call to getPassword |
|
| passwords.go:27:14:27:26 | call to getPassword | passwords.go:27:14:27:26 | call to getPassword | passwords.go:27:14:27:26 | call to getPassword | $@ flows to a logging call. | passwords.go:27:14:27:26 | call to getPassword | Sensitive data returned by a call to getPassword |
|
||||||
| passwords.go:28:14:28:28 | call to getPassword | passwords.go:28:14:28:28 | call to getPassword | passwords.go:28:14:28:28 | call to getPassword | $@ flows to a logging call. | passwords.go:28:14:28:28 | call to getPassword | Sensitive data returned by a call to getPassword |
|
| passwords.go:28:14:28:28 | call to getPassword | passwords.go:28:14:28:28 | call to getPassword | passwords.go:28:14:28:28 | call to getPassword | $@ flows to a logging call. | passwords.go:28:14:28:28 | call to getPassword | Sensitive data returned by a call to getPassword |
|
||||||
| passwords.go:32:12:32:19 | password | passwords.go:21:2:21:9 | definition of password | passwords.go:32:12:32:19 | password | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:33:13:33:20 | password | passwords.go:21:2:21:9 | definition of password | passwords.go:33:13:33:20 | password | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:34:14:34:35 | ...+... | passwords.go:21:2:21:9 | definition of password | passwords.go:34:14:34:35 | ...+... | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:36:14:36:35 | ...+... | passwords.go:21:2:21:9 | definition of password | passwords.go:36:14:36:35 | ...+... | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:39:14:39:17 | obj1 | passwords.go:37:13:37:13 | x | passwords.go:39:14:39:17 | obj1 | $@ flows to a logging call. | passwords.go:37:13:37:13 | x | Sensitive data returned by an access to password |
|
| passwords.go:41:14:41:17 | obj1 | passwords.go:39:13:39:13 | x | passwords.go:41:14:41:17 | obj1 | $@ flows to a logging call. | passwords.go:39:13:39:13 | x | Sensitive data returned by an access to password |
|
||||||
| passwords.go:44:14:44:17 | obj2 | passwords.go:21:2:21:9 | definition of password | passwords.go:44:14:44:17 | obj2 | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:46:14:46:17 | obj2 | passwords.go:21:2:21:9 | definition of password | passwords.go:46:14:46:17 | obj2 | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:51:14:51:27 | fixed_password | passwords.go:50:2:50:15 | definition of fixed_password | passwords.go:51:14:51:27 | fixed_password | $@ flows to a logging call. | passwords.go:50:2:50:15 | definition of fixed_password | Sensitive data returned by an access to fixed_password |
|
| passwords.go:53:14:53:27 | fixed_password | passwords.go:52:2:52:15 | definition of fixed_password | passwords.go:53:14:53:27 | fixed_password | $@ flows to a logging call. | passwords.go:52:2:52:15 | definition of fixed_password | Sensitive data returned by an access to fixed_password |
|
||||||
| passwords.go:89:14:89:26 | utilityObject | passwords.go:87:16:87:36 | call to make | passwords.go:89:14:89:26 | utilityObject | $@ flows to a logging call. | passwords.go:87:16:87:36 | call to make | Sensitive data returned by an access to passwordSet |
|
| passwords.go:91:14:91:26 | utilityObject | passwords.go:89:16:89:36 | call to make | passwords.go:91:14:91:26 | utilityObject | $@ flows to a logging call. | passwords.go:89:16:89:36 | call to make | Sensitive data returned by an access to passwordSet |
|
||||||
| passwords.go:92:23:92:28 | secret | passwords.go:21:2:21:9 | definition of password | passwords.go:92:23:92:28 | secret | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:94:23:94:28 | secret | passwords.go:21:2:21:9 | definition of password | passwords.go:94:23:94:28 | secret | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:102:15:102:40 | ...+... | passwords.go:21:2:21:9 | definition of password | passwords.go:102:15:102:40 | ...+... | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:104:15:104:40 | ...+... | passwords.go:21:2:21:9 | definition of password | passwords.go:104:15:104:40 | ...+... | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:108:16:108:41 | ...+... | passwords.go:21:2:21:9 | definition of password | passwords.go:108:16:108:41 | ...+... | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:110:16:110:41 | ...+... | passwords.go:21:2:21:9 | definition of password | passwords.go:110:16:110:41 | ...+... | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:113:15:113:40 | ...+... | passwords.go:21:2:21:9 | definition of password | passwords.go:113:15:113:40 | ...+... | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:115:15:115:40 | ...+... | passwords.go:21:2:21:9 | definition of password | passwords.go:115:15:115:40 | ...+... | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:117:14:117:45 | ...+... | passwords.go:116:6:116:14 | definition of password1 | passwords.go:117:14:117:45 | ...+... | $@ flows to a logging call. | passwords.go:116:6:116:14 | definition of password1 | Sensitive data returned by an access to password1 |
|
| passwords.go:119:14:119:45 | ...+... | passwords.go:118:6:118:14 | definition of password1 | passwords.go:119:14:119:45 | ...+... | $@ flows to a logging call. | passwords.go:118:6:118:14 | definition of password1 | Sensitive data returned by an access to password1 |
|
||||||
| passwords.go:127:14:127:19 | config | passwords.go:21:2:21:9 | definition of password | passwords.go:127:14:127:19 | config | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:129:14:129:19 | config | passwords.go:21:2:21:9 | definition of password | passwords.go:129:14:129:19 | config | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:127:14:127:19 | config | passwords.go:121:13:121:14 | x3 | passwords.go:127:14:127:19 | config | $@ flows to a logging call. | passwords.go:121:13:121:14 | x3 | Sensitive data returned by an access to password |
|
| passwords.go:129:14:129:19 | config | passwords.go:123:13:123:14 | x3 | passwords.go:129:14:129:19 | config | $@ flows to a logging call. | passwords.go:123:13:123:14 | x3 | Sensitive data returned by an access to password |
|
||||||
| passwords.go:127:14:127:19 | config | passwords.go:124:13:124:25 | call to getPassword | passwords.go:127:14:127:19 | config | $@ flows to a logging call. | passwords.go:124:13:124:25 | call to getPassword | Sensitive data returned by a call to getPassword |
|
| passwords.go:129:14:129:19 | config | passwords.go:126:13:126:25 | call to getPassword | passwords.go:129:14:129:19 | config | $@ flows to a logging call. | passwords.go:126:13:126:25 | call to getPassword | Sensitive data returned by a call to getPassword |
|
||||||
| passwords.go:128:14:128:21 | selection of x | passwords.go:21:2:21:9 | definition of password | passwords.go:128:14:128:21 | selection of x | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
| passwords.go:130:14:130:21 | selection of x | passwords.go:21:2:21:9 | definition of password | passwords.go:130:14:130:21 | selection of x | $@ flows to a logging call. | passwords.go:21:2:21:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
| passwords.go:129:14:129:21 | selection of y | passwords.go:124:13:124:25 | call to getPassword | passwords.go:129:14:129:21 | selection of y | $@ flows to a logging call. | passwords.go:124:13:124:25 | call to getPassword | Sensitive data returned by a call to getPassword |
|
| passwords.go:131:14:131:21 | selection of y | passwords.go:126:13:126:25 | call to getPassword | passwords.go:131:14:131:21 | selection of y | $@ flows to a logging call. | passwords.go:126:13:126:25 | call to getPassword | Sensitive data returned by a call to getPassword |
|
||||||
| protobuf.go:14:14:14:35 | call to GetDescription | protobuf.go:9:2:9:9 | definition of password | protobuf.go:14:14:14:35 | call to GetDescription | $@ flows to a logging call. | protobuf.go:9:2:9:9 | definition of password | Sensitive data returned by an access to password |
|
| protobuf.go:14:14:14:35 | call to GetDescription | protobuf.go:9:2:9:9 | definition of password | protobuf.go:14:14:14:35 | call to GetDescription | $@ flows to a logging call. | protobuf.go:9:2:9:9 | definition of password | Sensitive data returned by an access to password |
|
||||||
edges
|
edges
|
||||||
| klog.go:21:3:26:3 | range statement[1] | klog.go:22:27:22:33 | headers | provenance | |
|
| klog.go:21:3:26:3 | range statement[1] | klog.go:22:27:22:33 | headers | provenance | |
|
||||||
@@ -82,95 +82,15 @@ edges
|
|||||||
| main.go:53:11:53:18 | password | main.go:54:12:54:19 | password | provenance | |
|
| main.go:53:11:53:18 | password | main.go:54:12:54:19 | password | provenance | |
|
||||||
| main.go:53:11:53:18 | password | main.go:54:12:54:19 | password | provenance | |
|
| main.go:53:11:53:18 | password | main.go:54:12:54:19 | password | provenance | |
|
||||||
| main.go:54:12:54:19 | password | main.go:56:11:56:18 | password | provenance | |
|
| main.go:54:12:54:19 | password | main.go:56:11:56:18 | password | provenance | |
|
||||||
| main.go:54:12:54:19 | password | main.go:56:11:56:18 | password | provenance | |
|
|
||||||
| main.go:54:12:54:19 | password | main.go:59:18:59:25 | password | provenance | |
|
| main.go:54:12:54:19 | password | main.go:59:18:59:25 | password | provenance | |
|
||||||
| main.go:54:12:54:19 | password | main.go:59:18:59:25 | password | provenance | |
|
|
||||||
| main.go:54:12:54:19 | password | main.go:62:12:62:19 | password | provenance | |
|
|
||||||
| main.go:54:12:54:19 | password | main.go:62:12:62:19 | password | provenance | Sink:MaD:7 |
|
| main.go:54:12:54:19 | password | main.go:62:12:62:19 | password | provenance | Sink:MaD:7 |
|
||||||
| main.go:54:12:54:19 | password | main.go:65:13:65:20 | password | provenance | |
|
| main.go:54:12:54:19 | password | main.go:65:13:65:20 | password | provenance | |
|
||||||
| main.go:54:12:54:19 | password | main.go:65:13:65:20 | password | provenance | |
|
|
||||||
| main.go:54:12:54:19 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:54:12:54:19 | password | main.go:68:11:68:18 | password | provenance | |
|
| main.go:54:12:54:19 | password | main.go:68:11:68:18 | password | provenance | |
|
||||||
| main.go:54:12:54:19 | password | main.go:71:18:71:25 | password | provenance | |
|
| main.go:54:12:54:19 | password | main.go:71:18:71:25 | password | provenance | |
|
||||||
| main.go:54:12:54:19 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:54:12:54:19 | password | main.go:74:12:74:19 | password | provenance | |
|
|
||||||
| main.go:54:12:54:19 | password | main.go:74:12:74:19 | password | provenance | Sink:MaD:9 |
|
| main.go:54:12:54:19 | password | main.go:74:12:74:19 | password | provenance | Sink:MaD:9 |
|
||||||
| main.go:54:12:54:19 | password | main.go:77:13:77:20 | password | provenance | |
|
| main.go:54:12:54:19 | password | main.go:77:13:77:20 | password | provenance | |
|
||||||
| main.go:54:12:54:19 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:54:12:54:19 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
| main.go:54:12:54:19 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
||||||
| main.go:54:12:54:19 | password | main.go:80:17:80:24 | password | provenance | |
|
| main.go:54:12:54:19 | password | main.go:80:17:80:24 | password | provenance | |
|
||||||
| main.go:56:11:56:18 | password | main.go:59:18:59:25 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:59:18:59:25 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:62:12:62:19 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:62:12:62:19 | password | provenance | Sink:MaD:7 |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:65:13:65:20 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:65:13:65:20 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:74:12:74:19 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:74:12:74:19 | password | provenance | Sink:MaD:9 |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
|
||||||
| main.go:56:11:56:18 | password | main.go:80:17:80:24 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:62:12:62:19 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:62:12:62:19 | password | provenance | Sink:MaD:7 |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:65:13:65:20 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:65:13:65:20 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:74:12:74:19 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:74:12:74:19 | password | provenance | Sink:MaD:9 |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
|
||||||
| main.go:59:18:59:25 | password | main.go:80:17:80:24 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:65:13:65:20 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:65:13:65:20 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:74:12:74:19 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:74:12:74:19 | password | provenance | Sink:MaD:9 |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
|
||||||
| main.go:62:12:62:19 | password | main.go:80:17:80:24 | password | provenance | |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:68:11:68:18 | password | provenance | |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:74:12:74:19 | password | provenance | |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:74:12:74:19 | password | provenance | Sink:MaD:9 |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
|
||||||
| main.go:65:13:65:20 | password | main.go:80:17:80:24 | password | provenance | |
|
|
||||||
| main.go:68:11:68:18 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:68:11:68:18 | password | main.go:71:18:71:25 | password | provenance | |
|
|
||||||
| main.go:68:11:68:18 | password | main.go:74:12:74:19 | password | provenance | |
|
|
||||||
| main.go:68:11:68:18 | password | main.go:74:12:74:19 | password | provenance | Sink:MaD:9 |
|
|
||||||
| main.go:68:11:68:18 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:68:11:68:18 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:68:11:68:18 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
|
||||||
| main.go:68:11:68:18 | password | main.go:80:17:80:24 | password | provenance | |
|
|
||||||
| main.go:71:18:71:25 | password | main.go:74:12:74:19 | password | provenance | |
|
|
||||||
| main.go:71:18:71:25 | password | main.go:74:12:74:19 | password | provenance | Sink:MaD:9 |
|
|
||||||
| main.go:71:18:71:25 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:71:18:71:25 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:71:18:71:25 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
|
||||||
| main.go:71:18:71:25 | password | main.go:80:17:80:24 | password | provenance | |
|
|
||||||
| main.go:74:12:74:19 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:74:12:74:19 | password | main.go:77:13:77:20 | password | provenance | |
|
|
||||||
| main.go:74:12:74:19 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
|
||||||
| main.go:74:12:74:19 | password | main.go:80:17:80:24 | password | provenance | |
|
|
||||||
| main.go:77:13:77:20 | password | main.go:79:14:79:21 | password | provenance | Sink:MaD:8 |
|
|
||||||
| main.go:77:13:77:20 | password | main.go:80:17:80:24 | password | provenance | |
|
|
||||||
| main.go:80:17:80:24 | password | main.go:82:12:82:19 | password | provenance | |
|
| main.go:80:17:80:24 | password | main.go:82:12:82:19 | password | provenance | |
|
||||||
| main.go:80:17:80:24 | password | main.go:83:17:83:24 | password | provenance | |
|
| main.go:80:17:80:24 | password | main.go:83:17:83:24 | password | provenance | |
|
||||||
| main.go:80:17:80:24 | password | main.go:86:19:86:26 | password | provenance | |
|
| main.go:80:17:80:24 | password | main.go:86:19:86:26 | password | provenance | |
|
||||||
@@ -182,46 +102,46 @@ edges
|
|||||||
| passwords.go:8:12:8:12 | definition of x | passwords.go:9:14:9:14 | x | provenance | |
|
| passwords.go:8:12:8:12 | definition of x | passwords.go:9:14:9:14 | x | provenance | |
|
||||||
| passwords.go:21:2:21:9 | definition of password | passwords.go:25:14:25:21 | password | provenance | |
|
| passwords.go:21:2:21:9 | definition of password | passwords.go:25:14:25:21 | password | provenance | |
|
||||||
| passwords.go:21:2:21:9 | definition of password | passwords.go:30:8:30:15 | password | provenance | |
|
| passwords.go:21:2:21:9 | definition of password | passwords.go:30:8:30:15 | password | provenance | |
|
||||||
| passwords.go:21:2:21:9 | definition of password | passwords.go:32:12:32:19 | password | provenance | |
|
| passwords.go:21:2:21:9 | definition of password | passwords.go:33:13:33:20 | password | provenance | |
|
||||||
| passwords.go:21:2:21:9 | definition of password | passwords.go:34:28:34:35 | password | provenance | |
|
| passwords.go:21:2:21:9 | definition of password | passwords.go:36:28:36:35 | password | provenance | |
|
||||||
| passwords.go:30:8:30:15 | password | passwords.go:8:12:8:12 | definition of x | provenance | |
|
| passwords.go:30:8:30:15 | password | passwords.go:8:12:8:12 | definition of x | provenance | |
|
||||||
| passwords.go:34:28:34:35 | password | passwords.go:34:14:34:35 | ...+... | provenance | Config |
|
| passwords.go:36:28:36:35 | password | passwords.go:36:14:36:35 | ...+... | provenance | Config |
|
||||||
| passwords.go:34:28:34:35 | password | passwords.go:42:6:42:13 | password | provenance | |
|
| passwords.go:36:28:36:35 | password | passwords.go:44:6:44:13 | password | provenance | |
|
||||||
| passwords.go:36:10:38:2 | struct literal | passwords.go:39:14:39:17 | obj1 | provenance | |
|
| passwords.go:38:10:40:2 | struct literal | passwords.go:41:14:41:17 | obj1 | provenance | |
|
||||||
| passwords.go:37:13:37:13 | x | passwords.go:36:10:38:2 | struct literal | provenance | Config |
|
| passwords.go:39:13:39:13 | x | passwords.go:38:10:40:2 | struct literal | provenance | Config |
|
||||||
| passwords.go:41:10:43:2 | struct literal | passwords.go:44:14:44:17 | obj2 | provenance | |
|
| passwords.go:43:10:45:2 | struct literal | passwords.go:46:14:46:17 | obj2 | provenance | |
|
||||||
| passwords.go:42:6:42:13 | password | passwords.go:41:10:43:2 | struct literal | provenance | Config |
|
| passwords.go:44:6:44:13 | password | passwords.go:43:10:45:2 | struct literal | provenance | Config |
|
||||||
| passwords.go:42:6:42:13 | password | passwords.go:48:11:48:18 | password | provenance | |
|
| passwords.go:44:6:44:13 | password | passwords.go:50:11:50:18 | password | provenance | |
|
||||||
| passwords.go:48:11:48:18 | password | passwords.go:92:23:92:28 | secret | provenance | |
|
| passwords.go:50:11:50:18 | password | passwords.go:94:23:94:28 | secret | provenance | |
|
||||||
| passwords.go:48:11:48:18 | password | passwords.go:102:33:102:40 | password | provenance | |
|
| passwords.go:50:11:50:18 | password | passwords.go:104:33:104:40 | password | provenance | |
|
||||||
| passwords.go:48:11:48:18 | password | passwords.go:108:34:108:41 | password | provenance | |
|
| passwords.go:50:11:50:18 | password | passwords.go:110:34:110:41 | password | provenance | |
|
||||||
| passwords.go:48:11:48:18 | password | passwords.go:113:33:113:40 | password | provenance | |
|
| passwords.go:50:11:50:18 | password | passwords.go:115:33:115:40 | password | provenance | |
|
||||||
| passwords.go:48:11:48:18 | password | passwords.go:123:13:123:20 | password | provenance | |
|
| passwords.go:50:11:50:18 | password | passwords.go:125:13:125:20 | password | provenance | |
|
||||||
| passwords.go:50:2:50:15 | definition of fixed_password | passwords.go:51:14:51:27 | fixed_password | provenance | |
|
| passwords.go:52:2:52:15 | definition of fixed_password | passwords.go:53:14:53:27 | fixed_password | provenance | |
|
||||||
| passwords.go:86:19:88:2 | struct literal | passwords.go:89:14:89:26 | utilityObject | provenance | |
|
| passwords.go:88:19:90:2 | struct literal | passwords.go:91:14:91:26 | utilityObject | provenance | |
|
||||||
| passwords.go:87:16:87:36 | call to make | passwords.go:86:19:88:2 | struct literal | provenance | Config |
|
| passwords.go:89:16:89:36 | call to make | passwords.go:88:19:90:2 | struct literal | provenance | Config |
|
||||||
| passwords.go:102:33:102:40 | password | passwords.go:102:15:102:40 | ...+... | provenance | Config |
|
| passwords.go:104:33:104:40 | password | passwords.go:104:15:104:40 | ...+... | provenance | Config |
|
||||||
| passwords.go:102:33:102:40 | password | passwords.go:108:34:108:41 | password | provenance | |
|
| passwords.go:104:33:104:40 | password | passwords.go:110:34:110:41 | password | provenance | |
|
||||||
| passwords.go:102:33:102:40 | password | passwords.go:113:33:113:40 | password | provenance | |
|
| passwords.go:104:33:104:40 | password | passwords.go:115:33:115:40 | password | provenance | |
|
||||||
| passwords.go:102:33:102:40 | password | passwords.go:123:13:123:20 | password | provenance | |
|
| passwords.go:104:33:104:40 | password | passwords.go:125:13:125:20 | password | provenance | |
|
||||||
| passwords.go:108:34:108:41 | password | passwords.go:108:16:108:41 | ...+... | provenance | Config |
|
| passwords.go:110:34:110:41 | password | passwords.go:110:16:110:41 | ...+... | provenance | Config |
|
||||||
| passwords.go:108:34:108:41 | password | passwords.go:113:33:113:40 | password | provenance | |
|
| passwords.go:110:34:110:41 | password | passwords.go:115:33:115:40 | password | provenance | |
|
||||||
| passwords.go:108:34:108:41 | password | passwords.go:123:13:123:20 | password | provenance | |
|
| passwords.go:110:34:110:41 | password | passwords.go:125:13:125:20 | password | provenance | |
|
||||||
| passwords.go:113:33:113:40 | password | passwords.go:113:15:113:40 | ...+... | provenance | Config |
|
| passwords.go:115:33:115:40 | password | passwords.go:115:15:115:40 | ...+... | provenance | Config |
|
||||||
| passwords.go:113:33:113:40 | password | passwords.go:123:13:123:20 | password | provenance | |
|
| passwords.go:115:33:115:40 | password | passwords.go:125:13:125:20 | password | provenance | |
|
||||||
| passwords.go:116:6:116:14 | definition of password1 | passwords.go:117:28:117:36 | password1 | provenance | |
|
| passwords.go:118:6:118:14 | definition of password1 | passwords.go:119:28:119:36 | password1 | provenance | |
|
||||||
| passwords.go:117:28:117:36 | password1 | passwords.go:117:28:117:45 | call to String | provenance | Config |
|
| passwords.go:119:28:119:36 | password1 | passwords.go:119:28:119:45 | call to String | provenance | Config |
|
||||||
| passwords.go:117:28:117:45 | call to String | passwords.go:117:14:117:45 | ...+... | provenance | Config |
|
| passwords.go:119:28:119:45 | call to String | passwords.go:119:14:119:45 | ...+... | provenance | Config |
|
||||||
| passwords.go:120:12:125:2 | struct literal | passwords.go:127:14:127:19 | config | provenance | |
|
| passwords.go:122:12:127:2 | struct literal | passwords.go:129:14:129:19 | config | provenance | |
|
||||||
| passwords.go:120:12:125:2 | struct literal [x] | passwords.go:128:14:128:19 | config [x] | provenance | |
|
| passwords.go:122:12:127:2 | struct literal [x] | passwords.go:130:14:130:19 | config [x] | provenance | |
|
||||||
| passwords.go:120:12:125:2 | struct literal [y] | passwords.go:129:14:129:19 | config [y] | provenance | |
|
| passwords.go:122:12:127:2 | struct literal [y] | passwords.go:131:14:131:19 | config [y] | provenance | |
|
||||||
| passwords.go:121:13:121:14 | x3 | passwords.go:120:12:125:2 | struct literal | provenance | Config |
|
| passwords.go:123:13:123:14 | x3 | passwords.go:122:12:127:2 | struct literal | provenance | Config |
|
||||||
| passwords.go:123:13:123:20 | password | passwords.go:120:12:125:2 | struct literal | provenance | Config |
|
| passwords.go:125:13:125:20 | password | passwords.go:122:12:127:2 | struct literal | provenance | Config |
|
||||||
| passwords.go:123:13:123:20 | password | passwords.go:120:12:125:2 | struct literal [x] | provenance | |
|
| passwords.go:125:13:125:20 | password | passwords.go:122:12:127:2 | struct literal [x] | provenance | |
|
||||||
| passwords.go:124:13:124:25 | call to getPassword | passwords.go:120:12:125:2 | struct literal | provenance | Config |
|
| passwords.go:126:13:126:25 | call to getPassword | passwords.go:122:12:127:2 | struct literal | provenance | Config |
|
||||||
| passwords.go:124:13:124:25 | call to getPassword | passwords.go:120:12:125:2 | struct literal [y] | provenance | |
|
| passwords.go:126:13:126:25 | call to getPassword | passwords.go:122:12:127:2 | struct literal [y] | provenance | |
|
||||||
| passwords.go:128:14:128:19 | config [x] | passwords.go:128:14:128:21 | selection of x | provenance | |
|
| passwords.go:130:14:130:19 | config [x] | passwords.go:130:14:130:21 | selection of x | provenance | |
|
||||||
| passwords.go:129:14:129:19 | config [y] | passwords.go:129:14:129:21 | selection of y | provenance | |
|
| passwords.go:131:14:131:19 | config [y] | passwords.go:131:14:131:21 | selection of y | provenance | |
|
||||||
| protobuf.go:9:2:9:9 | definition of password | protobuf.go:12:22:12:29 | password | provenance | |
|
| protobuf.go:9:2:9:9 | definition of password | protobuf.go:12:22:12:29 | password | provenance | |
|
||||||
| protobuf.go:12:2:12:6 | implicit dereference [postupdate] [Description] | protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | provenance | |
|
| protobuf.go:12:2:12:6 | implicit dereference [postupdate] [Description] | protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | provenance | |
|
||||||
| protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | protobuf.go:14:14:14:18 | query [pointer, Description] | provenance | |
|
| protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | protobuf.go:14:14:14:18 | query [pointer, Description] | provenance | |
|
||||||
@@ -274,20 +194,12 @@ nodes
|
|||||||
| main.go:54:12:54:19 | password | semmle.label | password |
|
| main.go:54:12:54:19 | password | semmle.label | password |
|
||||||
| main.go:54:12:54:19 | password | semmle.label | password |
|
| main.go:54:12:54:19 | password | semmle.label | password |
|
||||||
| main.go:56:11:56:18 | password | semmle.label | password |
|
| main.go:56:11:56:18 | password | semmle.label | password |
|
||||||
| main.go:56:11:56:18 | password | semmle.label | password |
|
|
||||||
| main.go:59:18:59:25 | password | semmle.label | password |
|
|
||||||
| main.go:59:18:59:25 | password | semmle.label | password |
|
| main.go:59:18:59:25 | password | semmle.label | password |
|
||||||
| main.go:62:12:62:19 | password | semmle.label | password |
|
| main.go:62:12:62:19 | password | semmle.label | password |
|
||||||
| main.go:62:12:62:19 | password | semmle.label | password |
|
|
||||||
| main.go:65:13:65:20 | password | semmle.label | password |
|
|
||||||
| main.go:65:13:65:20 | password | semmle.label | password |
|
| main.go:65:13:65:20 | password | semmle.label | password |
|
||||||
| main.go:68:11:68:18 | password | semmle.label | password |
|
| main.go:68:11:68:18 | password | semmle.label | password |
|
||||||
| main.go:68:11:68:18 | password | semmle.label | password |
|
|
||||||
| main.go:71:18:71:25 | password | semmle.label | password |
|
|
||||||
| main.go:71:18:71:25 | password | semmle.label | password |
|
| main.go:71:18:71:25 | password | semmle.label | password |
|
||||||
| main.go:74:12:74:19 | password | semmle.label | password |
|
| main.go:74:12:74:19 | password | semmle.label | password |
|
||||||
| main.go:74:12:74:19 | password | semmle.label | password |
|
|
||||||
| main.go:77:13:77:20 | password | semmle.label | password |
|
|
||||||
| main.go:77:13:77:20 | password | semmle.label | password |
|
| main.go:77:13:77:20 | password | semmle.label | password |
|
||||||
| main.go:79:14:79:21 | password | semmle.label | password |
|
| main.go:79:14:79:21 | password | semmle.label | password |
|
||||||
| main.go:80:17:80:24 | password | semmle.label | password |
|
| main.go:80:17:80:24 | password | semmle.label | password |
|
||||||
@@ -308,43 +220,43 @@ nodes
|
|||||||
| passwords.go:27:14:27:26 | call to getPassword | semmle.label | call to getPassword |
|
| passwords.go:27:14:27:26 | call to getPassword | semmle.label | call to getPassword |
|
||||||
| passwords.go:28:14:28:28 | call to getPassword | semmle.label | call to getPassword |
|
| passwords.go:28:14:28:28 | call to getPassword | semmle.label | call to getPassword |
|
||||||
| passwords.go:30:8:30:15 | password | semmle.label | password |
|
| passwords.go:30:8:30:15 | password | semmle.label | password |
|
||||||
| passwords.go:32:12:32:19 | password | semmle.label | password |
|
| passwords.go:33:13:33:20 | password | semmle.label | password |
|
||||||
| passwords.go:34:14:34:35 | ...+... | semmle.label | ...+... |
|
| passwords.go:36:14:36:35 | ...+... | semmle.label | ...+... |
|
||||||
| passwords.go:34:28:34:35 | password | semmle.label | password |
|
| passwords.go:36:28:36:35 | password | semmle.label | password |
|
||||||
| passwords.go:36:10:38:2 | struct literal | semmle.label | struct literal |
|
| passwords.go:38:10:40:2 | struct literal | semmle.label | struct literal |
|
||||||
| passwords.go:37:13:37:13 | x | semmle.label | x |
|
| passwords.go:39:13:39:13 | x | semmle.label | x |
|
||||||
| passwords.go:39:14:39:17 | obj1 | semmle.label | obj1 |
|
| passwords.go:41:14:41:17 | obj1 | semmle.label | obj1 |
|
||||||
| passwords.go:41:10:43:2 | struct literal | semmle.label | struct literal |
|
| passwords.go:43:10:45:2 | struct literal | semmle.label | struct literal |
|
||||||
| passwords.go:42:6:42:13 | password | semmle.label | password |
|
| passwords.go:44:6:44:13 | password | semmle.label | password |
|
||||||
| passwords.go:44:14:44:17 | obj2 | semmle.label | obj2 |
|
| passwords.go:46:14:46:17 | obj2 | semmle.label | obj2 |
|
||||||
| passwords.go:48:11:48:18 | password | semmle.label | password |
|
| passwords.go:50:11:50:18 | password | semmle.label | password |
|
||||||
| passwords.go:50:2:50:15 | definition of fixed_password | semmle.label | definition of fixed_password |
|
| passwords.go:52:2:52:15 | definition of fixed_password | semmle.label | definition of fixed_password |
|
||||||
| passwords.go:51:14:51:27 | fixed_password | semmle.label | fixed_password |
|
| passwords.go:53:14:53:27 | fixed_password | semmle.label | fixed_password |
|
||||||
| passwords.go:86:19:88:2 | struct literal | semmle.label | struct literal |
|
| passwords.go:88:19:90:2 | struct literal | semmle.label | struct literal |
|
||||||
| passwords.go:87:16:87:36 | call to make | semmle.label | call to make |
|
| passwords.go:89:16:89:36 | call to make | semmle.label | call to make |
|
||||||
| passwords.go:89:14:89:26 | utilityObject | semmle.label | utilityObject |
|
| passwords.go:91:14:91:26 | utilityObject | semmle.label | utilityObject |
|
||||||
| passwords.go:92:23:92:28 | secret | semmle.label | secret |
|
| passwords.go:94:23:94:28 | secret | semmle.label | secret |
|
||||||
| passwords.go:102:15:102:40 | ...+... | semmle.label | ...+... |
|
| passwords.go:104:15:104:40 | ...+... | semmle.label | ...+... |
|
||||||
| passwords.go:102:33:102:40 | password | semmle.label | password |
|
| passwords.go:104:33:104:40 | password | semmle.label | password |
|
||||||
| passwords.go:108:16:108:41 | ...+... | semmle.label | ...+... |
|
| passwords.go:110:16:110:41 | ...+... | semmle.label | ...+... |
|
||||||
| passwords.go:108:34:108:41 | password | semmle.label | password |
|
| passwords.go:110:34:110:41 | password | semmle.label | password |
|
||||||
| passwords.go:113:15:113:40 | ...+... | semmle.label | ...+... |
|
| passwords.go:115:15:115:40 | ...+... | semmle.label | ...+... |
|
||||||
| passwords.go:113:33:113:40 | password | semmle.label | password |
|
| passwords.go:115:33:115:40 | password | semmle.label | password |
|
||||||
| passwords.go:116:6:116:14 | definition of password1 | semmle.label | definition of password1 |
|
| passwords.go:118:6:118:14 | definition of password1 | semmle.label | definition of password1 |
|
||||||
| passwords.go:117:14:117:45 | ...+... | semmle.label | ...+... |
|
| passwords.go:119:14:119:45 | ...+... | semmle.label | ...+... |
|
||||||
| passwords.go:117:28:117:36 | password1 | semmle.label | password1 |
|
| passwords.go:119:28:119:36 | password1 | semmle.label | password1 |
|
||||||
| passwords.go:117:28:117:45 | call to String | semmle.label | call to String |
|
| passwords.go:119:28:119:45 | call to String | semmle.label | call to String |
|
||||||
| passwords.go:120:12:125:2 | struct literal | semmle.label | struct literal |
|
| passwords.go:122:12:127:2 | struct literal | semmle.label | struct literal |
|
||||||
| passwords.go:120:12:125:2 | struct literal [x] | semmle.label | struct literal [x] |
|
| passwords.go:122:12:127:2 | struct literal [x] | semmle.label | struct literal [x] |
|
||||||
| passwords.go:120:12:125:2 | struct literal [y] | semmle.label | struct literal [y] |
|
| passwords.go:122:12:127:2 | struct literal [y] | semmle.label | struct literal [y] |
|
||||||
| passwords.go:121:13:121:14 | x3 | semmle.label | x3 |
|
| passwords.go:123:13:123:14 | x3 | semmle.label | x3 |
|
||||||
| passwords.go:123:13:123:20 | password | semmle.label | password |
|
| passwords.go:125:13:125:20 | password | semmle.label | password |
|
||||||
| passwords.go:124:13:124:25 | call to getPassword | semmle.label | call to getPassword |
|
| passwords.go:126:13:126:25 | call to getPassword | semmle.label | call to getPassword |
|
||||||
| passwords.go:127:14:127:19 | config | semmle.label | config |
|
| passwords.go:129:14:129:19 | config | semmle.label | config |
|
||||||
| passwords.go:128:14:128:19 | config [x] | semmle.label | config [x] |
|
| passwords.go:130:14:130:19 | config [x] | semmle.label | config [x] |
|
||||||
| passwords.go:128:14:128:21 | selection of x | semmle.label | selection of x |
|
| passwords.go:130:14:130:21 | selection of x | semmle.label | selection of x |
|
||||||
| passwords.go:129:14:129:19 | config [y] | semmle.label | config [y] |
|
| passwords.go:131:14:131:19 | config [y] | semmle.label | config [y] |
|
||||||
| passwords.go:129:14:129:21 | selection of y | semmle.label | selection of y |
|
| passwords.go:131:14:131:21 | selection of y | semmle.label | selection of y |
|
||||||
| protobuf.go:9:2:9:9 | definition of password | semmle.label | definition of password |
|
| protobuf.go:9:2:9:9 | definition of password | semmle.label | definition of password |
|
||||||
| protobuf.go:12:2:12:6 | implicit dereference [postupdate] [Description] | semmle.label | implicit dereference [postupdate] [Description] |
|
| protobuf.go:12:2:12:6 | implicit dereference [postupdate] [Description] | semmle.label | implicit dereference [postupdate] [Description] |
|
||||||
| protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | semmle.label | query [postupdate] [pointer, Description] |
|
| protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | semmle.label | query [postupdate] [pointer, Description] |
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func redact(kind, value string) string {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func test() {
|
func test(selector int) {
|
||||||
name := "user"
|
name := "user"
|
||||||
password := "P@ssw0rd" // $ Source
|
password := "P@ssw0rd" // $ Source
|
||||||
x := "horsebatterystapleincorrect"
|
x := "horsebatterystapleincorrect"
|
||||||
@@ -29,7 +29,9 @@ func test() {
|
|||||||
|
|
||||||
myLog(password)
|
myLog(password)
|
||||||
|
|
||||||
log.Panic(password) // $ Alert
|
if selector == 1 {
|
||||||
|
log.Panic(password) // $ Alert
|
||||||
|
}
|
||||||
|
|
||||||
log.Println(name + ", " + password) // $ Alert
|
log.Println(name + ", " + password) // $ Alert
|
||||||
|
|
||||||
|
|||||||
2
python/ql/consistency-queries/CfgConsistency.ql
Normal file
2
python/ql/consistency-queries/CfgConsistency.ql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import semmle.python.controlflow.internal.AstNodeImpl
|
||||||
|
import ControlFlow::Consistency
|
||||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
|
|||||||
private import semmle.python.dataflow.new.internal.DataFlowDispatch
|
private import semmle.python.dataflow.new.internal.DataFlowDispatch
|
||||||
private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific
|
private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
|
|
||||||
private module Input implements InputSig<Location, PythonDataFlow> {
|
private module Input implements InputSig<Location, PythonDataFlow> {
|
||||||
private import Private
|
private import Private
|
||||||
@@ -72,7 +73,7 @@ private module Input implements InputSig<Location, PythonDataFlow> {
|
|||||||
// resolve to multiple functions), but we only make _one_ ArgumentNode for each
|
// 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
|
// argument in the CallNode, we end up violating this consistency check in those
|
||||||
// cases. (see `getCallArg` in DataFlowDispatch.qll)
|
// cases. (see `getCallArg` in DataFlowDispatch.qll)
|
||||||
exists(DataFlowCall other, CallNode cfgCall | other != call |
|
exists(DataFlowCall other, Cfg::CallNode cfgCall | other != call |
|
||||||
call.getNode() = cfgCall and
|
call.getNode() = cfgCall and
|
||||||
other.getNode() = cfgCall and
|
other.getNode() = cfgCall and
|
||||||
isArgumentNode(arg, call, _) and
|
isArgumentNode(arg, call, _) and
|
||||||
@@ -88,16 +89,16 @@ private module Input implements InputSig<Location, PythonDataFlow> {
|
|||||||
// allow it instead.
|
// allow it instead.
|
||||||
(
|
(
|
||||||
call.getScope() = attr.getScope() and
|
call.getScope() = attr.getScope() and
|
||||||
any(CfgNode n | n.asCfgNode() = call.getNode().(CallNode).getFunction()).getALocalSource() =
|
any(CfgNode n | n.asCfgNode() = call.getNode().(Cfg::CallNode).getFunction())
|
||||||
attr
|
.getALocalSource() = attr
|
||||||
or
|
or
|
||||||
not exists(call.getScope().(Function).getDefinition()) and
|
not exists(call.getScope().(Function).getDefinition()) and
|
||||||
call.getScope().getScope+() = attr.getScope()
|
call.getScope().getScope+() = attr.getScope()
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
other.getScope() = attr.getScope() and
|
other.getScope() = attr.getScope() and
|
||||||
any(CfgNode n | n.asCfgNode() = other.getNode().(CallNode).getFunction()).getALocalSource() =
|
any(CfgNode n | n.asCfgNode() = other.getNode().(Cfg::CallNode).getFunction())
|
||||||
attr
|
.getALocalSource() = attr
|
||||||
or
|
or
|
||||||
not exists(other.getScope().(Function).getDefinition()) and
|
not exists(other.getScope().(Function).getDefinition()) and
|
||||||
other.getScope().getScope+() = attr.getScope()
|
other.getScope().getScope+() = attr.getScope()
|
||||||
|
|||||||
@@ -213,9 +213,11 @@ class ExprWithPointsTo extends Expr {
|
|||||||
* Gets what this expression might "refer-to" in the given `context`.
|
* Gets what this expression might "refer-to" in the given `context`.
|
||||||
*/
|
*/
|
||||||
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
|
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
|
||||||
this.getAFlowNode()
|
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||||
.(ControlFlowNodeWithPointsTo)
|
this_.getNode() = this and origin_.getNode() = origin
|
||||||
.refersTo(context, obj, cls, origin.getAFlowNode())
|
|
|
||||||
|
this_.(ControlFlowNodeWithPointsTo).refersTo(context, obj, cls, origin_)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -226,7 +228,11 @@ class ExprWithPointsTo extends Expr {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate refersTo(Object obj, AstNode origin) {
|
predicate refersTo(Object obj, AstNode origin) {
|
||||||
this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode())
|
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||||
|
this_.getNode() = this and origin_.getNode() = origin
|
||||||
|
|
|
||||||
|
this_.(ControlFlowNodeWithPointsTo).refersTo(obj, origin_)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,16 +246,22 @@ class ExprWithPointsTo extends Expr {
|
|||||||
* in the given `context`.
|
* in the given `context`.
|
||||||
*/
|
*/
|
||||||
predicate pointsTo(Context context, Value value, AstNode origin) {
|
predicate pointsTo(Context context, Value value, AstNode origin) {
|
||||||
this.getAFlowNode()
|
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||||
.(ControlFlowNodeWithPointsTo)
|
this_.getNode() = this and origin_.getNode() = origin
|
||||||
.pointsTo(context, value, origin.getAFlowNode())
|
|
|
||||||
|
this_.(ControlFlowNodeWithPointsTo).pointsTo(context, value, origin_)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this expression might "point-to" to `value` which is from `origin`.
|
* Holds if this expression might "point-to" to `value` which is from `origin`.
|
||||||
*/
|
*/
|
||||||
predicate pointsTo(Value value, AstNode origin) {
|
predicate pointsTo(Value value, AstNode origin) {
|
||||||
this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode())
|
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||||
|
this_.getNode() = this and origin_.getNode() = origin
|
||||||
|
|
|
||||||
|
this_.(ControlFlowNodeWithPointsTo).pointsTo(value, origin_)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -475,7 +487,10 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics {
|
|||||||
not non_coupling_method(result) and
|
not non_coupling_method(result) and
|
||||||
exists(Call call | call.getScope() = this |
|
exists(Call call | call.getScope() = this |
|
||||||
exists(FunctionObject callee | callee.getFunction() = result |
|
exists(FunctionObject callee | callee.getFunction() = result |
|
||||||
call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
|
exists(CallNode call_ |
|
||||||
|
call_.getNode() = call and
|
||||||
|
call_.getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Attribute a | call.getFunc() = a |
|
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) {
|
private predicate preferred_jump_to_defn(Expr use, Definition def) {
|
||||||
not use instanceof ClassExpr and
|
not use instanceof ClassExpr and
|
||||||
not use instanceof FunctionExpr and
|
not use instanceof FunctionExpr and
|
||||||
jump_to_defn(use.getAFlowNode(), def)
|
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, def))
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate unique_jump_to_defn(Expr use, Definition 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.
|
* This exists primarily for testing use `getPreferredDefinition()` instead.
|
||||||
*/
|
*/
|
||||||
Definition getADefinition(Expr use) {
|
Definition getADefinition(Expr use) {
|
||||||
jump_to_defn(use.getAFlowNode(), result) and
|
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, result)) and
|
||||||
not use instanceof Call and
|
not use instanceof Call and
|
||||||
not use.isArtificial() and
|
not use.isArtificial() and
|
||||||
// Not the use itself
|
// Not the use itself
|
||||||
|
|||||||
4
python/ql/lib/change-notes/2026-05-19-add-shared-cfg.md
Normal file
4
python/ql/lib/change-notes/2026-05-19-add-shared-cfg.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* A new Python control flow graph implementation has been added under `semmle.python.controlflow.internal.Cfg` (backed by `AstNodeImpl.qll`), built on the shared `codeql.controlflow.ControlFlowGraph` library. It is not yet used by the dataflow library or any production query; the legacy CFG in `semmle/python/Flow.qll` remains the default. The new library is exposed for tests and for upcoming migrations.
|
||||||
4
python/ql/lib/change-notes/2026-05-19-add-shared-ssa.md
Normal file
4
python/ql/lib/change-notes/2026-05-19-add-shared-ssa.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* A new SSA adapter has been added under `semmle.python.dataflow.new.internal.SsaImpl`, built on the shared `codeql.ssa.Ssa` library and the new shared CFG (`semmle.python.controlflow.internal.Cfg`). It is not yet used by the dataflow library or any production query; the legacy ESSA SSA in `semmle/python/essa/*` remains the default. The new SSA adapter is exposed for tests and for the upcoming dataflow migration.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
category: deprecated
|
||||||
|
---
|
||||||
|
* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: deprecated
|
||||||
|
---
|
||||||
|
* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: breaking
|
||||||
|
---
|
||||||
|
* The deprecated `AstNode.getAFlowNode()` and `Function.getAReturnValueFlowNode()` predicates now return nodes from the new shared CFG (`Cfg::ControlFlowNode`) rather than from the legacy CFG (`ControlFlowNode`). Callers that still rely on these deprecated APIs and feed the result into legacy-CFG-aware predicates will no longer type-check; migrate to `n.getNode() = e` (or, for return values, the explicit `Return` pattern shown in the deprecation message) to get nodes from the dataflow library's current CFG.
|
||||||
45
python/ql/lib/printCfgNew.ql
Normal file
45
python/ql/lib/printCfgNew.ql
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* @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>
|
||||||
@@ -6,8 +6,9 @@
|
|||||||
* directed and labeled; they specify how the components represented by nodes relate to each other.
|
* directed and labeled; they specify how the components represented by nodes relate to each other.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Importing python under the `py` namespace to avoid importing `CallNode` from `Flow.qll` and thereby having a naming conflict with `API::CallNode`.
|
// Importing python under the `PY` namespace to avoid pulling in `CallNode` from `Flow.qll` (via `import python`) and thereby having a naming conflict with `API::CallNode`.
|
||||||
private import python as PY
|
private import python as PY
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import semmle.python.dataflow.new.DataFlow
|
import semmle.python.dataflow.new.DataFlow
|
||||||
private import semmle.python.internal.CachedStages
|
private import semmle.python.internal.CachedStages
|
||||||
|
|
||||||
@@ -282,7 +283,7 @@ module API {
|
|||||||
index = this.getIndex() and
|
index = this.getIndex() and
|
||||||
(
|
(
|
||||||
// subscripting
|
// subscripting
|
||||||
exists(PY::SubscriptNode subscript |
|
exists(Cfg::SubscriptNode subscript |
|
||||||
subscript.getObject() = this.getAValueReachableFromSource().asCfgNode() and
|
subscript.getObject() = this.getAValueReachableFromSource().asCfgNode() and
|
||||||
subscript.getIndex() = index.asSink().asCfgNode()
|
subscript.getIndex() = index.asSink().asCfgNode()
|
||||||
|
|
|
|
||||||
@@ -290,7 +291,7 @@ module API {
|
|||||||
subscript = result.asSource().asCfgNode()
|
subscript = result.asSource().asCfgNode()
|
||||||
or
|
or
|
||||||
// writing
|
// writing
|
||||||
subscript.(PY::DefinitionNode).getValue() = result.asSink().asCfgNode()
|
subscript.(Cfg::DefinitionNode).getValue() = result.asSink().asCfgNode()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// dictionary literals
|
// dictionary literals
|
||||||
@@ -684,7 +685,7 @@ module API {
|
|||||||
* Ignores relative imports, such as `from ..foo.bar import baz`.
|
* Ignores relative imports, such as `from ..foo.bar import baz`.
|
||||||
*/
|
*/
|
||||||
private predicate imports(DataFlow::CfgNode imp, string name) {
|
private predicate imports(DataFlow::CfgNode imp, string name) {
|
||||||
exists(PY::ImportExprNode iexpr |
|
exists(Cfg::ImportExprNode iexpr |
|
||||||
imp.getNode() = iexpr and
|
imp.getNode() = iexpr and
|
||||||
not iexpr.getNode().isRelative() and
|
not iexpr.getNode().isRelative() and
|
||||||
name = iexpr.getNode().getImportedModuleName()
|
name = iexpr.getNode().getImportedModuleName()
|
||||||
@@ -775,7 +776,7 @@ module API {
|
|||||||
// list literals, from `x` to `[x]`
|
// list literals, from `x` to `[x]`
|
||||||
// TODO: once convenient, this should be done at a higher level than the AST,
|
// 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.
|
// at least at the CFG layer, to take splitting into account.
|
||||||
// Also consider `SequenceNode for generality.
|
// Also consider `Cfg::SequenceNode` for generality.
|
||||||
exists(PY::List list | list = pred.(DataFlow::ExprNode).getNode().getNode() |
|
exists(PY::List list | list = pred.(DataFlow::ExprNode).getNode().getNode() |
|
||||||
rhs.(DataFlow::ExprNode).getNode().getNode() = list.getAnElt() and
|
rhs.(DataFlow::ExprNode).getNode().getNode() = list.getAnElt() and
|
||||||
lbl = Label::subscript()
|
lbl = Label::subscript()
|
||||||
@@ -805,7 +806,7 @@ module API {
|
|||||||
subscript = trackUseNode(src).getSubscript(index)
|
subscript = trackUseNode(src).getSubscript(index)
|
||||||
|
|
|
|
||||||
// from `x` to a definition of `x[...]`
|
// from `x` to a definition of `x[...]`
|
||||||
rhs.asCfgNode() = subscript.asCfgNode().(PY::DefinitionNode).getValue() and
|
rhs.asCfgNode() = subscript.asCfgNode().(Cfg::DefinitionNode).getValue() and
|
||||||
lbl = Label::subscript()
|
lbl = Label::subscript()
|
||||||
or
|
or
|
||||||
// from `x` to `"key"` in `x["key"]`
|
// from `x` to `"key"` in `x["key"]`
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module;
|
|||||||
|
|
||||||
import python
|
import python
|
||||||
private import semmle.python.internal.CachedStages
|
private import semmle.python.internal.CachedStages
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
|
|
||||||
/** A syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
|
/** A syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
|
||||||
abstract class AstNode extends AstNode_ {
|
abstract class AstNode extends AstNode_ {
|
||||||
@@ -16,21 +17,25 @@ abstract class AstNode extends AstNode_ {
|
|||||||
/** Gets the scope that this node occurs in */
|
/** Gets the scope that this node occurs in */
|
||||||
abstract Scope getScope();
|
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 */
|
/** Gets the location for this AST node */
|
||||||
cached
|
cached
|
||||||
Location getLocation() { none() }
|
Location getLocation() { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead;
|
||||||
|
* that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is
|
||||||
|
* being removed to untangle the AST and CFG hierarchies.
|
||||||
|
*
|
||||||
|
* Gets a flow node corresponding directly to this node, from the new
|
||||||
|
* (shared) CFG. NOTE: For some statements and other purely syntactic
|
||||||
|
* elements, there may not be a `ControlFlowNode`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
deprecated Cfg::ControlFlowNode getAFlowNode() {
|
||||||
|
Stages::AST::ref() and
|
||||||
|
result.getNode() = this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this syntactic element is artificial, that is it is generated
|
* Whether this syntactic element is artificial, that is it is generated
|
||||||
* by the compiler and is not present in the source
|
* by the compiler and is not present in the source
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
|
private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
|
||||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
@@ -214,7 +215,7 @@ module Path {
|
|||||||
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
|
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate safeAccessCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
private predicate safeAccessCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
|
||||||
g.(SafeAccessCheck::Range).checks(node, branch)
|
g.(SafeAccessCheck::Range).checks(node, branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +224,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. */
|
/** 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 {
|
abstract class Range extends DataFlow::GuardNode {
|
||||||
/** Holds if this guard validates `node` upon evaluating to `branch`. */
|
/** Holds if this guard validates `node` upon evaluating to `branch`. */
|
||||||
abstract predicate checks(ControlFlowNode node, boolean branch);
|
abstract predicate checks(Cfg::ControlFlowNode node, boolean branch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ class Expr extends Expr_, AstNode {
|
|||||||
/** Whether this expression may have a side effect (as determined purely from its syntax) */
|
/** Whether this expression may have a side effect (as determined purely from its syntax) */
|
||||||
predicate hasSideEffects() {
|
predicate hasSideEffects() {
|
||||||
/* If an exception raised by this expression handled, count that as a side effect */
|
/* If an exception raised by this expression handled, count that as a side effect */
|
||||||
this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt
|
exists(ControlFlowNode n | n.getNode() = this |
|
||||||
|
n.getASuccessor().getNode() instanceof ExceptStmt
|
||||||
|
)
|
||||||
or
|
or
|
||||||
this.getASubExpression().hasSideEffects()
|
this.getASubExpression().hasSideEffects()
|
||||||
}
|
}
|
||||||
@@ -68,8 +70,6 @@ class Attribute extends Attribute_ {
|
|||||||
/* syntax: Expr.name */
|
/* syntax: Expr.name */
|
||||||
override Expr getASubExpression() { result = this.getObject() }
|
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` */
|
/** Gets the name of this attribute. That is the `name` in `obj.name` */
|
||||||
string getName() { result = Attribute_.super.getAttr() }
|
string getName() { result = Attribute_.super.getAttr() }
|
||||||
|
|
||||||
@@ -96,8 +96,6 @@ class Subscript extends Subscript_ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expr getObject() { result = Subscript_.super.getValue() }
|
Expr getObject() { result = Subscript_.super.getValue() }
|
||||||
|
|
||||||
override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A call expression, such as `func(...)` */
|
/** A call expression, such as `func(...)` */
|
||||||
@@ -113,8 +111,6 @@ class Call extends Call_ {
|
|||||||
|
|
||||||
override string toString() { result = this.getFunc().toString() + "()" }
|
override string toString() { result = this.getFunc().toString() + "()" }
|
||||||
|
|
||||||
override CallNode getAFlowNode() { result = super.getAFlowNode() }
|
|
||||||
|
|
||||||
/** Gets a tuple (*) argument of this call. */
|
/** Gets a tuple (*) argument of this call. */
|
||||||
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
|
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
|
||||||
|
|
||||||
@@ -200,8 +196,6 @@ class IfExp extends IfExp_ {
|
|||||||
override Expr getASubExpression() {
|
override Expr getASubExpression() {
|
||||||
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
|
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` */
|
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
|
||||||
@@ -410,8 +404,6 @@ class PlaceHolder extends PlaceHolder_ {
|
|||||||
override Expr getASubExpression() { none() }
|
override Expr getASubExpression() { none() }
|
||||||
|
|
||||||
override string toString() { result = "$" + this.getId() }
|
override string toString() { result = "$" + this.getId() }
|
||||||
|
|
||||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
||||||
@@ -478,8 +470,6 @@ class Name extends Name_ {
|
|||||||
|
|
||||||
override string toString() { result = this.getId() }
|
override string toString() { result = this.getId() }
|
||||||
|
|
||||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
|
||||||
|
|
||||||
override predicate isArtificial() {
|
override predicate isArtificial() {
|
||||||
/* Artificial variable names in comprehensions all start with "." */
|
/* Artificial variable names in comprehensions all start with "." */
|
||||||
this.getId().charAt(0) = "."
|
this.getId().charAt(0) = "."
|
||||||
@@ -585,8 +575,6 @@ abstract class NameConstant extends Name, ImmutableLiteral {
|
|||||||
|
|
||||||
override predicate isConstant() { any() }
|
override predicate isConstant() { any() }
|
||||||
|
|
||||||
override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
|
|
||||||
|
|
||||||
override predicate isArtificial() { none() }
|
override predicate isArtificial() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
overlay[local]
|
overlay[local]
|
||||||
module;
|
module;
|
||||||
|
|
||||||
import python
|
import python as Py
|
||||||
private import semmle.python.internal.CachedStages
|
private import semmle.python.internal.CachedStages
|
||||||
private import codeql.controlflow.BasicBlock as BB
|
private import codeql.controlflow.BasicBlock as BB
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ private import codeql.controlflow.BasicBlock as BB
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
|
private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
|
||||||
exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) |
|
exists(Py::Expr load_store | exists(Py::AugAssign aa | aa.getTarget() = load_store) |
|
||||||
toAst(load) = load_store and
|
toAst(load) = load_store and
|
||||||
toAst(store) = load_store and
|
toAst(store) = load_store and
|
||||||
load.strictlyDominates(store)
|
load.strictlyDominates(store)
|
||||||
@@ -25,7 +25,7 @@ private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A non-dispatched getNode() to avoid negative recursion issues */
|
/** A non-dispatched getNode() to avoid negative recursion issues */
|
||||||
private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
|
private Py::AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A control flow node. Control flow nodes have a many-to-one relation with syntactic nodes,
|
* A control flow node. Control flow nodes have a many-to-one relation with syntactic nodes,
|
||||||
@@ -35,19 +35,19 @@ private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
|
|||||||
class ControlFlowNode extends @py_flow_node {
|
class ControlFlowNode extends @py_flow_node {
|
||||||
/** Whether this control flow node is a load (including those in augmented assignments) */
|
/** Whether this control flow node is a load (including those in augmented assignments) */
|
||||||
predicate isLoad() {
|
predicate isLoad() {
|
||||||
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
|
exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether this control flow node is a store (including those in augmented assignments) */
|
/** Whether this control flow node is a store (including those in augmented assignments) */
|
||||||
predicate isStore() {
|
predicate isStore() {
|
||||||
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
|
exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether this control flow node is a delete */
|
/** Whether this control flow node is a delete */
|
||||||
predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
|
predicate isDelete() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
|
||||||
|
|
||||||
/** Whether this control flow node is a parameter */
|
/** Whether this control flow node is a parameter */
|
||||||
predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
|
predicate isParameter() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
|
||||||
|
|
||||||
/** Whether this control flow node is a store in an augmented assignment */
|
/** Whether this control flow node is a store in an augmented assignment */
|
||||||
predicate isAugStore() { augstore(_, this) }
|
predicate isAugStore() { augstore(_, this) }
|
||||||
@@ -57,61 +57,61 @@ class ControlFlowNode extends @py_flow_node {
|
|||||||
|
|
||||||
/** Whether this flow node corresponds to a literal */
|
/** Whether this flow node corresponds to a literal */
|
||||||
predicate isLiteral() {
|
predicate isLiteral() {
|
||||||
toAst(this) instanceof Bytes
|
toAst(this) instanceof Py::Bytes
|
||||||
or
|
or
|
||||||
toAst(this) instanceof Dict
|
toAst(this) instanceof Py::Dict
|
||||||
or
|
or
|
||||||
toAst(this) instanceof DictComp
|
toAst(this) instanceof Py::DictComp
|
||||||
or
|
or
|
||||||
toAst(this) instanceof Set
|
toAst(this) instanceof Py::Set
|
||||||
or
|
or
|
||||||
toAst(this) instanceof SetComp
|
toAst(this) instanceof Py::SetComp
|
||||||
or
|
or
|
||||||
toAst(this) instanceof Ellipsis
|
toAst(this) instanceof Py::Ellipsis
|
||||||
or
|
or
|
||||||
toAst(this) instanceof GeneratorExp
|
toAst(this) instanceof Py::GeneratorExp
|
||||||
or
|
or
|
||||||
toAst(this) instanceof Lambda
|
toAst(this) instanceof Py::Lambda
|
||||||
or
|
or
|
||||||
toAst(this) instanceof ListComp
|
toAst(this) instanceof Py::ListComp
|
||||||
or
|
or
|
||||||
toAst(this) instanceof List
|
toAst(this) instanceof Py::List
|
||||||
or
|
or
|
||||||
toAst(this) instanceof Num
|
toAst(this) instanceof Py::Num
|
||||||
or
|
or
|
||||||
toAst(this) instanceof Tuple
|
toAst(this) instanceof Py::Tuple
|
||||||
or
|
or
|
||||||
toAst(this) instanceof Unicode
|
toAst(this) instanceof Py::Unicode
|
||||||
or
|
or
|
||||||
toAst(this) instanceof NameConstant
|
toAst(this) instanceof Py::NameConstant
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether this flow node corresponds to an attribute expression */
|
/** Whether this flow node corresponds to an attribute expression */
|
||||||
predicate isAttribute() { toAst(this) instanceof Attribute }
|
predicate isAttribute() { toAst(this) instanceof Py::Attribute }
|
||||||
|
|
||||||
/** Whether this flow node corresponds to an subscript expression */
|
/** Whether this flow node corresponds to an subscript expression */
|
||||||
predicate isSubscript() { toAst(this) instanceof Subscript }
|
predicate isSubscript() { toAst(this) instanceof Py::Subscript }
|
||||||
|
|
||||||
/** Whether this flow node corresponds to an import member */
|
/** Whether this flow node corresponds to an import member */
|
||||||
predicate isImportMember() { toAst(this) instanceof ImportMember }
|
predicate isImportMember() { toAst(this) instanceof Py::ImportMember }
|
||||||
|
|
||||||
/** Whether this flow node corresponds to a call */
|
/** Whether this flow node corresponds to a call */
|
||||||
predicate isCall() { toAst(this) instanceof Call }
|
predicate isCall() { toAst(this) instanceof Py::Call }
|
||||||
|
|
||||||
/** Whether this flow node is the first in a module */
|
/** Whether this flow node is the first in a module */
|
||||||
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module }
|
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Py::Module }
|
||||||
|
|
||||||
/** Whether this flow node corresponds to an import */
|
/** Whether this flow node corresponds to an import */
|
||||||
predicate isImport() { toAst(this) instanceof ImportExpr }
|
predicate isImport() { toAst(this) instanceof Py::ImportExpr }
|
||||||
|
|
||||||
/** Whether this flow node corresponds to a conditional expression */
|
/** Whether this flow node corresponds to a conditional expression */
|
||||||
predicate isIfExp() { toAst(this) instanceof IfExp }
|
predicate isIfExp() { toAst(this) instanceof Py::IfExp }
|
||||||
|
|
||||||
/** Whether this flow node corresponds to a function definition expression */
|
/** Whether this flow node corresponds to a function definition expression */
|
||||||
predicate isFunction() { toAst(this) instanceof FunctionExpr }
|
predicate isFunction() { toAst(this) instanceof Py::FunctionExpr }
|
||||||
|
|
||||||
/** Whether this flow node corresponds to a class definition expression */
|
/** Whether this flow node corresponds to a class definition expression */
|
||||||
predicate isClass() { toAst(this) instanceof ClassExpr }
|
predicate isClass() { toAst(this) instanceof Py::ClassExpr }
|
||||||
|
|
||||||
/** Gets a predecessor of this flow node */
|
/** Gets a predecessor of this flow node */
|
||||||
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
|
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
|
||||||
@@ -123,25 +123,25 @@ class ControlFlowNode extends @py_flow_node {
|
|||||||
ControlFlowNode getImmediateDominator() { py_idoms(this, result) }
|
ControlFlowNode getImmediateDominator() { py_idoms(this, result) }
|
||||||
|
|
||||||
/** Gets the syntactic element corresponding to this flow node */
|
/** Gets the syntactic element corresponding to this flow node */
|
||||||
AstNode getNode() { py_flow_bb_node(this, result, _, _) }
|
Py::AstNode getNode() { py_flow_bb_node(this, result, _, _) }
|
||||||
|
|
||||||
/** Gets a textual representation of this element. */
|
/** Gets a textual representation of this element. */
|
||||||
cached
|
cached
|
||||||
string toString() {
|
string toString() {
|
||||||
Stages::AST::ref() and
|
Stages::AST::ref() and
|
||||||
// Since modules can have ambigous names, entry nodes can too, if we do not collate them.
|
// Since modules can have ambigous names, entry nodes can too, if we do not collate them.
|
||||||
exists(Scope s | s.getEntryNode() = this |
|
exists(Py::Scope s | s.getEntryNode() = this |
|
||||||
result = "Entry node for " + concat( | | s.toString(), ",")
|
result = "Entry node for " + concat( | | s.toString(), ",")
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
|
exists(Py::Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
|
||||||
or
|
or
|
||||||
not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
|
not exists(Py::Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
|
||||||
result = "ControlFlowNode for " + this.getNode().toString()
|
result = "ControlFlowNode for " + this.getNode().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the location of this ControlFlowNode */
|
/** Gets the location of this ControlFlowNode */
|
||||||
Location getLocation() { result = this.getNode().getLocation() }
|
Py::Location getLocation() { result = this.getNode().getLocation() }
|
||||||
|
|
||||||
/** Whether this flow node is the first in its scope */
|
/** Whether this flow node is the first in its scope */
|
||||||
predicate isEntryNode() { py_scope_flow(this, _, -1) }
|
predicate isEntryNode() { py_scope_flow(this, _, -1) }
|
||||||
@@ -151,9 +151,9 @@ class ControlFlowNode extends @py_flow_node {
|
|||||||
|
|
||||||
/** Gets the scope containing this flow node */
|
/** Gets the scope containing this flow node */
|
||||||
cached
|
cached
|
||||||
Scope getScope() {
|
Py::Scope getScope() {
|
||||||
Stages::AST::ref() and
|
Stages::AST::ref() and
|
||||||
if this.getNode() instanceof Scope
|
if this.getNode() instanceof Py::Scope
|
||||||
then
|
then
|
||||||
/* Entry or exit node */
|
/* Entry or exit node */
|
||||||
result = this.getNode()
|
result = this.getNode()
|
||||||
@@ -161,7 +161,7 @@ class ControlFlowNode extends @py_flow_node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the enclosing module */
|
/** Gets the enclosing module */
|
||||||
Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
|
Py::Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
|
||||||
|
|
||||||
/** Gets a successor for this node if the relevant condition is True. */
|
/** Gets a successor for this node if the relevant condition is True. */
|
||||||
ControlFlowNode getATrueSuccessor() {
|
ControlFlowNode getATrueSuccessor() {
|
||||||
@@ -188,7 +188,7 @@ class ControlFlowNode extends @py_flow_node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the scope may be exited as a result of this node raising an exception */
|
/** Whether the scope may be exited as a result of this node raising an exception */
|
||||||
predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) }
|
predicate isExceptionalExit(Py::Scope s) { py_scope_flow(this, s, 1) }
|
||||||
|
|
||||||
/** Whether this node is a normal (non-exceptional) exit */
|
/** Whether this node is a normal (non-exceptional) exit */
|
||||||
predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) }
|
predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) }
|
||||||
@@ -236,7 +236,7 @@ class ControlFlowNode extends @py_flow_node {
|
|||||||
/* join-ordering helper for `getAChild() */
|
/* join-ordering helper for `getAChild() */
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private ControlFlowNode getExprChild(BasicBlock dom) {
|
private ControlFlowNode getExprChild(BasicBlock dom) {
|
||||||
this.getNode().(Expr).getAChildNode() = result.getNode() and
|
this.getNode().(Py::Expr).getAChildNode() = result.getNode() and
|
||||||
result.getBasicBlock().dominates(dom) and
|
result.getBasicBlock().dominates(dom) and
|
||||||
not this instanceof UnaryExprNode
|
not this instanceof UnaryExprNode
|
||||||
}
|
}
|
||||||
@@ -249,16 +249,16 @@ class ControlFlowNode extends @py_flow_node {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private class AnyNode extends ControlFlowNode {
|
private class AnyNode extends ControlFlowNode {
|
||||||
override AstNode getNode() { result = super.getNode() }
|
override Py::AstNode getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a call expression, such as `func(...)` */
|
/** A control flow node corresponding to a call expression, such as `func(...)` */
|
||||||
class CallNode extends ControlFlowNode {
|
class CallNode extends ControlFlowNode {
|
||||||
CallNode() { toAst(this) instanceof Call }
|
CallNode() { toAst(this) instanceof Py::Call }
|
||||||
|
|
||||||
/** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */
|
/** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */
|
||||||
ControlFlowNode getFunction() {
|
ControlFlowNode getFunction() {
|
||||||
exists(Call c |
|
exists(Py::Call c |
|
||||||
this.getNode() = c and
|
this.getNode() = c and
|
||||||
c.getFunc() = result.getNode() and
|
c.getFunc() = result.getNode() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
@@ -267,7 +267,7 @@ class CallNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** Gets the flow node corresponding to the n'th positional argument of the call corresponding to this flow node */
|
/** Gets the flow node corresponding to the n'th positional argument of the call corresponding to this flow node */
|
||||||
ControlFlowNode getArg(int n) {
|
ControlFlowNode getArg(int n) {
|
||||||
exists(Call c |
|
exists(Py::Call c |
|
||||||
this.getNode() = c and
|
this.getNode() = c and
|
||||||
c.getArg(n) = result.getNode() and
|
c.getArg(n) = result.getNode() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
@@ -276,7 +276,7 @@ class CallNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */
|
/** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */
|
||||||
ControlFlowNode getArgByName(string name) {
|
ControlFlowNode getArgByName(string name) {
|
||||||
exists(Call c, Keyword k |
|
exists(Py::Call c, Py::Keyword k |
|
||||||
this.getNode() = c and
|
this.getNode() = c and
|
||||||
k = c.getANamedArg() and
|
k = c.getANamedArg() and
|
||||||
k.getValue() = result.getNode() and
|
k.getValue() = result.getNode() and
|
||||||
@@ -292,7 +292,7 @@ class CallNode extends ControlFlowNode {
|
|||||||
result = this.getArgByName(_)
|
result = this.getArgByName(_)
|
||||||
}
|
}
|
||||||
|
|
||||||
override Call getNode() { result = super.getNode() }
|
override Py::Call getNode() { result = super.getNode() }
|
||||||
|
|
||||||
predicate isDecoratorCall() {
|
predicate isDecoratorCall() {
|
||||||
this.isClassDecoratorCall()
|
this.isClassDecoratorCall()
|
||||||
@@ -301,11 +301,11 @@ class CallNode extends ControlFlowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate isClassDecoratorCall() {
|
predicate isClassDecoratorCall() {
|
||||||
exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall())
|
exists(Py::ClassExpr cls | this.getNode() = cls.getADecoratorCall())
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isFunctionDecoratorCall() {
|
predicate isFunctionDecoratorCall() {
|
||||||
exists(FunctionExpr func | this.getNode() = func.getADecoratorCall())
|
exists(Py::FunctionExpr func | this.getNode() = func.getADecoratorCall())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the first tuple (*) argument of this call, if any. */
|
/** Gets the first tuple (*) argument of this call, if any. */
|
||||||
@@ -323,11 +323,11 @@ class CallNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** A control flow corresponding to an attribute expression, such as `value.attr` */
|
/** A control flow corresponding to an attribute expression, such as `value.attr` */
|
||||||
class AttrNode extends ControlFlowNode {
|
class AttrNode extends ControlFlowNode {
|
||||||
AttrNode() { toAst(this) instanceof Attribute }
|
AttrNode() { toAst(this) instanceof Py::Attribute }
|
||||||
|
|
||||||
/** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */
|
/** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */
|
||||||
ControlFlowNode getObject() {
|
ControlFlowNode getObject() {
|
||||||
exists(Attribute a |
|
exists(Py::Attribute a |
|
||||||
this.getNode() = a and
|
this.getNode() = a and
|
||||||
a.getObject() = result.getNode() and
|
a.getObject() = result.getNode() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
@@ -339,7 +339,7 @@ class AttrNode extends ControlFlowNode {
|
|||||||
* with the matching name
|
* with the matching name
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getObject(string name) {
|
ControlFlowNode getObject(string name) {
|
||||||
exists(Attribute a |
|
exists(Py::Attribute a |
|
||||||
this.getNode() = a and
|
this.getNode() = a and
|
||||||
a.getObject() = result.getNode() and
|
a.getObject() = result.getNode() and
|
||||||
a.getName() = name and
|
a.getName() = name and
|
||||||
@@ -348,57 +348,57 @@ class AttrNode extends ControlFlowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the attribute name of the attribute expression corresponding to this flow node */
|
/** Gets the attribute name of the attribute expression corresponding to this flow node */
|
||||||
string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) }
|
string getName() { exists(Py::Attribute a | this.getNode() = a and a.getName() = result) }
|
||||||
|
|
||||||
override Attribute getNode() { result = super.getNode() }
|
override Py::Attribute getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a `from ... import ...` expression */
|
/** A control flow node corresponding to a `from ... import ...` expression */
|
||||||
class ImportMemberNode extends ControlFlowNode {
|
class ImportMemberNode extends ControlFlowNode {
|
||||||
ImportMemberNode() { toAst(this) instanceof ImportMember }
|
ImportMemberNode() { toAst(this) instanceof Py::ImportMember }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node,
|
* Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node,
|
||||||
* with the matching name
|
* with the matching name
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getModule(string name) {
|
ControlFlowNode getModule(string name) {
|
||||||
exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
|
exists(Py::ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
|
||||||
i.getName() = name and
|
i.getName() = name and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override ImportMember getNode() { result = super.getNode() }
|
override Py::ImportMember getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to an artificial expression representing an import */
|
/** A control flow node corresponding to an artificial expression representing an import */
|
||||||
class ImportExprNode extends ControlFlowNode {
|
class ImportExprNode extends ControlFlowNode {
|
||||||
ImportExprNode() { toAst(this) instanceof ImportExpr }
|
ImportExprNode() { toAst(this) instanceof Py::ImportExpr }
|
||||||
|
|
||||||
override ImportExpr getNode() { result = super.getNode() }
|
override Py::ImportExpr getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a `from ... import *` statement */
|
/** A control flow node corresponding to a `from ... import *` statement */
|
||||||
class ImportStarNode extends ControlFlowNode {
|
class ImportStarNode extends ControlFlowNode {
|
||||||
ImportStarNode() { toAst(this) instanceof ImportStar }
|
ImportStarNode() { toAst(this) instanceof Py::ImportStar }
|
||||||
|
|
||||||
/** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */
|
/** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */
|
||||||
ControlFlowNode getModule() {
|
ControlFlowNode getModule() {
|
||||||
exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
|
exists(Py::ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override ImportStar getNode() { result = super.getNode() }
|
override Py::ImportStar getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a subscript expression, such as `value[slice]` */
|
/** A control flow node corresponding to a subscript expression, such as `value[slice]` */
|
||||||
class SubscriptNode extends ControlFlowNode {
|
class SubscriptNode extends ControlFlowNode {
|
||||||
SubscriptNode() { toAst(this) instanceof Subscript }
|
SubscriptNode() { toAst(this) instanceof Py::Subscript }
|
||||||
|
|
||||||
/** flow node corresponding to the value of the sequence in a subscript operation */
|
/** flow node corresponding to the value of the sequence in a subscript operation */
|
||||||
ControlFlowNode getObject() {
|
ControlFlowNode getObject() {
|
||||||
exists(Subscript s |
|
exists(Py::Subscript s |
|
||||||
this.getNode() = s and
|
this.getNode() = s and
|
||||||
s.getObject() = result.getNode() and
|
s.getObject() = result.getNode() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
@@ -407,23 +407,23 @@ class SubscriptNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** flow node corresponding to the index in a subscript operation */
|
/** flow node corresponding to the index in a subscript operation */
|
||||||
ControlFlowNode getIndex() {
|
ControlFlowNode getIndex() {
|
||||||
exists(Subscript s |
|
exists(Py::Subscript s |
|
||||||
this.getNode() = s and
|
this.getNode() = s and
|
||||||
s.getIndex() = result.getNode() and
|
s.getIndex() = result.getNode() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override Subscript getNode() { result = super.getNode() }
|
override Py::Subscript getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a comparison operation, such as `x<y` */
|
/** A control flow node corresponding to a comparison operation, such as `x<y` */
|
||||||
class CompareNode extends ControlFlowNode {
|
class CompareNode extends ControlFlowNode {
|
||||||
CompareNode() { toAst(this) instanceof Compare }
|
CompareNode() { toAst(this) instanceof Py::Compare }
|
||||||
|
|
||||||
/** Whether left and right are a pair of operands for this comparison */
|
/** Whether left and right are a pair of operands for this comparison */
|
||||||
predicate operands(ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
predicate operands(ControlFlowNode left, Py::Cmpop op, ControlFlowNode right) {
|
||||||
exists(Compare c, Expr eleft, Expr eright |
|
exists(Py::Compare c, Py::Expr eleft, Py::Expr eright |
|
||||||
this.getNode() = c and left.getNode() = eleft and right.getNode() = eright
|
this.getNode() = c and left.getNode() = eleft and right.getNode() = eright
|
||||||
|
|
|
|
||||||
eleft = c.getLeft() and eright = c.getComparator(0) and op = c.getOp(0)
|
eleft = c.getLeft() and eright = c.getComparator(0) and op = c.getOp(0)
|
||||||
@@ -436,26 +436,26 @@ class CompareNode extends ControlFlowNode {
|
|||||||
right.getBasicBlock().dominates(this.getBasicBlock())
|
right.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
}
|
}
|
||||||
|
|
||||||
override Compare getNode() { result = super.getNode() }
|
override Py::Compare getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a conditional expression such as, `body if test else orelse` */
|
/** A control flow node corresponding to a conditional expression such as, `body if test else orelse` */
|
||||||
class IfExprNode extends ControlFlowNode {
|
class IfExprNode extends ControlFlowNode {
|
||||||
IfExprNode() { toAst(this) instanceof IfExp }
|
IfExprNode() { toAst(this) instanceof Py::IfExp }
|
||||||
|
|
||||||
/** flow node corresponding to one of the operands of an if-expression */
|
/** flow node corresponding to one of the operands of an if-expression */
|
||||||
ControlFlowNode getAnOperand() { result = this.getAPredecessor() }
|
ControlFlowNode getAnOperand() { result = this.getAPredecessor() }
|
||||||
|
|
||||||
override IfExp getNode() { result = super.getNode() }
|
override Py::IfExp getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */
|
/** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */
|
||||||
class AssignmentExprNode extends ControlFlowNode {
|
class AssignmentExprNode extends ControlFlowNode {
|
||||||
AssignmentExprNode() { toAst(this) instanceof AssignExpr }
|
AssignmentExprNode() { toAst(this) instanceof Py::AssignExpr }
|
||||||
|
|
||||||
/** Gets the flow node corresponding to the left-hand side of the assignment expression */
|
/** Gets the flow node corresponding to the left-hand side of the assignment expression */
|
||||||
ControlFlowNode getTarget() {
|
ControlFlowNode getTarget() {
|
||||||
exists(AssignExpr a |
|
exists(Py::AssignExpr a |
|
||||||
this.getNode() = a and
|
this.getNode() = a and
|
||||||
a.getTarget() = result.getNode() and
|
a.getTarget() = result.getNode() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
@@ -464,27 +464,27 @@ class AssignmentExprNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** Gets the flow node corresponding to the right-hand side of the assignment expression */
|
/** Gets the flow node corresponding to the right-hand side of the assignment expression */
|
||||||
ControlFlowNode getValue() {
|
ControlFlowNode getValue() {
|
||||||
exists(AssignExpr a |
|
exists(Py::AssignExpr a |
|
||||||
this.getNode() = a and
|
this.getNode() = a and
|
||||||
a.getValue() = result.getNode() and
|
a.getValue() = result.getNode() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override AssignExpr getNode() { result = super.getNode() }
|
override Py::AssignExpr getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a binary expression, such as `x + y` */
|
/** A control flow node corresponding to a binary expression, such as `x + y` */
|
||||||
class BinaryExprNode extends ControlFlowNode {
|
class BinaryExprNode extends ControlFlowNode {
|
||||||
BinaryExprNode() { toAst(this) instanceof BinaryExpr }
|
BinaryExprNode() { toAst(this) instanceof Py::BinaryExpr }
|
||||||
|
|
||||||
/** flow node corresponding to one of the operands of a binary expression */
|
/** flow node corresponding to one of the operands of a binary expression */
|
||||||
ControlFlowNode getAnOperand() { result = this.getLeft() or result = this.getRight() }
|
ControlFlowNode getAnOperand() { result = this.getLeft() or result = this.getRight() }
|
||||||
|
|
||||||
override BinaryExpr getNode() { result = super.getNode() }
|
override Py::BinaryExpr getNode() { result = super.getNode() }
|
||||||
|
|
||||||
ControlFlowNode getLeft() {
|
ControlFlowNode getLeft() {
|
||||||
exists(BinaryExpr b |
|
exists(Py::BinaryExpr b |
|
||||||
this.getNode() = b and
|
this.getNode() = b and
|
||||||
result.getNode() = b.getLeft() and
|
result.getNode() = b.getLeft() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
@@ -492,7 +492,7 @@ class BinaryExprNode extends ControlFlowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ControlFlowNode getRight() {
|
ControlFlowNode getRight() {
|
||||||
exists(BinaryExpr b |
|
exists(Py::BinaryExpr b |
|
||||||
this.getNode() = b and
|
this.getNode() = b and
|
||||||
result.getNode() = b.getRight() and
|
result.getNode() = b.getRight() and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
@@ -500,11 +500,11 @@ class BinaryExprNode extends ControlFlowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the operator of this binary expression node. */
|
/** Gets the operator of this binary expression node. */
|
||||||
Operator getOp() { result = this.getNode().getOp() }
|
Py::Operator getOp() { result = this.getNode().getOp() }
|
||||||
|
|
||||||
/** Whether left and right are a pair of operands for this binary expression */
|
/** Whether left and right are a pair of operands for this binary expression */
|
||||||
predicate operands(ControlFlowNode left, Operator op, ControlFlowNode right) {
|
predicate operands(ControlFlowNode left, Py::Operator op, ControlFlowNode right) {
|
||||||
exists(BinaryExpr b, Expr eleft, Expr eright |
|
exists(Py::BinaryExpr b, Py::Expr eleft, Py::Expr eright |
|
||||||
this.getNode() = b and left.getNode() = eleft and right.getNode() = eright
|
this.getNode() = b and left.getNode() = eleft and right.getNode() = eright
|
||||||
|
|
|
|
||||||
eleft = b.getLeft() and eright = b.getRight() and op = b.getOp()
|
eleft = b.getLeft() and eright = b.getRight() and op = b.getOp()
|
||||||
@@ -516,20 +516,20 @@ class BinaryExprNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** A control flow node corresponding to a boolean shortcut (and/or) operation */
|
/** A control flow node corresponding to a boolean shortcut (and/or) operation */
|
||||||
class BoolExprNode extends ControlFlowNode {
|
class BoolExprNode extends ControlFlowNode {
|
||||||
BoolExprNode() { toAst(this) instanceof BoolExpr }
|
BoolExprNode() { toAst(this) instanceof Py::BoolExpr }
|
||||||
|
|
||||||
/** flow node corresponding to one of the operands of a boolean expression */
|
/** flow node corresponding to one of the operands of a boolean expression */
|
||||||
ControlFlowNode getAnOperand() {
|
ControlFlowNode getAnOperand() {
|
||||||
exists(BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
|
exists(Py::BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
|
||||||
this.getBasicBlock().dominates(result.getBasicBlock())
|
this.getBasicBlock().dominates(result.getBasicBlock())
|
||||||
}
|
}
|
||||||
|
|
||||||
override BoolExpr getNode() { result = super.getNode() }
|
override Py::BoolExpr getNode() { result = super.getNode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a unary expression: (`+x`), (`-x`) or (`~x`) */
|
/** A control flow node corresponding to a unary expression: (`+x`), (`-x`) or (`~x`) */
|
||||||
class UnaryExprNode extends ControlFlowNode {
|
class UnaryExprNode extends ControlFlowNode {
|
||||||
UnaryExprNode() { toAst(this) instanceof UnaryExpr }
|
UnaryExprNode() { toAst(this) instanceof Py::UnaryExpr }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets flow node corresponding to the operand of a unary expression.
|
* Gets flow node corresponding to the operand of a unary expression.
|
||||||
@@ -540,7 +540,7 @@ class UnaryExprNode extends ControlFlowNode {
|
|||||||
*/
|
*/
|
||||||
ControlFlowNode getOperand() { result = this.getAPredecessor() }
|
ControlFlowNode getOperand() { result = this.getAPredecessor() }
|
||||||
|
|
||||||
override UnaryExpr getNode() { result = super.getNode() }
|
override Py::UnaryExpr getNode() { result = super.getNode() }
|
||||||
|
|
||||||
override ControlFlowNode getAChild() { result = this.getAPredecessor() }
|
override ControlFlowNode getAChild() { result = this.getAPredecessor() }
|
||||||
}
|
}
|
||||||
@@ -555,27 +555,27 @@ class DefinitionNode extends ControlFlowNode {
|
|||||||
cached
|
cached
|
||||||
DefinitionNode() {
|
DefinitionNode() {
|
||||||
Stages::AST::ref() and
|
Stages::AST::ref() and
|
||||||
exists(Assign a | a.getATarget().getAFlowNode() = this)
|
exists(Py::Assign a | this.getNode() = a.getATarget())
|
||||||
or
|
or
|
||||||
exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
|
exists(Py::AssignExpr a | this.getNode() = a.getTarget())
|
||||||
or
|
or
|
||||||
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
|
exists(Py::AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue()))
|
||||||
or
|
or
|
||||||
exists(Alias a | a.getAsname().getAFlowNode() = this)
|
exists(Py::Alias a | this.getNode() = a.getAsname())
|
||||||
or
|
or
|
||||||
augstore(_, this)
|
augstore(_, this)
|
||||||
or
|
or
|
||||||
// `x, y = 1, 2` where LHS is a combination of list or tuples
|
// `x, y = 1, 2` where LHS is a combination of list or tuples
|
||||||
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
|
exists(Py::Assign a | this.getNode() = list_or_tuple_nested_element(a.getATarget()))
|
||||||
or
|
or
|
||||||
exists(For for | for.getTarget().getAFlowNode() = this)
|
exists(Py::For for | this.getNode() = for.getTarget())
|
||||||
or
|
or
|
||||||
exists(Parameter param | this = param.asName().getAFlowNode() and exists(param.getDefault()))
|
exists(Py::Parameter param | this.getNode() = param.asName() and exists(param.getDefault()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
|
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
|
||||||
ControlFlowNode getValue() {
|
ControlFlowNode getValue() {
|
||||||
result = assigned_value(this.getNode()).getAFlowNode() and
|
result.getNode() = assigned_value(this.getNode()) and
|
||||||
(
|
(
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
or
|
or
|
||||||
@@ -584,16 +584,16 @@ class DefinitionNode extends ControlFlowNode {
|
|||||||
// since the default value for a parameter is evaluated in the same basic block as
|
// since the default value for a parameter is evaluated in the same basic block as
|
||||||
// the function definition, but the parameter belongs to the basic block of the function,
|
// the function definition, but the parameter belongs to the basic block of the function,
|
||||||
// there is no dominance relationship between the two.
|
// there is no dominance relationship between the two.
|
||||||
exists(Parameter param | this = param.asName().getAFlowNode())
|
exists(Py::Parameter param | this.getNode() = param.asName())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
|
private Py::Expr list_or_tuple_nested_element(Py::Expr list_or_tuple) {
|
||||||
exists(Expr elt |
|
exists(Py::Expr elt |
|
||||||
elt = list_or_tuple.(Tuple).getAnElt()
|
elt = list_or_tuple.(Py::Tuple).getAnElt()
|
||||||
or
|
or
|
||||||
elt = list_or_tuple.(List).getAnElt()
|
elt = list_or_tuple.(Py::List).getAnElt()
|
||||||
|
|
|
|
||||||
result = elt
|
result = elt
|
||||||
or
|
or
|
||||||
@@ -603,12 +603,12 @@ private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A control flow node corresponding to a deletion statement, such as `del x`.
|
* A control flow node corresponding to a deletion statement, such as `del x`.
|
||||||
* There can be multiple `DeletionNode`s for each `Delete` such that each
|
* There can be multiple `DeletionNode`s for each `Py::Delete` such that each
|
||||||
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
|
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
|
||||||
* `NameNode('a') -> DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`.
|
* `NameNode('a') -> DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`.
|
||||||
*/
|
*/
|
||||||
class DeletionNode extends ControlFlowNode {
|
class DeletionNode extends ControlFlowNode {
|
||||||
DeletionNode() { toAst(this) instanceof Delete }
|
DeletionNode() { toAst(this) instanceof Py::Delete }
|
||||||
|
|
||||||
/** Gets the unique target of this deletion node. */
|
/** Gets the unique target of this deletion node. */
|
||||||
ControlFlowNode getTarget() { result.getASuccessor() = this }
|
ControlFlowNode getTarget() { result.getASuccessor() = this }
|
||||||
@@ -617,9 +617,9 @@ class DeletionNode extends ControlFlowNode {
|
|||||||
/** A control flow node corresponding to a sequence (tuple or list) literal */
|
/** A control flow node corresponding to a sequence (tuple or list) literal */
|
||||||
abstract class SequenceNode extends ControlFlowNode {
|
abstract class SequenceNode extends ControlFlowNode {
|
||||||
SequenceNode() {
|
SequenceNode() {
|
||||||
toAst(this) instanceof Tuple
|
toAst(this) instanceof Py::Tuple
|
||||||
or
|
or
|
||||||
toAst(this) instanceof List
|
toAst(this) instanceof Py::List
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the control flow node for an element of this sequence */
|
/** Gets the control flow node for an element of this sequence */
|
||||||
@@ -632,11 +632,11 @@ abstract class SequenceNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
/** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
||||||
class TupleNode extends SequenceNode {
|
class TupleNode extends SequenceNode {
|
||||||
TupleNode() { toAst(this) instanceof Tuple }
|
TupleNode() { toAst(this) instanceof Py::Tuple }
|
||||||
|
|
||||||
override ControlFlowNode getElement(int n) {
|
override ControlFlowNode getElement(int n) {
|
||||||
Stages::AST::ref() and
|
Stages::AST::ref() and
|
||||||
exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
|
exists(Py::Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
|
||||||
(
|
(
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
or
|
or
|
||||||
@@ -647,10 +647,10 @@ class TupleNode extends SequenceNode {
|
|||||||
|
|
||||||
/** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */
|
/** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */
|
||||||
class ListNode extends SequenceNode {
|
class ListNode extends SequenceNode {
|
||||||
ListNode() { toAst(this) instanceof List }
|
ListNode() { toAst(this) instanceof Py::List }
|
||||||
|
|
||||||
override ControlFlowNode getElement(int n) {
|
override ControlFlowNode getElement(int n) {
|
||||||
exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
|
exists(Py::List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
|
||||||
(
|
(
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
or
|
or
|
||||||
@@ -661,10 +661,10 @@ class ListNode extends SequenceNode {
|
|||||||
|
|
||||||
/** A control flow node corresponding to a set expression, such as `{ 1, 3, 5, 7, 9 }` */
|
/** A control flow node corresponding to a set expression, such as `{ 1, 3, 5, 7, 9 }` */
|
||||||
class SetNode extends ControlFlowNode {
|
class SetNode extends ControlFlowNode {
|
||||||
SetNode() { toAst(this) instanceof Set }
|
SetNode() { toAst(this) instanceof Py::Set }
|
||||||
|
|
||||||
ControlFlowNode getAnElement() {
|
ControlFlowNode getAnElement() {
|
||||||
exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
|
exists(Py::Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
|
||||||
(
|
(
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
or
|
or
|
||||||
@@ -675,20 +675,20 @@ class SetNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */
|
/** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */
|
||||||
class DictNode extends ControlFlowNode {
|
class DictNode extends ControlFlowNode {
|
||||||
DictNode() { toAst(this) instanceof Dict }
|
DictNode() { toAst(this) instanceof Py::Dict }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a key of this dictionary literal node, for those items that have keys
|
* Gets a key of this dictionary literal node, for those items that have keys
|
||||||
* E.g, in {'a':1, **b} this returns only 'a'
|
* E.g, in {'a':1, **b} this returns only 'a'
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getAKey() {
|
ControlFlowNode getAKey() {
|
||||||
exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
|
exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets a value of this dictionary literal node */
|
/** Gets a value of this dictionary literal node */
|
||||||
ControlFlowNode getAValue() {
|
ControlFlowNode getAValue() {
|
||||||
exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
|
exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -712,21 +712,23 @@ class IterableNode extends ControlFlowNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AstNode assigned_value(Expr lhs) {
|
private Py::AstNode assigned_value(Py::Expr lhs) {
|
||||||
/* lhs = result */
|
/* lhs = result */
|
||||||
exists(Assign a | a.getATarget() = lhs and result = a.getValue())
|
exists(Py::Assign a | a.getATarget() = lhs and result = a.getValue())
|
||||||
or
|
or
|
||||||
/* lhs := result */
|
/* lhs := result */
|
||||||
exists(AssignExpr a | a.getTarget() = lhs and result = a.getValue())
|
exists(Py::AssignExpr a | a.getTarget() = lhs and result = a.getValue())
|
||||||
or
|
or
|
||||||
/* lhs : annotation = result */
|
/* lhs : annotation = result */
|
||||||
exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue())
|
exists(Py::AnnAssign a | a.getTarget() = lhs and result = a.getValue())
|
||||||
or
|
or
|
||||||
/* import result as lhs */
|
/* import result as lhs */
|
||||||
exists(Alias a | a.getAsname() = lhs and result = a.getValue())
|
exists(Py::Alias a | a.getAsname() = lhs and result = a.getValue())
|
||||||
or
|
or
|
||||||
/* lhs += x => result = (lhs + x) */
|
/* lhs += x => result = (lhs + x) */
|
||||||
exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
|
exists(Py::AugAssign a, Py::BinaryExpr b |
|
||||||
|
b = a.getOperation() and result = b and lhs = b.getLeft()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
/*
|
/*
|
||||||
* ..., lhs, ... = ..., result, ...
|
* ..., lhs, ... = ..., result, ...
|
||||||
@@ -734,31 +736,31 @@ private AstNode assigned_value(Expr lhs) {
|
|||||||
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
|
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
|
exists(Py::Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
|
||||||
or
|
or
|
||||||
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
|
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
|
||||||
result.(For).getTarget() = lhs
|
result.(Py::For).getTarget() = lhs
|
||||||
or
|
or
|
||||||
exists(Parameter param | lhs = param.asName() and result = param.getDefault())
|
exists(Py::Parameter param | lhs = param.asName() and result = param.getDefault())
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate nested_sequence_assign(
|
predicate nested_sequence_assign(
|
||||||
Expr left_parent, Expr right_parent, Expr left_result, Expr right_result
|
Py::Expr left_parent, Py::Expr right_parent, Py::Expr left_result, Py::Expr right_result
|
||||||
) {
|
) {
|
||||||
exists(Assign a |
|
exists(Py::Assign a |
|
||||||
a.getATarget().getASubExpression*() = left_parent and
|
a.getATarget().getASubExpression*() = left_parent and
|
||||||
a.getValue().getASubExpression*() = right_parent
|
a.getValue().getASubExpression*() = right_parent
|
||||||
) and
|
) and
|
||||||
exists(int i, Expr left_elem, Expr right_elem |
|
exists(int i, Py::Expr left_elem, Py::Expr right_elem |
|
||||||
(
|
(
|
||||||
left_elem = left_parent.(Tuple).getElt(i)
|
left_elem = left_parent.(Py::Tuple).getElt(i)
|
||||||
or
|
or
|
||||||
left_elem = left_parent.(List).getElt(i)
|
left_elem = left_parent.(Py::List).getElt(i)
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
right_elem = right_parent.(Tuple).getElt(i)
|
right_elem = right_parent.(Py::Tuple).getElt(i)
|
||||||
or
|
or
|
||||||
right_elem = right_parent.(List).getElt(i)
|
right_elem = right_parent.(Py::List).getElt(i)
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
left_result = left_elem and right_result = right_elem
|
left_result = left_elem and right_result = right_elem
|
||||||
@@ -769,9 +771,9 @@ predicate nested_sequence_assign(
|
|||||||
|
|
||||||
/** A flow node for a `for` statement. */
|
/** A flow node for a `for` statement. */
|
||||||
class ForNode extends ControlFlowNode {
|
class ForNode extends ControlFlowNode {
|
||||||
ForNode() { toAst(this) instanceof For }
|
ForNode() { toAst(this) instanceof Py::For }
|
||||||
|
|
||||||
override For getNode() { result = super.getNode() }
|
override Py::For getNode() { result = super.getNode() }
|
||||||
|
|
||||||
/** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */
|
/** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */
|
||||||
predicate iterates(ControlFlowNode target, ControlFlowNode sequence) {
|
predicate iterates(ControlFlowNode target, ControlFlowNode sequence) {
|
||||||
@@ -782,7 +784,7 @@ class ForNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** Gets the sequence node for this `for` statement. */
|
/** Gets the sequence node for this `for` statement. */
|
||||||
ControlFlowNode getSequence() {
|
ControlFlowNode getSequence() {
|
||||||
exists(For for |
|
exists(Py::For for |
|
||||||
toAst(this) = for and
|
toAst(this) = for and
|
||||||
for.getIter() = result.getNode()
|
for.getIter() = result.getNode()
|
||||||
|
|
|
|
||||||
@@ -792,7 +794,7 @@ class ForNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** A possible `target` for this `for` statement, not accounting for loop unrolling */
|
/** A possible `target` for this `for` statement, not accounting for loop unrolling */
|
||||||
private ControlFlowNode possibleTarget() {
|
private ControlFlowNode possibleTarget() {
|
||||||
exists(For for |
|
exists(Py::For for |
|
||||||
toAst(this) = for and
|
toAst(this) = for and
|
||||||
for.getTarget() = result.getNode() and
|
for.getTarget() = result.getNode() and
|
||||||
this.getBasicBlock().dominates(result.getBasicBlock())
|
this.getBasicBlock().dominates(result.getBasicBlock())
|
||||||
@@ -809,11 +811,11 @@ class ForNode extends ControlFlowNode {
|
|||||||
|
|
||||||
/** A flow node for a `raise` statement */
|
/** A flow node for a `raise` statement */
|
||||||
class RaiseStmtNode extends ControlFlowNode {
|
class RaiseStmtNode extends ControlFlowNode {
|
||||||
RaiseStmtNode() { toAst(this) instanceof Raise }
|
RaiseStmtNode() { toAst(this) instanceof Py::Raise }
|
||||||
|
|
||||||
/** Gets the control flow node for the exception raised by this raise statement */
|
/** Gets the control flow node for the exception raised by this raise statement */
|
||||||
ControlFlowNode getException() {
|
ControlFlowNode getException() {
|
||||||
exists(Raise r |
|
exists(Py::Raise r |
|
||||||
r = toAst(this) and
|
r = toAst(this) and
|
||||||
r.getException() = toAst(result) and
|
r.getException() = toAst(result) and
|
||||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||||
@@ -827,36 +829,36 @@ class RaiseStmtNode extends ControlFlowNode {
|
|||||||
*/
|
*/
|
||||||
class NameNode extends ControlFlowNode {
|
class NameNode extends ControlFlowNode {
|
||||||
NameNode() {
|
NameNode() {
|
||||||
exists(Name n | py_flow_bb_node(this, n, _, _))
|
exists(Py::Name n | py_flow_bb_node(this, n, _, _))
|
||||||
or
|
or
|
||||||
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
exists(Py::PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether this flow node defines the variable `v`. */
|
/** Whether this flow node defines the variable `v`. */
|
||||||
predicate defines(Variable v) {
|
predicate defines(Py::Variable v) {
|
||||||
exists(Name d | this.getNode() = d and d.defines(v)) and
|
exists(Py::Name d | this.getNode() = d and d.defines(v)) and
|
||||||
not this.isLoad()
|
not this.isLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether this flow node deletes the variable `v`. */
|
/** Whether this flow node deletes the variable `v`. */
|
||||||
predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) }
|
predicate deletes(Py::Variable v) { exists(Py::Name d | this.getNode() = d and d.deletes(v)) }
|
||||||
|
|
||||||
/** Whether this flow node uses the variable `v`. */
|
/** Whether this flow node uses the variable `v`. */
|
||||||
predicate uses(Variable v) {
|
predicate uses(Py::Variable v) {
|
||||||
this.isLoad() and
|
this.isLoad() and
|
||||||
exists(Name u | this.getNode() = u and u.uses(v))
|
exists(Py::Name u | this.getNode() = u and u.uses(v))
|
||||||
or
|
or
|
||||||
exists(PlaceHolder u |
|
exists(Py::PlaceHolder u |
|
||||||
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load
|
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Py::Load
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
Scopes::use_of_global_variable(this, v.getScope(), v.getId())
|
Scopes::use_of_global_variable(this, v.getScope(), v.getId())
|
||||||
}
|
}
|
||||||
|
|
||||||
string getId() {
|
string getId() {
|
||||||
result = this.getNode().(Name).getId()
|
result = this.getNode().(Py::Name).getId()
|
||||||
or
|
or
|
||||||
result = this.getNode().(PlaceHolder).getId()
|
result = this.getNode().(Py::PlaceHolder).getId()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether this is a use of a local variable. */
|
/** Whether this is a use of a local variable. */
|
||||||
@@ -868,82 +870,84 @@ class NameNode extends ControlFlowNode {
|
|||||||
/** Whether this is a use of a global (including builtin) variable. */
|
/** Whether this is a use of a global (including builtin) variable. */
|
||||||
predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) }
|
predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) }
|
||||||
|
|
||||||
predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) }
|
predicate isSelf() {
|
||||||
|
exists(Py::SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
||||||
class NameConstantNode extends NameNode {
|
class NameConstantNode extends NameNode {
|
||||||
NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) }
|
NameConstantNode() { exists(Py::NameConstant n | py_flow_bb_node(this, n, _, _)) }
|
||||||
/*
|
/*
|
||||||
* We ought to override uses as well, but that has
|
* We ought to override uses as well, but that has
|
||||||
* a serious performance impact.
|
* a serious performance impact.
|
||||||
* deprecated predicate uses(Variable v) { none() }
|
* deprecated predicate uses(Py::Variable v) { none() }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A control flow node corresponding to a starred expression, `*a`. */
|
/** A control flow node corresponding to a starred expression, `*a`. */
|
||||||
class StarredNode extends ControlFlowNode {
|
class StarredNode extends ControlFlowNode {
|
||||||
StarredNode() { toAst(this) instanceof Starred }
|
StarredNode() { toAst(this) instanceof Py::Starred }
|
||||||
|
|
||||||
ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() }
|
ControlFlowNode getValue() { toAst(result) = toAst(this).(Py::Starred).getValue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The ControlFlowNode for an 'except' statement. */
|
/** The ControlFlowNode for an 'except' statement. */
|
||||||
class ExceptFlowNode extends ControlFlowNode {
|
class ExceptFlowNode extends ControlFlowNode {
|
||||||
ExceptFlowNode() { this.getNode() instanceof ExceptStmt }
|
ExceptFlowNode() { this.getNode() instanceof Py::ExceptStmt }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the type handled by this exception handler.
|
* Gets the type handled by this exception handler.
|
||||||
* `ExceptionType` in `except ExceptionType as e:`
|
* `Py::ExceptionType` in `except Py::ExceptionType as e:`
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getType() {
|
ControlFlowNode getType() {
|
||||||
exists(ExceptStmt ex |
|
exists(Py::ExceptStmt ex |
|
||||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||||
ex = this.getNode() and
|
ex = this.getNode() and
|
||||||
result = ex.getType().getAFlowNode()
|
result.getNode() = ex.getType()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name assigned to the handled exception, if any.
|
* Gets the name assigned to the handled exception, if any.
|
||||||
* `e` in `except ExceptionType as e:`
|
* `e` in `except Py::ExceptionType as e:`
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getName() {
|
ControlFlowNode getName() {
|
||||||
exists(ExceptStmt ex |
|
exists(Py::ExceptStmt ex |
|
||||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||||
ex = this.getNode() and
|
ex = this.getNode() and
|
||||||
result = ex.getName().getAFlowNode()
|
result.getNode() = ex.getName()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The ControlFlowNode for an 'except*' statement. */
|
/** The ControlFlowNode for an 'except*' statement. */
|
||||||
class ExceptGroupFlowNode extends ControlFlowNode {
|
class ExceptGroupFlowNode extends ControlFlowNode {
|
||||||
ExceptGroupFlowNode() { this.getNode() instanceof ExceptGroupStmt }
|
ExceptGroupFlowNode() { this.getNode() instanceof Py::ExceptGroupStmt }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the type handled by this exception handler.
|
* Gets the type handled by this exception handler.
|
||||||
* `ExceptionType` in `except* ExceptionType as e:`
|
* `Py::ExceptionType` in `except* Py::ExceptionType as e:`
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getType() {
|
ControlFlowNode getType() {
|
||||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||||
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
|
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getType()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name assigned to the handled exception, if any.
|
* Gets the name assigned to the handled exception, if any.
|
||||||
* `e` in `except* ExceptionType as e:`
|
* `e` in `except* Py::ExceptionType as e:`
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getName() {
|
ControlFlowNode getName() {
|
||||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||||
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
|
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private module Scopes {
|
private module Scopes {
|
||||||
private predicate fast_local(NameNode n) {
|
private predicate fast_local(NameNode n) {
|
||||||
exists(FastLocalVariable v |
|
exists(Py::FastLocalVariable v |
|
||||||
n.uses(v) and
|
n.uses(v) and
|
||||||
v.getScope() = n.getScope()
|
v.getScope() = n.getScope()
|
||||||
)
|
)
|
||||||
@@ -952,15 +956,15 @@ private module Scopes {
|
|||||||
predicate local(NameNode n) {
|
predicate local(NameNode n) {
|
||||||
fast_local(n)
|
fast_local(n)
|
||||||
or
|
or
|
||||||
exists(SsaVariable var |
|
exists(Py::SsaVariable var |
|
||||||
var.getAUse() = n and
|
var.getAUse() = n and
|
||||||
n.getScope() instanceof Class and
|
n.getScope() instanceof Py::Class and
|
||||||
exists(var.getDefinition())
|
exists(var.getDefinition())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate non_local(NameNode n) {
|
predicate non_local(NameNode n) {
|
||||||
exists(FastLocalVariable flv |
|
exists(Py::FastLocalVariable flv |
|
||||||
flv.getALoad() = n.getNode() and
|
flv.getALoad() = n.getNode() and
|
||||||
not flv.getScope() = n.getScope()
|
not flv.getScope() = n.getScope()
|
||||||
)
|
)
|
||||||
@@ -968,20 +972,20 @@ private module Scopes {
|
|||||||
|
|
||||||
// magic is fine, but we get questionable join-ordering of it
|
// magic is fine, but we get questionable join-ordering of it
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate use_of_global_variable(NameNode n, Module scope, string name) {
|
predicate use_of_global_variable(NameNode n, Py::Module scope, string name) {
|
||||||
n.isLoad() and
|
n.isLoad() and
|
||||||
not non_local(n) and
|
not non_local(n) and
|
||||||
not exists(SsaVariable var | var.getAUse() = n |
|
not exists(Py::SsaVariable var | var.getAUse() = n |
|
||||||
var.getVariable() instanceof FastLocalVariable
|
var.getVariable() instanceof Py::FastLocalVariable
|
||||||
or
|
or
|
||||||
n.getScope() instanceof Class and
|
n.getScope() instanceof Py::Class and
|
||||||
not maybe_undefined(var)
|
not maybe_undefined(var)
|
||||||
) and
|
) and
|
||||||
name = n.getId() and
|
name = n.getId() and
|
||||||
scope = n.getEnclosingModule()
|
scope = n.getEnclosingModule()
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate maybe_undefined(SsaVariable var) {
|
private predicate maybe_undefined(Py::SsaVariable var) {
|
||||||
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
||||||
or
|
or
|
||||||
var.getDefinition().isDelete()
|
var.getDefinition().isDelete()
|
||||||
@@ -1058,13 +1062,13 @@ class BasicBlock extends @py_flow_node {
|
|||||||
private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() }
|
private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() }
|
||||||
|
|
||||||
private predicate startLocationInfo(string file, int line, int col) {
|
private predicate startLocationInfo(string file, int line, int col) {
|
||||||
if this.firstNode().getNode() instanceof Scope
|
if this.firstNode().getNode() instanceof Py::Scope
|
||||||
then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _)
|
then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _)
|
||||||
else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _)
|
else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate endLocationInfo(int endl, int endc) {
|
private predicate endLocationInfo(int endl, int endc) {
|
||||||
if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock()
|
if this.getLastNode().getNode() instanceof Py::Scope and not this.oneNodeBlock()
|
||||||
then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc)
|
then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc)
|
||||||
else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc)
|
else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc)
|
||||||
}
|
}
|
||||||
@@ -1081,7 +1085,7 @@ class BasicBlock extends @py_flow_node {
|
|||||||
|
|
||||||
/** Whether flow from this basic block reaches a normal exit from its scope */
|
/** Whether flow from this basic block reaches a normal exit from its scope */
|
||||||
predicate reachesExit() {
|
predicate reachesExit() {
|
||||||
exists(Scope s | s.getANormalExit().getBasicBlock() = this)
|
exists(Py::Scope s | s.getANormalExit().getBasicBlock() = this)
|
||||||
or
|
or
|
||||||
this.getASuccessor().reachesExit()
|
this.getASuccessor().reachesExit()
|
||||||
}
|
}
|
||||||
@@ -1122,7 +1126,7 @@ class BasicBlock extends @py_flow_node {
|
|||||||
|
|
||||||
/** Gets the scope of this block */
|
/** Gets the scope of this block */
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
Scope getScope() {
|
Py::Scope getScope() {
|
||||||
exists(ControlFlowNode n | n.getBasicBlock() = this |
|
exists(ControlFlowNode n | n.getBasicBlock() = this |
|
||||||
/* Take care not to use an entry or exit node as that node's scope will be the outer scope */
|
/* Take care not to use an entry or exit node as that node's scope will be the outer scope */
|
||||||
not py_scope_flow(n, _, -1) and
|
not py_scope_flow(n, _, -1) and
|
||||||
@@ -1145,17 +1149,17 @@ class BasicBlock extends @py_flow_node {
|
|||||||
predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) }
|
predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the `ConditionBlock`, if any, that controls this block and
|
* Gets the `Py::ConditionBlock`, if any, that controls this block and
|
||||||
* does not control any other `ConditionBlock`s that control this block.
|
* does not control any other `Py::ConditionBlock`s that control this block.
|
||||||
* That is the `ConditionBlock` that is closest dominator.
|
* That is the `Py::ConditionBlock` that is closest dominator.
|
||||||
*/
|
*/
|
||||||
ConditionBlock getImmediatelyControllingBlock() {
|
Py::ConditionBlock getImmediatelyControllingBlock() {
|
||||||
result = this.nonControllingImmediateDominator*().getImmediateDominator()
|
result = this.nonControllingImmediateDominator*().getImmediateDominator()
|
||||||
}
|
}
|
||||||
|
|
||||||
private BasicBlock nonControllingImmediateDominator() {
|
private BasicBlock nonControllingImmediateDominator() {
|
||||||
result = this.getImmediateDominator() and
|
result = this.getImmediateDominator() and
|
||||||
not result.(ConditionBlock).controls(this, _)
|
not result.(Py::ConditionBlock).controls(this, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1175,7 +1179,7 @@ private class ControlFlowNodeAlias = ControlFlowNode;
|
|||||||
|
|
||||||
final private class FinalBasicBlock = BasicBlock;
|
final private class FinalBasicBlock = BasicBlock;
|
||||||
|
|
||||||
module Cfg implements BB::CfgSig<Location> {
|
module Cfg implements BB::CfgSig<Py::Location> {
|
||||||
private import codeql.controlflow.SuccessorType
|
private import codeql.controlflow.SuccessorType
|
||||||
|
|
||||||
class ControlFlowNode = ControlFlowNodeAlias;
|
class ControlFlowNode = ControlFlowNodeAlias;
|
||||||
@@ -1186,7 +1190,7 @@ module Cfg implements BB::CfgSig<Location> {
|
|||||||
// Using the location of the first node is simple
|
// Using the location of the first node is simple
|
||||||
// and we just need a way to identify the basic block
|
// and we just need a way to identify the basic block
|
||||||
// during debugging, so this will be serviceable.
|
// during debugging, so this will be serviceable.
|
||||||
Location getLocation() { result = super.getNode(0).getLocation() }
|
Py::Location getLocation() { result = super.getNode(0).getLocation() }
|
||||||
|
|
||||||
int length() { result = count(int i | exists(this.getNode(i))) }
|
int length() { result = count(int i | exists(this.getNode(i))) }
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ overlay[local]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
import python
|
import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function, independent of defaults and binding.
|
* A function, independent of defaults and binding.
|
||||||
@@ -153,8 +154,16 @@ class Function extends Function_, Scope, AstNode {
|
|||||||
|
|
||||||
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
|
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
|
||||||
|
|
||||||
/** Gets a control flow node for a return value of this function */
|
/**
|
||||||
ControlFlowNode getAReturnValueFlowNode() {
|
* DEPRECATED: bind a `Return` node explicitly instead, e.g.
|
||||||
|
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
|
||||||
|
* This API is being phased out together with `AstNode.getAFlowNode()` to
|
||||||
|
* untangle the AST and CFG hierarchies.
|
||||||
|
*
|
||||||
|
* Gets a control flow node for a return value of this function, from the
|
||||||
|
* new (shared) CFG.
|
||||||
|
*/
|
||||||
|
deprecated Cfg::ControlFlowNode getAReturnValueFlowNode() {
|
||||||
exists(Return ret |
|
exists(Return ret |
|
||||||
ret.getScope() = this and
|
ret.getScope() = this and
|
||||||
ret.getValue() = result.getNode()
|
ret.getValue() = result.getNode()
|
||||||
|
|||||||
@@ -162,8 +162,6 @@ class ImportMember extends ImportMember_ {
|
|||||||
string getImportedModuleName() {
|
string getImportedModuleName() {
|
||||||
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
|
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
|
||||||
}
|
}
|
||||||
|
|
||||||
override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An import statement */
|
/** An import statement */
|
||||||
|
|||||||
@@ -46,20 +46,23 @@ class SelfAttributeRead extends SelfAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate guardedByHasattr() {
|
predicate guardedByHasattr() {
|
||||||
exists(Variable var, ControlFlowNode n |
|
exists(Variable var, ControlFlowNode n, ControlFlowNode this_, ControlFlowNode obj_ |
|
||||||
var.getAUse() = this.getObject().getAFlowNode() and
|
this_.getNode() = this and obj_.getNode() = this.getObject()
|
||||||
|
|
|
||||||
|
var.getAUse() = obj_ and
|
||||||
hasattr(n, var.getAUse(), this.getName()) and
|
hasattr(n, var.getAUse(), this.getName()) and
|
||||||
n.strictlyDominates(this.getAFlowNode())
|
n.strictlyDominates(this_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
predicate locallyDefined() {
|
predicate locallyDefined() {
|
||||||
exists(SelfAttributeStore store |
|
exists(SelfAttributeStore store, ControlFlowNode store_, ControlFlowNode this_ |
|
||||||
this.getName() = store.getName() and
|
store_.getNode() = store and this_.getNode() = this
|
||||||
this.getScope() = store.getScope()
|
|
||||||
|
|
|
|
||||||
store.getAFlowNode().strictlyDominates(this.getAFlowNode())
|
this.getName() = store.getName() and
|
||||||
|
this.getScope() = store.getScope() and
|
||||||
|
store_.strictlyDominates(this_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1688
python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll
Normal file
1688
python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll
Normal file
File diff suppressed because it is too large
Load Diff
1163
python/ql/lib/semmle/python/controlflow/internal/Cfg.qll
Normal file
1163
python/ql/lib/semmle/python/controlflow/internal/Cfg.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,43 @@
|
|||||||
/** Provides commonly used BarrierGuards. */
|
/** Provides commonly used BarrierGuards. */
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
|
|
||||||
private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
private predicate constCompare(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
|
||||||
exists(CompareNode cn | cn = g |
|
exists(Cfg::CompareNode cn | cn = g |
|
||||||
exists(ImmutableLiteral const, Cmpop op |
|
exists(ImmutableLiteral const, Cmpop op, Cfg::ControlFlowNode c |
|
||||||
op = any(Eq eq) and branch = true
|
c.getNode() = const and
|
||||||
or
|
(
|
||||||
op = any(NotEq ne) and branch = false
|
op = any(Eq eq) and branch = true
|
||||||
|
or
|
||||||
|
op = any(NotEq ne) and branch = false
|
||||||
|
)
|
||||||
|
|
|
|
||||||
cn.operands(const.getAFlowNode(), op, node)
|
cn.operands(c, op, node)
|
||||||
or
|
or
|
||||||
cn.operands(node, op, const.getAFlowNode())
|
cn.operands(node, op, c)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(NameConstant const, Cmpop op |
|
exists(NameConstant const, Cmpop op, Cfg::ControlFlowNode c |
|
||||||
op = any(Is is_) and branch = true
|
c.getNode() = const and
|
||||||
or
|
(
|
||||||
op = any(IsNot isn) and branch = false
|
op = any(Is is_) and branch = true
|
||||||
|
or
|
||||||
|
op = any(IsNot isn) and branch = false
|
||||||
|
)
|
||||||
|
|
|
|
||||||
cn.operands(const.getAFlowNode(), op, node)
|
cn.operands(c, op, node)
|
||||||
or
|
or
|
||||||
cn.operands(node, op, const.getAFlowNode())
|
cn.operands(node, op, c)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(IterableNode const_iterable, Cmpop op |
|
exists(Cfg::IterableNode const_iterable, Cmpop op |
|
||||||
op = any(In in_) and branch = true
|
op = any(In in_) and branch = true
|
||||||
or
|
or
|
||||||
op = any(NotIn ni) and branch = false
|
op = any(NotIn ni) and branch = false
|
||||||
|
|
|
|
||||||
forall(ControlFlowNode elem | elem = const_iterable.getAnElement() |
|
forall(Cfg::ControlFlowNode elem | elem = const_iterable.getAnElement() |
|
||||||
elem.getNode() instanceof ImmutableLiteral
|
elem.getNode() instanceof ImmutableLiteral
|
||||||
) and
|
) and
|
||||||
cn.operands(node, op, const_iterable)
|
cn.operands(node, op, const_iterable)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
|
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
|
||||||
private import semmle.python.Frameworks
|
private import semmle.python.Frameworks
|
||||||
@@ -105,7 +106,7 @@ private module SensitiveDataModeling {
|
|||||||
or
|
or
|
||||||
// to cover functions that we don't have the definition for, and where the
|
// 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
|
// reference to the function has not already been marked as being sensitive
|
||||||
this.getFunction().asCfgNode().(NameNode).getId() = sensitiveString(classification)
|
this.getFunction().asCfgNode().(Cfg::NameNode).getId() = sensitiveString(classification)
|
||||||
}
|
}
|
||||||
|
|
||||||
override SensitiveDataClassification getClassification() { result = classification }
|
override SensitiveDataClassification getClassification() { result = classification }
|
||||||
@@ -251,12 +252,12 @@ private module SensitiveDataModeling {
|
|||||||
SensitiveDataClassification classification;
|
SensitiveDataClassification classification;
|
||||||
|
|
||||||
SensitiveVariableAssignment() {
|
SensitiveVariableAssignment() {
|
||||||
exists(DefinitionNode def |
|
exists(Cfg::DefinitionNode def |
|
||||||
def.(NameNode).getId() = sensitiveString(classification) and
|
def.(Cfg::NameNode).getId() = sensitiveString(classification) and
|
||||||
(
|
(
|
||||||
this.asCfgNode() = def.getValue()
|
this.asCfgNode() = def.getValue()
|
||||||
or
|
or
|
||||||
this.asCfgNode() = def.getValue().(ForNode).getSequence()
|
this.asCfgNode() = def.getValue().(Cfg::ForNode).getSequence()
|
||||||
) and
|
) and
|
||||||
not this.asExpr() instanceof FunctionExpr and
|
not this.asExpr() instanceof FunctionExpr and
|
||||||
not this.asExpr() instanceof ClassExpr
|
not this.asExpr() instanceof ClassExpr
|
||||||
@@ -293,7 +294,7 @@ private module SensitiveDataModeling {
|
|||||||
SensitiveDataClassification classification;
|
SensitiveDataClassification classification;
|
||||||
|
|
||||||
SensitiveSubscript() {
|
SensitiveSubscript() {
|
||||||
this.asCfgNode().(SubscriptNode).getIndex() =
|
this.asCfgNode().(Cfg::SubscriptNode).getIndex() =
|
||||||
sensitiveLookupStringConst(classification).asCfgNode()
|
sensitiveLookupStringConst(classification).asCfgNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ overlay[local]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import DataFlowUtil
|
import DataFlowUtil
|
||||||
import DataFlowPublic
|
import DataFlowPublic
|
||||||
private import DataFlowPrivate
|
private import DataFlowPrivate
|
||||||
@@ -83,9 +84,9 @@ abstract class AttrWrite extends AttrRef {
|
|||||||
* ```python
|
* ```python
|
||||||
* object.attr = value
|
* object.attr = value
|
||||||
* ```
|
* ```
|
||||||
* Also gives access to the `value` being written, by extending `DefinitionNode`.
|
* Also gives access to the `value` being written, by extending `Cfg::DefinitionNode`.
|
||||||
*/
|
*/
|
||||||
private class AttributeAssignmentNode extends DefinitionNode, AttrNode { }
|
private class AttributeAssignmentNode extends Cfg::DefinitionNode, Cfg::AttrNode { }
|
||||||
|
|
||||||
/** A simple attribute assignment: `object.attr = value`. */
|
/** A simple attribute assignment: `object.attr = value`. */
|
||||||
private class AttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
|
private class AttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
|
||||||
@@ -131,13 +132,13 @@ private class GlobalAttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
|
|||||||
override string getAttributeName() { result = node.getName() }
|
override string getAttributeName() { result = node.getName() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents `CallNode`s that may refer to calls to built-in functions or classes. */
|
/** Represents `Cfg::CallNode`s that may refer to calls to built-in functions or classes. */
|
||||||
private class BuiltInCallNode extends CallNode {
|
private class BuiltInCallNode extends Cfg::CallNode {
|
||||||
string name;
|
string name;
|
||||||
|
|
||||||
BuiltInCallNode() {
|
BuiltInCallNode() {
|
||||||
// TODO disallow instances where the name of the built-in may refer to an in-scope variable of that name.
|
// TODO disallow instances where the name of the built-in may refer to an in-scope variable of that name.
|
||||||
exists(NameNode id |
|
exists(Cfg::NameNode id |
|
||||||
name = Builtins::getBuiltinName() and
|
name = Builtins::getBuiltinName() and
|
||||||
this.getFunction() = id and
|
this.getFunction() = id and
|
||||||
id.getId() = name and
|
id.getId() = name and
|
||||||
@@ -145,7 +146,7 @@ private class BuiltInCallNode extends CallNode {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the name of the built-in function that is called at this `CallNode` */
|
/** Gets the name of the built-in function that is called at this `Cfg::CallNode` */
|
||||||
string getBuiltinName() { result = name }
|
string getBuiltinName() { result = name }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,20 +158,20 @@ private class BuiltinAttrCallNode extends BuiltInCallNode {
|
|||||||
BuiltinAttrCallNode() { name in ["setattr", "getattr", "hasattr", "delattr"] }
|
BuiltinAttrCallNode() { name in ["setattr", "getattr", "hasattr", "delattr"] }
|
||||||
|
|
||||||
/** Gets the control flow node for object on which the attribute is accessed. */
|
/** Gets the control flow node for object on which the attribute is accessed. */
|
||||||
ControlFlowNode getObject() { result in [this.getArg(0), this.getArgByName("object")] }
|
Cfg::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.
|
* Gets the control flow node for the value that is being written to the attribute.
|
||||||
* Only relevant for `setattr` calls.
|
* Only relevant for `setattr` calls.
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getValue() {
|
Cfg::ControlFlowNode getValue() {
|
||||||
// only valid for `setattr`
|
// only valid for `setattr`
|
||||||
name = "setattr" and
|
name = "setattr" and
|
||||||
result in [this.getArg(2), this.getArgByName("value")]
|
result in [this.getArg(2), this.getArgByName("value")]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the control flow node that defines the name of the attribute being accessed. */
|
/** Gets the control flow node that defines the name of the attribute being accessed. */
|
||||||
ControlFlowNode getName() { result in [this.getArg(1), this.getArgByName("name")] }
|
Cfg::ControlFlowNode getName() { result in [this.getArg(1), this.getArgByName("name")] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents calls to the built-in `setattr`. */
|
/** Represents calls to the built-in `setattr`. */
|
||||||
@@ -205,10 +206,10 @@ private class SetAttrCallAsAttrWrite extends AttrWrite, CfgNode {
|
|||||||
* attr = value
|
* attr = value
|
||||||
* ...
|
* ...
|
||||||
* ```
|
* ```
|
||||||
* Instances of this class correspond to the `NameNode` for `attr`, and also gives access to `value` by
|
* Instances of this class correspond to the `Cfg::NameNode` for `attr`, and also gives access to `value` by
|
||||||
* virtue of being a `DefinitionNode`.
|
* virtue of being a `Cfg::DefinitionNode`.
|
||||||
*/
|
*/
|
||||||
private class ClassAttributeAssignmentNode extends DefinitionNode, NameNode {
|
private class ClassAttributeAssignmentNode extends Cfg::DefinitionNode, Cfg::NameNode {
|
||||||
ClassAttributeAssignmentNode() { this.getScope() = any(ClassExpr c).getInnerScope() }
|
ClassAttributeAssignmentNode() { this.getScope() = any(ClassExpr c).getInnerScope() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +229,7 @@ private class ClassDefinitionAsAttrWrite extends AttrWrite, CfgNode {
|
|||||||
|
|
||||||
override Node getValue() { result.asCfgNode() = node.getValue() }
|
override Node getValue() { result.asCfgNode() = node.getValue() }
|
||||||
|
|
||||||
override Node getObject() { result.asCfgNode() = cls.getAFlowNode() }
|
override Node getObject() { result.asCfgNode().getNode() = cls }
|
||||||
|
|
||||||
override ExprNode getAttributeNameExpr() { none() }
|
override ExprNode getAttributeNameExpr() { none() }
|
||||||
|
|
||||||
@@ -248,7 +249,7 @@ abstract class AttrRead extends AttrRef, Node, LocalSourceNode {
|
|||||||
|
|
||||||
/** A simple attribute read, e.g. `object.attr` */
|
/** A simple attribute read, e.g. `object.attr` */
|
||||||
private class AttributeReadAsAttrRead extends AttrRead, CfgNode {
|
private class AttributeReadAsAttrRead extends AttrRead, CfgNode {
|
||||||
override AttrNode node;
|
override Cfg::AttrNode node;
|
||||||
|
|
||||||
AttributeReadAsAttrRead() { node.isLoad() }
|
AttributeReadAsAttrRead() { node.isLoad() }
|
||||||
|
|
||||||
@@ -285,7 +286,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.
|
* 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 {
|
private class ModuleAttributeImportAsAttrRead extends AttrRead, CfgNode {
|
||||||
override ImportMemberNode node;
|
override Cfg::ImportMemberNode node;
|
||||||
|
|
||||||
override Node getObject() { result.asCfgNode() = node.getModule(_) }
|
override Node getObject() { result.asCfgNode() = node.getModule(_) }
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ overlay[local]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.internal.ImportStar
|
private import semmle.python.dataflow.new.internal.ImportStar
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ module Builtins {
|
|||||||
DataFlow::CfgNode likelyBuiltin(string name) {
|
DataFlow::CfgNode likelyBuiltin(string name) {
|
||||||
exists(Module m |
|
exists(Module m |
|
||||||
result.getNode() =
|
result.getNode() =
|
||||||
any(NameNode n |
|
any(Cfg::NameNode n |
|
||||||
possible_builtin_accessed_in_module(n, name, m) and
|
possible_builtin_accessed_in_module(n, name, m) and
|
||||||
not possible_builtin_defined_in_module(name, m)
|
not possible_builtin_defined_in_module(name, m)
|
||||||
)
|
)
|
||||||
@@ -87,7 +88,7 @@ module Builtins {
|
|||||||
* Holds if `n` is an access of a global variable called `name` (which is also the name of a
|
* 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`.
|
* built-in) inside the module `m`.
|
||||||
*/
|
*/
|
||||||
private predicate possible_builtin_accessed_in_module(NameNode n, string name, Module m) {
|
private predicate possible_builtin_accessed_in_module(Cfg::NameNode n, string name, Module m) {
|
||||||
n.isGlobal() and
|
n.isGlobal() and
|
||||||
n.isLoad() and
|
n.isLoad() and
|
||||||
name = n.getId() and
|
name = n.getId() and
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
* what callable this call might end up targeting.
|
* what callable this call might end up targeting.
|
||||||
*
|
*
|
||||||
* Specifically this means that we cannot use type-backtrackers from the function of a
|
* Specifically this means that we cannot use type-backtrackers from the function of a
|
||||||
* `CallNode`, since there is no `CallNode` to backtrack from for `func` in the example
|
* `Cfg::CallNode`, since there is no `Cfg::CallNode` to backtrack from for `func` in the example
|
||||||
* above.
|
* above.
|
||||||
*
|
*
|
||||||
* Note: This hasn't been 100% realized yet, so we don't currently expose a predicate to
|
* Note: This hasn't been 100% realized yet, so we don't currently expose a predicate to
|
||||||
@@ -35,6 +35,7 @@ overlay[local?]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
private import DataFlowPublic
|
private import DataFlowPublic
|
||||||
private import DataFlowPrivate
|
private import DataFlowPrivate
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
@@ -162,7 +163,7 @@ newtype TArgumentPosition =
|
|||||||
*/
|
*/
|
||||||
TLambdaSelfArgumentPosition() or
|
TLambdaSelfArgumentPosition() or
|
||||||
TPositionalArgumentPosition(int index) {
|
TPositionalArgumentPosition(int index) {
|
||||||
exists(any(CallNode c).getArg(index))
|
exists(any(Cfg::CallNode c).getArg(index))
|
||||||
or
|
or
|
||||||
// since synthetic calls within a summarized callable could use a unique argument
|
// 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
|
// position, we need to ensure we make these available (these are specified as
|
||||||
@@ -174,7 +175,7 @@ newtype TArgumentPosition =
|
|||||||
index = 0
|
index = 0
|
||||||
} or
|
} or
|
||||||
TKeywordArgumentPosition(string name) {
|
TKeywordArgumentPosition(string name) {
|
||||||
exists(any(CallNode c).getArgByName(name))
|
exists(any(Cfg::CallNode c).getArgByName(name))
|
||||||
or
|
or
|
||||||
// see comment for TPositionalArgumentPosition
|
// see comment for TPositionalArgumentPosition
|
||||||
FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name)
|
FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name)
|
||||||
@@ -297,10 +298,12 @@ predicate hasPropertyDecorator(Function func) {
|
|||||||
*/
|
*/
|
||||||
overlay[local]
|
overlay[local]
|
||||||
predicate hasContextmanagerDecorator(Function func) {
|
predicate hasContextmanagerDecorator(Function func) {
|
||||||
exists(ControlFlowNode contextmanager |
|
exists(Cfg::ControlFlowNode contextmanager |
|
||||||
contextmanager.(NameNode).getId() = "contextmanager" and contextmanager.(NameNode).isGlobal()
|
contextmanager.(Cfg::NameNode).getId() = "contextmanager" and
|
||||||
|
contextmanager.(Cfg::NameNode).isGlobal()
|
||||||
or
|
or
|
||||||
contextmanager.(AttrNode).getObject("contextmanager").(NameNode).getId() = "contextlib"
|
contextmanager.(Cfg::AttrNode).getObject("contextmanager").(Cfg::NameNode).getId() =
|
||||||
|
"contextlib"
|
||||||
|
|
|
|
||||||
func.getADecorator() = contextmanager.getNode()
|
func.getADecorator() = contextmanager.getNode()
|
||||||
)
|
)
|
||||||
@@ -316,10 +319,10 @@ predicate hasContextmanagerDecorator(Function func) {
|
|||||||
*/
|
*/
|
||||||
overlay[local]
|
overlay[local]
|
||||||
private predicate hasOverloadDecorator(Function func) {
|
private predicate hasOverloadDecorator(Function func) {
|
||||||
exists(ControlFlowNode overload |
|
exists(Cfg::ControlFlowNode overload |
|
||||||
overload.(NameNode).getId() = "overload" and overload.(NameNode).isGlobal()
|
overload.(Cfg::NameNode).getId() = "overload" and overload.(Cfg::NameNode).isGlobal()
|
||||||
or
|
or
|
||||||
overload.(AttrNode).getObject("overload").(NameNode).isGlobal()
|
overload.(Cfg::AttrNode).getObject("overload").(Cfg::NameNode).isGlobal()
|
||||||
|
|
|
|
||||||
func.getADecorator() = overload.getNode()
|
func.getADecorator() = overload.getNode()
|
||||||
)
|
)
|
||||||
@@ -538,7 +541,7 @@ class LibraryCallableValue extends DataFlowCallable, TLibraryCallable {
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
/** Gets a call to `type`. */
|
/** Gets a call to `type`. */
|
||||||
private CallCfgNode getTypeCall() {
|
private CallCfgNode getTypeCall() {
|
||||||
exists(NameNode id | id.getId() = "type" and id.isGlobal() |
|
exists(Cfg::NameNode id | id.getId() = "type" and id.isGlobal() |
|
||||||
result.getFunction().asCfgNode() = id
|
result.getFunction().asCfgNode() = id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -550,7 +553,7 @@ private CallCfgNode getSuperCall() {
|
|||||||
// link below), but otherwise only 2 edgecases. Overall it seems ok to ignore this complexity.
|
// 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
|
// https://github.com/python/cpython/blob/18b1782192f85bd26db89f5bc850f8bee4247c1a/Lib/unittest/mock.py#L48-L50
|
||||||
exists(NameNode id | id.getId() = "super" and id.isGlobal() |
|
exists(Cfg::NameNode id | id.getId() = "super" and id.isGlobal() |
|
||||||
result.getFunction().asCfgNode() = id
|
result.getFunction().asCfgNode() = id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1036,7 +1039,7 @@ private module MethodCalls {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate directCall(
|
private predicate directCall(
|
||||||
CallNode call, Function target, string functionName, Class cls, AttrRead attr, Node self
|
Cfg::CallNode call, Function target, string functionName, Class cls, AttrRead attr, Node self
|
||||||
) {
|
) {
|
||||||
target = findFunctionAccordingToMroKnownStartingClass(cls, functionName) and
|
target = findFunctionAccordingToMroKnownStartingClass(cls, functionName) and
|
||||||
directCall_join(call, functionName, cls, attr, self)
|
directCall_join(call, functionName, cls, attr, self)
|
||||||
@@ -1045,7 +1048,7 @@ private module MethodCalls {
|
|||||||
/** Extracted to give good join order */
|
/** Extracted to give good join order */
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate directCall_join(
|
private predicate directCall_join(
|
||||||
CallNode call, string functionName, Class cls, AttrRead attr, Node self
|
Cfg::CallNode call, string functionName, Class cls, AttrRead attr, Node self
|
||||||
) {
|
) {
|
||||||
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
||||||
attr.accesses(self, functionName) and
|
attr.accesses(self, functionName) and
|
||||||
@@ -1062,7 +1065,7 @@ private module MethodCalls {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate callWithinMethodImplicitSelfOrCls(
|
private predicate callWithinMethodImplicitSelfOrCls(
|
||||||
CallNode call, Function target, string functionName, Class classWithMethod, AttrRead attr,
|
Cfg::CallNode call, Function target, string functionName, Class classWithMethod, AttrRead attr,
|
||||||
Node self
|
Node self
|
||||||
) {
|
) {
|
||||||
target = findFunctionAccordingToMro(getADirectSubclass*(classWithMethod), functionName) and
|
target = findFunctionAccordingToMro(getADirectSubclass*(classWithMethod), functionName) and
|
||||||
@@ -1072,7 +1075,7 @@ private module MethodCalls {
|
|||||||
/** Extracted to give good join order */
|
/** Extracted to give good join order */
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate callWithinMethodImplicitSelfOrCls_join(
|
private predicate callWithinMethodImplicitSelfOrCls_join(
|
||||||
CallNode call, string functionName, Class classWithMethod, AttrRead attr, Node self
|
Cfg::CallNode call, string functionName, Class classWithMethod, AttrRead attr, Node self
|
||||||
) {
|
) {
|
||||||
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
||||||
attr.accesses(self, functionName) and
|
attr.accesses(self, functionName) and
|
||||||
@@ -1084,7 +1087,7 @@ private module MethodCalls {
|
|||||||
* resolve the call to a known target (since the only super class might be the
|
* 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).
|
* builtin `object`, so we never have the implementation of `__new__` in the DB).
|
||||||
*/
|
*/
|
||||||
predicate fromSuperNewCall(CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
|
predicate fromSuperNewCall(Cfg::CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
|
||||||
fromSuper_join(call, "__new__", classUsedInSuper, attr, self) and
|
fromSuper_join(call, "__new__", classUsedInSuper, attr, self) and
|
||||||
self in [classTracker(_), clsArgumentTracker(_)]
|
self in [classTracker(_), clsArgumentTracker(_)]
|
||||||
}
|
}
|
||||||
@@ -1106,7 +1109,7 @@ private module MethodCalls {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate fromSuper(
|
predicate fromSuper(
|
||||||
CallNode call, Function target, string functionName, Class classUsedInSuper, AttrRead attr,
|
Cfg::CallNode call, Function target, string functionName, Class classUsedInSuper, AttrRead attr,
|
||||||
Node self
|
Node self
|
||||||
) {
|
) {
|
||||||
target = findFunctionAccordingToMro(getNextClassInMro(classUsedInSuper), functionName) and
|
target = findFunctionAccordingToMro(getNextClassInMro(classUsedInSuper), functionName) and
|
||||||
@@ -1116,7 +1119,7 @@ private module MethodCalls {
|
|||||||
/** Extracted to give good join order */
|
/** Extracted to give good join order */
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate fromSuper_join(
|
private predicate fromSuper_join(
|
||||||
CallNode call, string functionName, Class classUsedInSuper, AttrRead attr, Node self
|
Cfg::CallNode call, string functionName, Class classUsedInSuper, AttrRead attr, Node self
|
||||||
) {
|
) {
|
||||||
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
call.getFunction() = attrReadTracker(attr).asCfgNode() and
|
||||||
(
|
(
|
||||||
@@ -1135,7 +1138,7 @@ private module MethodCalls {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate resolveMethodCall(CallNode call, Function target, CallType type, Node self) {
|
predicate resolveMethodCall(Cfg::CallNode call, Function target, CallType type, Node self) {
|
||||||
(
|
(
|
||||||
directCall(call, target, _, _, _, self)
|
directCall(call, target, _, _, _, self)
|
||||||
or
|
or
|
||||||
@@ -1182,7 +1185,7 @@ import MethodCalls
|
|||||||
* NOTE: We have this predicate mostly to be able to compare with old point-to
|
* 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.
|
* call-graph resolution. So it could be removed in the future.
|
||||||
*/
|
*/
|
||||||
predicate resolveClassCall(CallNode call, Class cls) {
|
predicate resolveClassCall(Cfg::CallNode call, Class cls) {
|
||||||
call.getFunction() = classTracker(cls).asCfgNode()
|
call.getFunction() = classTracker(cls).asCfgNode()
|
||||||
or
|
or
|
||||||
// `cls()` inside a classmethod (which also contains `type(self)()` inside a method)
|
// `cls()` inside a classmethod (which also contains `type(self)()` inside a method)
|
||||||
@@ -1212,7 +1215,7 @@ Function invokedFunctionFromClassConstruction(Class cls, string funcName) {
|
|||||||
*
|
*
|
||||||
* See https://docs.python.org/3/reference/datamodel.html#object.__call__
|
* See https://docs.python.org/3/reference/datamodel.html#object.__call__
|
||||||
*/
|
*/
|
||||||
predicate resolveClassInstanceCall(CallNode call, Function target, Node self) {
|
predicate resolveClassInstanceCall(Cfg::CallNode call, Function target, Node self) {
|
||||||
exists(Class cls |
|
exists(Class cls |
|
||||||
call.getFunction() = classInstanceTracker(cls).asCfgNode() and
|
call.getFunction() = classInstanceTracker(cls).asCfgNode() and
|
||||||
target = findFunctionAccordingToMroKnownStartingClass(cls, "__call__")
|
target = findFunctionAccordingToMroKnownStartingClass(cls, "__call__")
|
||||||
@@ -1231,7 +1234,7 @@ predicate resolveClassInstanceCall(CallNode call, Function target, Node self) {
|
|||||||
* Holds if `call` is a call to the `target`, with call-type `type`.
|
* Holds if `call` is a call to the `target`, with call-type `type`.
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate resolveCall(CallNode call, Function target, CallType type) {
|
predicate resolveCall(Cfg::CallNode call, Function target, CallType type) {
|
||||||
Stages::DataFlow::ref() and
|
Stages::DataFlow::ref() and
|
||||||
(
|
(
|
||||||
type instanceof CallTypePlainFunction and
|
type instanceof CallTypePlainFunction and
|
||||||
@@ -1256,11 +1259,11 @@ predicate resolveCall(CallNode call, Function target, CallType type) {
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
/**
|
/**
|
||||||
* Holds if the argument of `call` at position `apos` is `arg`. This is just a helper
|
* 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 `CallNode`.
|
* predicate that maps ArgumentPositions to the arguments of the underlying `Cfg::CallNode`.
|
||||||
*/
|
*/
|
||||||
overlay[local]
|
overlay[local]
|
||||||
cached
|
cached
|
||||||
predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
|
predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) {
|
||||||
exists(int index |
|
exists(int index |
|
||||||
apos.isPositional(index) and
|
apos.isPositional(index) and
|
||||||
arg.asCfgNode() = call.getArg(index)
|
arg.asCfgNode() = call.getArg(index)
|
||||||
@@ -1275,7 +1278,7 @@ predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
|
|||||||
exists(int index |
|
exists(int index |
|
||||||
apos.isStarArgs(index) and
|
apos.isStarArgs(index) and
|
||||||
arg.asCfgNode() = call.getStarArg() and
|
arg.asCfgNode() = call.getStarArg() and
|
||||||
// since `CallNode.getArg` doesn't include `*args`, we need to drop to the AST level
|
// since `Cfg::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
|
// 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.
|
// don't need to check for dominance in regards to splitting.
|
||||||
call.getStarArg().getNode() = call.getNode().getPositionalArg(index).(Starred).getValue()
|
call.getStarArg().getNode() = call.getNode().getPositionalArg(index).(Starred).getValue()
|
||||||
@@ -1349,7 +1352,9 @@ predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
|
|||||||
* translated into `l.clear()`, and we can still have use-use flow.
|
* translated into `l.clear()`, and we can still have use-use flow.
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate getCallArg(CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos) {
|
predicate getCallArg(
|
||||||
|
Cfg::CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos
|
||||||
|
) {
|
||||||
Stages::DataFlow::ref() and
|
Stages::DataFlow::ref() and
|
||||||
resolveCall(call, target, type) and
|
resolveCall(call, target, type) and
|
||||||
(
|
(
|
||||||
@@ -1442,10 +1447,13 @@ private predicate sameEnclosingCallable(Node node1, Node node2) {
|
|||||||
// DataFlowCall
|
// DataFlowCall
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
newtype TDataFlowCall =
|
newtype TDataFlowCall =
|
||||||
TNormalCall(CallNode call, Function target, CallType type) { resolveCall(call, target, type) } or
|
TNormalCall(Cfg::CallNode call, Function target, CallType type) {
|
||||||
|
resolveCall(call, target, type) and
|
||||||
|
Cfg::isCanonicalAstNodeRepresentative(call)
|
||||||
|
} or
|
||||||
/** A call to the generated function inside a comprehension */
|
/** A call to the generated function inside a comprehension */
|
||||||
TComprehensionCall(Comp c) or
|
TComprehensionCall(Comp c) or
|
||||||
TPotentialLibraryCall(CallNode call) or
|
TPotentialLibraryCall(Cfg::CallNode call) { Cfg::isCanonicalAstNodeRepresentative(call) } or
|
||||||
/** A synthesized call inside a summarized callable */
|
/** A synthesized call inside a summarized callable */
|
||||||
TSummaryCall(
|
TSummaryCall(
|
||||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||||
@@ -1465,7 +1473,7 @@ abstract class DataFlowCall extends TDataFlowCall {
|
|||||||
abstract ArgumentNode getArgument(ArgumentPosition apos);
|
abstract ArgumentNode getArgument(ArgumentPosition apos);
|
||||||
|
|
||||||
/** Get the control flow node representing this call, if any. */
|
/** Get the control flow node representing this call, if any. */
|
||||||
abstract ControlFlowNode getNode();
|
abstract Cfg::ControlFlowNode getNode();
|
||||||
|
|
||||||
/** Gets the enclosing callable of this call. */
|
/** Gets the enclosing callable of this call. */
|
||||||
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
|
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
|
||||||
@@ -1496,28 +1504,28 @@ abstract class ExtractedDataFlowCall extends DataFlowCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A resolved call in source code with an underlying `CallNode`.
|
* A resolved call in source code with an underlying `Cfg::CallNode`.
|
||||||
*
|
*
|
||||||
* This is considered normal, compared with special calls such as `obj[0]` calling the
|
* 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
|
* `__getitem__` method on the object. However, this also includes calls that go to the
|
||||||
* `__call__` special method.
|
* `__call__` special method.
|
||||||
*/
|
*/
|
||||||
class NormalCall extends ExtractedDataFlowCall, TNormalCall {
|
class NormalCall extends ExtractedDataFlowCall, TNormalCall {
|
||||||
CallNode call;
|
Cfg::CallNode call;
|
||||||
Function target;
|
Function target;
|
||||||
CallType type;
|
CallType type;
|
||||||
|
|
||||||
NormalCall() { this = TNormalCall(call, target, type) }
|
NormalCall() { this = TNormalCall(call, target, type) }
|
||||||
|
|
||||||
override string toString() {
|
override string toString() {
|
||||||
// note: if we used toString directly on the CallNode we would get
|
// note: if we used toString directly on the Cfg::CallNode we would get
|
||||||
// `ControlFlowNode for func()`
|
// `Cfg::ControlFlowNode for func()`
|
||||||
// but the `ControlFlowNode` part is just clutter, so we go directly to the AST node
|
// but the `Cfg::ControlFlowNode` part is just clutter, so we go directly to the AST node
|
||||||
// instead.
|
// instead.
|
||||||
result = call.getNode().toString()
|
result = call.getNode().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override ControlFlowNode getNode() { result = call }
|
override Cfg::ControlFlowNode getNode() { result = call }
|
||||||
|
|
||||||
override Scope getScope() { result = call.getScope() }
|
override Scope getScope() { result = call.getScope() }
|
||||||
|
|
||||||
@@ -1545,7 +1553,7 @@ class ComprehensionCall extends ExtractedDataFlowCall, TComprehensionCall {
|
|||||||
|
|
||||||
override string toString() { result = "comprehension call" }
|
override string toString() { result = "comprehension call" }
|
||||||
|
|
||||||
override ControlFlowNode getNode() { result.getNode() = c }
|
override Cfg::ControlFlowNode getNode() { result.getNode() = c }
|
||||||
|
|
||||||
override Scope getScope() { result = c.getScope() }
|
override Scope getScope() { result = c.getScope() }
|
||||||
|
|
||||||
@@ -1568,14 +1576,14 @@ class ComprehensionCall extends ExtractedDataFlowCall, TComprehensionCall {
|
|||||||
* in this class.
|
* in this class.
|
||||||
*/
|
*/
|
||||||
class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall {
|
class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall {
|
||||||
CallNode call;
|
Cfg::CallNode call;
|
||||||
|
|
||||||
PotentialLibraryCall() { this = TPotentialLibraryCall(call) }
|
PotentialLibraryCall() { this = TPotentialLibraryCall(call) }
|
||||||
|
|
||||||
override string toString() {
|
override string toString() {
|
||||||
// note: if we used toString directly on the CallNode we would get
|
// note: if we used toString directly on the Cfg::CallNode we would get
|
||||||
// `ControlFlowNode for func()`
|
// `Cfg::ControlFlowNode for func()`
|
||||||
// but the `ControlFlowNode` part is just clutter, so we go directly to the AST node
|
// but the `Cfg::ControlFlowNode` part is just clutter, so we go directly to the AST node
|
||||||
// instead.
|
// instead.
|
||||||
result = call.getNode().toString()
|
result = call.getNode().toString()
|
||||||
}
|
}
|
||||||
@@ -1592,10 +1600,10 @@ class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall
|
|||||||
// potential self argument, from `foo.bar()` -- note that this could also just be a
|
// 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 :|
|
// module reference, but we really don't have a good way of knowing :|
|
||||||
apos.isSelf() and
|
apos.isSelf() and
|
||||||
result.asCfgNode() = call.getFunction().(AttrNode).getObject()
|
result.asCfgNode() = call.getFunction().(Cfg::AttrNode).getObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
override ControlFlowNode getNode() { result = call }
|
override Cfg::ControlFlowNode getNode() { result = call }
|
||||||
|
|
||||||
override Scope getScope() { result = call.getScope() }
|
override Scope getScope() { result = call.getScope() }
|
||||||
}
|
}
|
||||||
@@ -1627,7 +1635,7 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
|
|||||||
|
|
||||||
override ArgumentNode getArgument(ArgumentPosition apos) { none() }
|
override ArgumentNode getArgument(ArgumentPosition apos) { none() }
|
||||||
|
|
||||||
override ControlFlowNode getNode() { none() }
|
override Cfg::ControlFlowNode getNode() { none() }
|
||||||
|
|
||||||
override string toString() { result = "[summary] call to " + receiver + " in " + c }
|
override string toString() { result = "[summary] call to " + receiver + " in " + c }
|
||||||
|
|
||||||
@@ -1769,12 +1777,12 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl
|
|||||||
* This is used for tracking flow through captured variables.
|
* This is used for tracking flow through captured variables.
|
||||||
*/
|
*/
|
||||||
class SynthCapturedVariablesArgumentNode extends Node, TSynthCapturedVariablesArgumentNode {
|
class SynthCapturedVariablesArgumentNode extends Node, TSynthCapturedVariablesArgumentNode {
|
||||||
ControlFlowNode callable;
|
Cfg::ControlFlowNode callable;
|
||||||
|
|
||||||
SynthCapturedVariablesArgumentNode() { this = TSynthCapturedVariablesArgumentNode(callable) }
|
SynthCapturedVariablesArgumentNode() { this = TSynthCapturedVariablesArgumentNode(callable) }
|
||||||
|
|
||||||
/** Gets the `CallNode` corresponding to this captured variables argument node. */
|
/** Gets the `Cfg::CallNode` corresponding to this captured variables argument node. */
|
||||||
CallNode getCallNode() { result.getFunction() = callable }
|
Cfg::CallNode getCallNode() { result.getFunction() = callable }
|
||||||
|
|
||||||
/** Gets the `CfgNode` that corresponds to this synthetic node. */
|
/** Gets the `CfgNode` that corresponds to this synthetic node. */
|
||||||
CfgNode getUnderlyingNode() { result.asCfgNode() = callable }
|
CfgNode getUnderlyingNode() { result.asCfgNode() = callable }
|
||||||
@@ -1792,7 +1800,7 @@ class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
|
|||||||
{
|
{
|
||||||
overlay[global]
|
overlay[global]
|
||||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||||
exists(CallNode callNode | callNode = this.getCallNode() |
|
exists(Cfg::CallNode callNode | callNode = this.getCallNode() |
|
||||||
callNode = call.getNode() and
|
callNode = call.getNode() and
|
||||||
exists(Function target | resolveCall(callNode, target, _) |
|
exists(Function target | resolveCall(callNode, target, _) |
|
||||||
target = any(VariableCapture::CapturedVariable v).getACapturingScope()
|
target = any(VariableCapture::CapturedVariable v).getACapturingScope()
|
||||||
@@ -1806,7 +1814,7 @@ class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
|
|||||||
class SynthCapturedVariablesArgumentPostUpdateNode extends PostUpdateNodeImpl,
|
class SynthCapturedVariablesArgumentPostUpdateNode extends PostUpdateNodeImpl,
|
||||||
TSynthCapturedVariablesArgumentPostUpdateNode
|
TSynthCapturedVariablesArgumentPostUpdateNode
|
||||||
{
|
{
|
||||||
ControlFlowNode callable;
|
Cfg::ControlFlowNode callable;
|
||||||
|
|
||||||
SynthCapturedVariablesArgumentPostUpdateNode() {
|
SynthCapturedVariablesArgumentPostUpdateNode() {
|
||||||
this = TSynthCapturedVariablesArgumentPostUpdateNode(callable)
|
this = TSynthCapturedVariablesArgumentPostUpdateNode(callable)
|
||||||
@@ -1913,8 +1921,8 @@ abstract class ReturnNode extends Node {
|
|||||||
class ExtractedReturnNode extends ReturnNode, CfgNode {
|
class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||||
// See `TaintTrackingImplementation::returnFlowStep`
|
// See `TaintTrackingImplementation::returnFlowStep`
|
||||||
ExtractedReturnNode() {
|
ExtractedReturnNode() {
|
||||||
node = any(Return ret).getValue().getAFlowNode() or
|
node.getNode() = any(Return ret).getValue() or
|
||||||
node = any(Yield yield).getAFlowNode()
|
node.getNode() = any(Yield yield)
|
||||||
}
|
}
|
||||||
|
|
||||||
override ReturnKind getKind() { any() }
|
override ReturnKind getKind() { any() }
|
||||||
@@ -1932,7 +1940,7 @@ class ExtractedReturnNode extends ReturnNode, CfgNode {
|
|||||||
class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode {
|
class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode {
|
||||||
YieldNodeInContextManagerFunction() {
|
YieldNodeInContextManagerFunction() {
|
||||||
hasContextmanagerDecorator(node.getScope()) and
|
hasContextmanagerDecorator(node.getScope()) and
|
||||||
node = any(Yield yield).getValue().getAFlowNode()
|
node.getNode() = any(Yield yield).getValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
override ReturnKind getKind() { any() }
|
override ReturnKind getKind() { any() }
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ overlay[local?]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
private import DataFlowPublic
|
private import DataFlowPublic
|
||||||
private import semmle.python.essa.SsaCompute
|
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||||
private import semmle.python.dataflow.new.internal.ImportResolution
|
private import semmle.python.dataflow.new.internal.ImportResolution
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
private import semmle.python.frameworks.data.ModelsAsData
|
private import semmle.python.frameworks.data.ModelsAsData
|
||||||
@@ -43,13 +44,23 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
|
|||||||
// Nodes
|
// Nodes
|
||||||
//--------
|
//--------
|
||||||
overlay[local]
|
overlay[local]
|
||||||
predicate isExpressionNode(ControlFlowNode node) { node.getNode() instanceof Expr }
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// SyntheticPreUpdateNode
|
// SyntheticPreUpdateNode
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
|
class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
|
||||||
CallNode node;
|
Cfg::CallNode node;
|
||||||
|
|
||||||
SyntheticPreUpdateNode() { this = TSyntheticPreUpdateNode(node) }
|
SyntheticPreUpdateNode() { this = TSyntheticPreUpdateNode(node) }
|
||||||
|
|
||||||
@@ -151,7 +162,7 @@ predicate synthStarArgsElementParameterNodeStoreStep(
|
|||||||
* been passed in a `**kwargs` argument.
|
* been passed in a `**kwargs` argument.
|
||||||
*/
|
*/
|
||||||
class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
|
class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
|
||||||
CallNode node;
|
Cfg::CallNode node;
|
||||||
|
|
||||||
SynthDictSplatArgumentNode() { this = TSynthDictSplatArgumentNode(node) }
|
SynthDictSplatArgumentNode() { this = TSynthDictSplatArgumentNode(node) }
|
||||||
|
|
||||||
@@ -165,7 +176,7 @@ class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
|
|||||||
private predicate synthDictSplatArgumentNodeStoreStep(
|
private predicate synthDictSplatArgumentNodeStoreStep(
|
||||||
ArgumentNode nodeFrom, DictionaryElementContent c, SynthDictSplatArgumentNode nodeTo
|
ArgumentNode nodeFrom, DictionaryElementContent c, SynthDictSplatArgumentNode nodeTo
|
||||||
) {
|
) {
|
||||||
exists(string name, CallNode call, ArgumentPosition keywordPos |
|
exists(string name, Cfg::CallNode call, ArgumentPosition keywordPos |
|
||||||
nodeTo = TSynthDictSplatArgumentNode(call) and
|
nodeTo = TSynthDictSplatArgumentNode(call) and
|
||||||
getCallArg(call, _, _, nodeFrom, keywordPos) and
|
getCallArg(call, _, _, nodeFrom, keywordPos) and
|
||||||
keywordPos.isKeyword(name) and
|
keywordPos.isKeyword(name) and
|
||||||
@@ -185,8 +196,8 @@ private predicate synthDictSplatArgumentNodeStoreStep(
|
|||||||
*/
|
*/
|
||||||
predicate yieldStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
predicate yieldStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||||
exists(Yield yield |
|
exists(Yield yield |
|
||||||
nodeTo.asCfgNode() = yield.getAFlowNode() and
|
nodeTo.asCfgNode().getNode() = yield and
|
||||||
nodeFrom.asCfgNode() = yield.getValue().getAFlowNode() and
|
nodeFrom.asCfgNode().getNode() = yield.getValue() and
|
||||||
// TODO: Consider if this will also need to transfer dictionary content
|
// TODO: Consider if this will also need to transfer dictionary content
|
||||||
// once dictionary comprehensions are supported.
|
// once dictionary comprehensions are supported.
|
||||||
c instanceof ListElementContent
|
c instanceof ListElementContent
|
||||||
@@ -289,7 +300,7 @@ abstract class PostUpdateNodeImpl extends Node {
|
|||||||
* Synthetic post-update nodes for synthetic nodes need to be listed one by one.
|
* Synthetic post-update nodes for synthetic nodes need to be listed one by one.
|
||||||
*/
|
*/
|
||||||
class SyntheticPostUpdateNode extends PostUpdateNodeImpl, TSyntheticPostUpdateNode {
|
class SyntheticPostUpdateNode extends PostUpdateNodeImpl, TSyntheticPostUpdateNode {
|
||||||
ControlFlowNode node;
|
Cfg::ControlFlowNode node;
|
||||||
|
|
||||||
SyntheticPostUpdateNode() { this = TSyntheticPostUpdateNode(node) }
|
SyntheticPostUpdateNode() { this = TSyntheticPostUpdateNode(node) }
|
||||||
|
|
||||||
@@ -333,16 +344,42 @@ module LocalFlow {
|
|||||||
// `x = f(42)`
|
// `x = f(42)`
|
||||||
// nodeFrom is `f(42)`
|
// nodeFrom is `f(42)`
|
||||||
// nodeTo is `x`
|
// nodeTo is `x`
|
||||||
exists(AssignmentDefinition def |
|
//
|
||||||
|
// 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 |
|
||||||
nodeFrom.(CfgNode).getNode() = def.getValue() and
|
nodeFrom.(CfgNode).getNode() = def.getValue() and
|
||||||
nodeTo.(CfgNode).getNode() = def.getDefiningNode()
|
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())
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// With definition
|
// With definition
|
||||||
// `with f(42) as x:`
|
// `with f(42) as x:`
|
||||||
// nodeFrom is `f(42)`
|
// nodeFrom is `f(42)`
|
||||||
// nodeTo is `x`
|
// nodeTo is `x`
|
||||||
exists(With with, ControlFlowNode contextManager, WithDefinition withDef, ControlFlowNode var |
|
exists(
|
||||||
|
With with, Cfg::ControlFlowNode contextManager, SsaImpl::WithDefinition withDef,
|
||||||
|
Cfg::ControlFlowNode var
|
||||||
|
|
|
||||||
var = withDef.getDefiningNode()
|
var = withDef.getDefiningNode()
|
||||||
|
|
|
|
||||||
nodeFrom.(CfgNode).getNode() = contextManager and
|
nodeFrom.(CfgNode).getNode() = contextManager and
|
||||||
@@ -361,13 +398,13 @@ module LocalFlow {
|
|||||||
|
|
||||||
predicate expressionFlowStep(Node nodeFrom, Node nodeTo) {
|
predicate expressionFlowStep(Node nodeFrom, Node nodeTo) {
|
||||||
// If expressions
|
// If expressions
|
||||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
|
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::IfExprNode).getAnOperand()
|
||||||
or
|
or
|
||||||
// Assignment expressions
|
// Assignment expressions
|
||||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
|
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::AssignmentExprNode).getValue()
|
||||||
or
|
or
|
||||||
// boolean inline expressions such as `x or y` or `x and y`
|
// boolean inline expressions such as `x or y` or `x and y`
|
||||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
|
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::BoolExprNode).getAnOperand()
|
||||||
or
|
or
|
||||||
// Flow inside an unpacking assignment
|
// Flow inside an unpacking assignment
|
||||||
iterableUnpackingFlowStep(nodeFrom, nodeTo)
|
iterableUnpackingFlowStep(nodeFrom, nodeTo)
|
||||||
@@ -376,12 +413,28 @@ module LocalFlow {
|
|||||||
matchFlowStep(nodeFrom, nodeTo)
|
matchFlowStep(nodeFrom, nodeTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
|
predicate useToNextUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) {
|
||||||
AdjacentUses::adjacentUseUse(nodeFrom, 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 defToFirstUse(EssaVariable var, NameNode nodeTo) {
|
predicate defToFirstUse(SsaImpl::EssaVariable var, Cfg::NameNode nodeTo) {
|
||||||
AdjacentUses::firstUse(var.getDefinition(), nodeTo)
|
exists(Cfg::NameNode toVariant |
|
||||||
|
SsaImpl::AdjacentUses::firstUse(var.getDefinition(), toVariant) and
|
||||||
|
toVariant.getNode() = nodeTo.getNode() and
|
||||||
|
Cfg::isCanonicalAstNodeRepresentative(nodeTo)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate useUseFlowStep(Node nodeFrom, Node nodeTo) {
|
predicate useUseFlowStep(Node nodeFrom, Node nodeTo) {
|
||||||
@@ -390,12 +443,14 @@ module LocalFlow {
|
|||||||
// `x = f(y)`
|
// `x = f(y)`
|
||||||
// nodeFrom is `y` on first line
|
// nodeFrom is `y` on first line
|
||||||
// nodeTo is `y` on second line
|
// nodeTo is `y` on second line
|
||||||
exists(EssaDefinition def |
|
exists(SsaImpl::EssaDefinition def, Cfg::NameNode toVariant |
|
||||||
nodeFrom.(CfgNode).getNode() = def.(EssaNodeDefinition).getDefiningNode()
|
nodeFrom.(CfgNode).getNode() = def.(SsaImpl::EssaNodeDefinition).getDefiningNode()
|
||||||
or
|
or
|
||||||
nodeFrom.(ScopeEntryDefinitionNode).getDefinition() = def
|
nodeFrom.(ScopeEntryDefinitionNode).getDefinition() = def
|
||||||
|
|
|
|
||||||
AdjacentUses::firstUse(def, nodeTo.(CfgNode).getNode())
|
SsaImpl::AdjacentUses::firstUse(def, toVariant) and
|
||||||
|
toVariant.getNode() = nodeTo.(CfgNode).getNode().getNode() and
|
||||||
|
Cfg::isCanonicalAstNodeRepresentative(nodeTo.(CfgNode).getNode())
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// Next use after use
|
// Next use after use
|
||||||
@@ -557,9 +612,9 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
|
|||||||
// a parameter with a default value, since the parameter will be in the scope of the
|
// 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, while the default value itself will be in the scope that _defines_ the
|
||||||
// function.
|
// function.
|
||||||
exists(ParameterDefinition param |
|
exists(SsaImpl::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.
|
// 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() = param.getDefault() and
|
nodeFrom.asCfgNode().getNode() = param.getParameter().(Parameter).getDefault() and
|
||||||
nodeTo.asCfgNode() = param.getDefiningNode()
|
nodeTo.asCfgNode() = param.getDefiningNode()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -663,7 +718,7 @@ predicate neverSkipInPathGraph(Node n) {
|
|||||||
// ```
|
// ```
|
||||||
// we would end up saying that the path MUST not skip the x in `y = x`, which is just
|
// 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.
|
// annoying and doesn't help the path explanation become clearer.
|
||||||
n.asCfgNode() = any(EssaNodeDefinition def).getDefiningNode()
|
n.asCfgNode() = any(SsaImpl::EssaNodeDefinition def).getDefiningNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -872,7 +927,7 @@ predicate listStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo)
|
|||||||
// nodeFrom is `42`, cfg node
|
// nodeFrom is `42`, cfg node
|
||||||
// nodeTo is the list, `[..., 42, ...]`, cfg node
|
// nodeTo is the list, `[..., 42, ...]`, cfg node
|
||||||
// c denotes element of list
|
// c denotes element of list
|
||||||
nodeTo.getNode().(ListNode).getAnElement() = nodeFrom.getNode() and
|
nodeTo.getNode().(Cfg::ListNode).getAnElement() = nodeFrom.getNode() and
|
||||||
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
|
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
|
||||||
// Suppress unused variable warning
|
// Suppress unused variable warning
|
||||||
c = c
|
c = c
|
||||||
@@ -885,7 +940,7 @@ predicate setStoreStep(CfgNode nodeFrom, SetElementContent c, CfgNode nodeTo) {
|
|||||||
// nodeFrom is `42`, cfg node
|
// nodeFrom is `42`, cfg node
|
||||||
// nodeTo is the set, `{..., 42, ...}`, cfg node
|
// nodeTo is the set, `{..., 42, ...}`, cfg node
|
||||||
// c denotes element of list
|
// c denotes element of list
|
||||||
nodeTo.getNode().(SetNode).getAnElement() = nodeFrom.getNode() and
|
nodeTo.getNode().(Cfg::SetNode).getAnElement() = nodeFrom.getNode() and
|
||||||
// Suppress unused variable warning
|
// Suppress unused variable warning
|
||||||
c = c
|
c = c
|
||||||
}
|
}
|
||||||
@@ -898,7 +953,7 @@ predicate tupleStoreStep(CfgNode nodeFrom, TupleElementContent c, CfgNode nodeTo
|
|||||||
// nodeTo is the tuple, `(..., 42, ...)`, cfg node
|
// nodeTo is the tuple, `(..., 42, ...)`, cfg node
|
||||||
// c denotes element of tuple and index of nodeFrom
|
// c denotes element of tuple and index of nodeFrom
|
||||||
exists(int n |
|
exists(int n |
|
||||||
nodeTo.getNode().(TupleNode).getElement(n) = nodeFrom.getNode() and
|
nodeTo.getNode().(Cfg::TupleNode).getElement(n) = nodeFrom.getNode() and
|
||||||
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
|
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
|
||||||
c.getIndex() = n
|
c.getIndex() = n
|
||||||
)
|
)
|
||||||
@@ -912,7 +967,7 @@ predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeT
|
|||||||
// nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node
|
// nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node
|
||||||
// c denotes element of dictionary and the key `"key"`
|
// c denotes element of dictionary and the key `"key"`
|
||||||
exists(KeyValuePair item |
|
exists(KeyValuePair item |
|
||||||
item = nodeTo.asCfgNode().(DictNode).getNode().(Dict).getAnItem() and
|
item = nodeTo.asCfgNode().(Cfg::DictNode).getNode().(Dict).getAnItem() and
|
||||||
nodeFrom.getNode().getNode() = item.getValue() and
|
nodeFrom.getNode().getNode() = item.getValue() and
|
||||||
c.getKey() = item.getKey().(StringLiteral).getS()
|
c.getKey() = item.getKey().(StringLiteral).getS()
|
||||||
)
|
)
|
||||||
@@ -927,9 +982,9 @@ predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeT
|
|||||||
private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
|
private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
|
||||||
// NOTE: It's important to add logic to the newtype definition of
|
// NOTE: It's important to add logic to the newtype definition of
|
||||||
// DictionaryElementContent if you add new cases here.
|
// DictionaryElementContent if you add new cases here.
|
||||||
exists(SubscriptNode subscript |
|
exists(Cfg::SubscriptNode subscript |
|
||||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() = subscript.getObject() and
|
nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() = subscript.getObject() and
|
||||||
nodeFrom.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
nodeFrom.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
|
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -942,8 +997,8 @@ private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate dictClearStep(Node node, DictionaryElementContent c) {
|
predicate dictClearStep(Node node, DictionaryElementContent c) {
|
||||||
exists(SubscriptNode subscript |
|
exists(Cfg::SubscriptNode subscript |
|
||||||
subscript instanceof DefinitionNode and
|
subscript instanceof Cfg::DefinitionNode and
|
||||||
node.asCfgNode() = subscript.getObject() and
|
node.asCfgNode() = subscript.getObject() and
|
||||||
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
|
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
|
||||||
)
|
)
|
||||||
@@ -1018,7 +1073,7 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
|||||||
// nodeFrom is `l`, cfg node
|
// nodeFrom is `l`, cfg node
|
||||||
// nodeTo is `l[3]`, cfg node
|
// nodeTo is `l[3]`, cfg node
|
||||||
// c is compatible with 3
|
// c is compatible with 3
|
||||||
nodeFrom.getNode() = nodeTo.getNode().(SubscriptNode).getObject() and
|
nodeFrom.getNode() = nodeTo.getNode().(Cfg::SubscriptNode).getObject() and
|
||||||
(
|
(
|
||||||
c instanceof ListElementContent
|
c instanceof ListElementContent
|
||||||
or
|
or
|
||||||
@@ -1027,10 +1082,10 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
|||||||
c instanceof DictionaryElementAnyContent
|
c instanceof DictionaryElementAnyContent
|
||||||
or
|
or
|
||||||
c.(TupleElementContent).getIndex() =
|
c.(TupleElementContent).getIndex() =
|
||||||
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
|
nodeTo.getNode().(Cfg::SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
|
||||||
or
|
or
|
||||||
c.(DictionaryElementContent).getKey() =
|
c.(DictionaryElementContent).getKey() =
|
||||||
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(StringLiteral).getS()
|
nodeTo.getNode().(Cfg::SubscriptNode).getIndex().getNode().(StringLiteral).getS()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ overlay[local]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
private import DataFlowPrivate
|
private import DataFlowPrivate
|
||||||
import semmle.python.dataflow.new.TypeTracking
|
import semmle.python.dataflow.new.TypeTracking
|
||||||
import Attributes
|
import Attributes
|
||||||
import LocalSources
|
import LocalSources
|
||||||
private import semmle.python.essa.SsaCompute
|
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
|
||||||
private import semmle.python.dataflow.new.internal.ImportStar
|
private import semmle.python.dataflow.new.internal.ImportStar
|
||||||
private import semmle.python.frameworks.data.ModelsAsData
|
private import semmle.python.frameworks.data.ModelsAsData
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
@@ -27,16 +28,21 @@ private import semmle.python.frameworks.data.ModelsAsData
|
|||||||
overlay[local]
|
overlay[local]
|
||||||
newtype TNode =
|
newtype TNode =
|
||||||
/** A node corresponding to a control flow node. */
|
/** A node corresponding to a control flow node. */
|
||||||
TCfgNode(ControlFlowNode node) {
|
TCfgNode(Cfg::ControlFlowNode node) {
|
||||||
isExpressionNode(node)
|
(
|
||||||
or
|
isExpressionNode(node)
|
||||||
node.getNode() instanceof Pattern
|
or
|
||||||
|
node.getNode() instanceof Pattern
|
||||||
|
) and
|
||||||
|
Cfg::isCanonicalAstNodeRepresentative(node)
|
||||||
} or
|
} or
|
||||||
/**
|
/**
|
||||||
* A node corresponding to a scope entry definition. That is, the value of a variable
|
* A node corresponding to a scope entry definition. That is, the value of a variable
|
||||||
* as it enters a scope.
|
* as it enters a scope.
|
||||||
*/
|
*/
|
||||||
TScopeEntryDefinitionNode(ScopeEntryDefinition def) { not def.getScope() instanceof Module } or
|
TScopeEntryDefinitionNode(SsaImpl::ScopeEntryDefinition def) {
|
||||||
|
not def.getScope() instanceof Module
|
||||||
|
} or
|
||||||
/**
|
/**
|
||||||
* A synthetic node representing the value of an object before a state change.
|
* A synthetic node representing the value of an object before a state change.
|
||||||
*
|
*
|
||||||
@@ -47,36 +53,39 @@ newtype TNode =
|
|||||||
// NOTE: since we can't rely on the call graph, but we want to have synthetic
|
// 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
|
// pre-update nodes for class calls, we end up getting synthetic pre-update nodes for
|
||||||
// ALL calls :|
|
// ALL calls :|
|
||||||
TSyntheticPreUpdateNode(CallNode call) or
|
TSyntheticPreUpdateNode(Cfg::CallNode call) { Cfg::isCanonicalAstNodeRepresentative(call) } or
|
||||||
/**
|
/**
|
||||||
* A synthetic node representing the value of an object after a state change.
|
* A synthetic node representing the value of an object after a state change.
|
||||||
* See QLDoc for `PostUpdateNode`.
|
* See QLDoc for `PostUpdateNode`.
|
||||||
*/
|
*/
|
||||||
TSyntheticPostUpdateNode(ControlFlowNode node) {
|
TSyntheticPostUpdateNode(Cfg::ControlFlowNode node) {
|
||||||
exists(CallNode call |
|
Cfg::isCanonicalAstNodeRepresentative(node) and
|
||||||
node = call.getArg(_)
|
(
|
||||||
|
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()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
node = call.getArgByName(_)
|
node = any(Cfg::AttrNode a).getObject()
|
||||||
or
|
or
|
||||||
// `self` argument when handling class instance calls (`__call__` special method))
|
node = any(Cfg::SubscriptNode s).getObject()
|
||||||
node = call.getFunction()
|
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()
|
||||||
)
|
)
|
||||||
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
|
} or
|
||||||
/** A node representing a global (module-level) variable in a specific module. */
|
/** A node representing a global (module-level) variable in a specific module. */
|
||||||
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m } or
|
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m } or
|
||||||
@@ -112,7 +121,9 @@ newtype TNode =
|
|||||||
exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos)))
|
exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos)))
|
||||||
} or
|
} or
|
||||||
/** A synthetic node to capture keyword arguments that are passed to a `**kwargs` parameter. */
|
/** A synthetic node to capture keyword arguments that are passed to a `**kwargs` parameter. */
|
||||||
TSynthDictSplatArgumentNode(CallNode call) { exists(call.getArgByName(_)) } or
|
TSynthDictSplatArgumentNode(Cfg::CallNode call) {
|
||||||
|
exists(call.getArgByName(_)) and Cfg::isCanonicalAstNodeRepresentative(call)
|
||||||
|
} or
|
||||||
/** A synthetic node to allow flow to keyword parameters from a `**kwargs` argument. */
|
/** A synthetic node to allow flow to keyword parameters from a `**kwargs` argument. */
|
||||||
TSynthDictSplatParameterNode(DataFlowCallable callable) {
|
TSynthDictSplatParameterNode(DataFlowCallable callable) {
|
||||||
exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos)))
|
exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos)))
|
||||||
@@ -128,15 +139,17 @@ newtype TNode =
|
|||||||
* A synthetic node representing the values of the variables captured
|
* A synthetic node representing the values of the variables captured
|
||||||
* by the callable being called.
|
* by the callable being called.
|
||||||
*/
|
*/
|
||||||
TSynthCapturedVariablesArgumentNode(ControlFlowNode callable) {
|
TSynthCapturedVariablesArgumentNode(Cfg::ControlFlowNode callable) {
|
||||||
callable = any(CallNode c).getFunction()
|
callable = any(Cfg::CallNode c).getFunction() and
|
||||||
|
Cfg::isCanonicalAstNodeRepresentative(callable)
|
||||||
} or
|
} or
|
||||||
/**
|
/**
|
||||||
* A synthetic node representing the values of the variables captured
|
* A synthetic node representing the values of the variables captured
|
||||||
* by the callable being called, after the output has been computed.
|
* by the callable being called, after the output has been computed.
|
||||||
*/
|
*/
|
||||||
TSynthCapturedVariablesArgumentPostUpdateNode(ControlFlowNode callable) {
|
TSynthCapturedVariablesArgumentPostUpdateNode(Cfg::ControlFlowNode callable) {
|
||||||
callable = any(CallNode c).getFunction()
|
callable = any(Cfg::CallNode c).getFunction() and
|
||||||
|
Cfg::isCanonicalAstNodeRepresentative(callable)
|
||||||
} or
|
} or
|
||||||
/** A synthetic node representing the values of variables captured by a comprehension. */
|
/** A synthetic node representing the values of variables captured by a comprehension. */
|
||||||
TSynthCompCapturedVariablesArgumentNode(Comp comp) {
|
TSynthCompCapturedVariablesArgumentNode(Comp comp) {
|
||||||
@@ -194,7 +207,7 @@ class Node extends TNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the control-flow node corresponding to this node, if any. */
|
/** Gets the control-flow node corresponding to this node, if any. */
|
||||||
ControlFlowNode asCfgNode() { none() }
|
Cfg::ControlFlowNode asCfgNode() { none() }
|
||||||
|
|
||||||
/** Gets the expression corresponding to this node, if any. */
|
/** Gets the expression corresponding to this node, if any. */
|
||||||
Expr asExpr() { none() }
|
Expr asExpr() { none() }
|
||||||
@@ -207,14 +220,14 @@ class Node extends TNode {
|
|||||||
|
|
||||||
/** A data-flow node corresponding to a control-flow node. */
|
/** A data-flow node corresponding to a control-flow node. */
|
||||||
class CfgNode extends Node, TCfgNode {
|
class CfgNode extends Node, TCfgNode {
|
||||||
ControlFlowNode node;
|
Cfg::ControlFlowNode node;
|
||||||
|
|
||||||
CfgNode() { this = TCfgNode(node) }
|
CfgNode() { this = TCfgNode(node) }
|
||||||
|
|
||||||
/** Gets the `ControlFlowNode` represented by this data-flow node. */
|
/** Gets the `Cfg::ControlFlowNode` represented by this data-flow node. */
|
||||||
ControlFlowNode getNode() { result = node }
|
Cfg::ControlFlowNode getNode() { result = node }
|
||||||
|
|
||||||
override ControlFlowNode asCfgNode() { result = node }
|
override Cfg::ControlFlowNode asCfgNode() { result = node }
|
||||||
|
|
||||||
/** Gets a textual representation of this element. */
|
/** Gets a textual representation of this element. */
|
||||||
override string toString() { result = node.toString() }
|
override string toString() { result = node.toString() }
|
||||||
@@ -224,9 +237,9 @@ class CfgNode extends Node, TCfgNode {
|
|||||||
override Location getLocation() { result = node.getLocation() }
|
override Location getLocation() { result = node.getLocation() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A data-flow node corresponding to a `CallNode` in the control-flow graph. */
|
/** A data-flow node corresponding to a `Cfg::CallNode` in the control-flow graph. */
|
||||||
class CallCfgNode extends CfgNode, LocalSourceNode {
|
class CallCfgNode extends CfgNode, LocalSourceNode {
|
||||||
override CallNode node;
|
override Cfg::CallNode node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the data-flow node for the function component of the call corresponding to this data-flow
|
* Gets the data-flow node for the function component of the call corresponding to this data-flow
|
||||||
@@ -307,15 +320,15 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
|
|||||||
* as it enters a scope.
|
* as it enters a scope.
|
||||||
*/
|
*/
|
||||||
class ScopeEntryDefinitionNode extends Node, TScopeEntryDefinitionNode {
|
class ScopeEntryDefinitionNode extends Node, TScopeEntryDefinitionNode {
|
||||||
ScopeEntryDefinition def;
|
SsaImpl::ScopeEntryDefinition def;
|
||||||
|
|
||||||
ScopeEntryDefinitionNode() { this = TScopeEntryDefinitionNode(def) }
|
ScopeEntryDefinitionNode() { this = TScopeEntryDefinitionNode(def) }
|
||||||
|
|
||||||
/** Gets the `ScopeEntryDefinition` associated with this node. */
|
/** Gets the `SsaImpl::ScopeEntryDefinition` associated with this node. */
|
||||||
ScopeEntryDefinition getDefinition() { result = def }
|
SsaImpl::ScopeEntryDefinition getDefinition() { result = def }
|
||||||
|
|
||||||
/** Gets the source variable represented by this node. */
|
/** Gets the source variable represented by this node. */
|
||||||
SsaSourceVariable getVariable() { result = def.getSourceVariable() }
|
SsaImpl::SsaSourceVariable getVariable() { result = def.getSourceVariable() }
|
||||||
|
|
||||||
override Location getLocation() { result = def.getLocation() }
|
override Location getLocation() { result = def.getLocation() }
|
||||||
|
|
||||||
@@ -337,7 +350,7 @@ class ParameterNode extends Node instanceof ParameterNodeImpl {
|
|||||||
/** A parameter node found in the source code (not in a summary). */
|
/** A parameter node found in the source code (not in a summary). */
|
||||||
class ExtractedParameterNode extends ParameterNodeImpl, CfgNode {
|
class ExtractedParameterNode extends ParameterNodeImpl, CfgNode {
|
||||||
//, LocalSourceNode {
|
//, LocalSourceNode {
|
||||||
ParameterDefinition def;
|
SsaImpl::ParameterDefinition def;
|
||||||
|
|
||||||
ExtractedParameterNode() { node = def.getDefiningNode() }
|
ExtractedParameterNode() { node = def.getDefiningNode() }
|
||||||
|
|
||||||
@@ -368,10 +381,10 @@ Node getCallArgApproximation() {
|
|||||||
exists(Class c | result.asExpr() = c.getAMethod().getArg(0))
|
exists(Class c | result.asExpr() = c.getAMethod().getArg(0))
|
||||||
or
|
or
|
||||||
// the object part of an attribute expression (which might be a bound method)
|
// the object part of an attribute expression (which might be a bound method)
|
||||||
result.asCfgNode() = any(AttrNode a).getObject()
|
result.asCfgNode() = any(Cfg::AttrNode a).getObject()
|
||||||
or
|
or
|
||||||
// the function part of any call
|
// the function part of any call
|
||||||
result.asCfgNode() = any(CallNode c).getFunction()
|
result.asCfgNode() = any(Cfg::CallNode c).getFunction()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the extracted argument nodes that do not rely on `getCallArg`. */
|
/** Gets the extracted argument nodes that do not rely on `getCallArg`. */
|
||||||
@@ -380,7 +393,7 @@ private Node implicitArgumentNode() {
|
|||||||
normalCallArg(_, result, _)
|
normalCallArg(_, result, _)
|
||||||
or
|
or
|
||||||
// and self arguments
|
// and self arguments
|
||||||
result.asCfgNode() = any(CallNode c).getFunction().(AttrNode).getObject()
|
result.asCfgNode() = any(Cfg::CallNode c).getFunction().(Cfg::AttrNode).getObject()
|
||||||
or
|
or
|
||||||
// for comprehensions, we allow the synthetic `iterable` argument
|
// for comprehensions, we allow the synthetic `iterable` argument
|
||||||
result.asExpr() = any(Comp c).getIterable()
|
result.asExpr() = any(Comp c).getIterable()
|
||||||
@@ -485,21 +498,24 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
|
|||||||
|
|
||||||
/** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */
|
/** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */
|
||||||
Node getALocalRead() {
|
Node getALocalRead() {
|
||||||
result.asCfgNode() = var.getALoad().getAFlowNode() and
|
result.asCfgNode().getNode() = var.getALoad() and
|
||||||
not result.getScope() = mod
|
not result.getScope() = mod
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets an `EssaNode` that corresponds to an assignment of this global variable. */
|
/** Gets a CFG node that corresponds to an assignment of this global variable. */
|
||||||
Node getAWrite() {
|
Node getAWrite() {
|
||||||
any(EssaNodeDefinition def).definedBy(var, result.asCfgNode().(DefinitionNode))
|
exists(Cfg::NameNode n |
|
||||||
|
n.defines(var) and
|
||||||
|
result.asCfgNode() = n
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the possible values of the variable at the end of import time */
|
/** Gets the possible values of the variable at the end of import time */
|
||||||
CfgNode getADefiningWrite() {
|
CfgNode getADefiningWrite() {
|
||||||
exists(SsaVariable def |
|
exists(SsaImpl::EssaVariable def |
|
||||||
def = any(SsaVariable ssa_var).getAnUltimateDefinition() and
|
def = any(SsaImpl::EssaVariable ssa_var).getAnUltimateDefinition() and
|
||||||
def.getDefinition() = result.asCfgNode() and
|
def.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode() = result.asCfgNode() and
|
||||||
def.getVariable() = var
|
def.getSourceVariable().getVariable() = var
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,7 +532,7 @@ private ModuleVariableNode import_star_read(Node n) {
|
|||||||
overlay[global]
|
overlay[global]
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate resolved_import_star_module(Module m, string name, Node n) {
|
private predicate resolved_import_star_module(Module m, string name, Node n) {
|
||||||
exists(NameNode nn | nn = n.asCfgNode() |
|
exists(Cfg::NameNode nn | nn = n.asCfgNode() |
|
||||||
ImportStar::importStarResolvesTo(pragma[only_bind_into](nn), m) and
|
ImportStar::importStarResolvesTo(pragma[only_bind_into](nn), m) and
|
||||||
nn.getId() = name
|
nn.getId() = name
|
||||||
)
|
)
|
||||||
@@ -574,88 +590,116 @@ class StarPatternElementNode extends Node, TStarPatternElementNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a node that controls whether other nodes are evaluated.
|
* 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.
|
||||||
*
|
*
|
||||||
* In the base case, this is the last node of `conditionBlock`, and `flipped` is `false`.
|
* Users typically obtain a `GuardNode` by casting from a more specific
|
||||||
* This definition accounts for (short circuting) `and`- and `or`-expressions, as the structure
|
* Cfg type: `g.(Cfg::CallNode)` for a call-based check, etc.
|
||||||
* of basic blocks will reflect their semantics.
|
|
||||||
*
|
*
|
||||||
* However, in the program
|
* This replaces the legacy (pre-shared-CFG) `GuardNode`/`flipped`
|
||||||
* ```python
|
* machinery: the shared CFG carries outcome information structurally
|
||||||
* if not is_safe(path):
|
* (via `isAfterTrue`/`isAfterFalse`), so no separate polarity field
|
||||||
* return
|
* is required.
|
||||||
* ```
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) {
|
class GuardNode extends Cfg::ControlFlowNode {
|
||||||
// Base case: the last node truly does determine which successor is chosen
|
GuardNode() {
|
||||||
result = conditionBlock.getLastNode() and
|
// This is the canonical (post-order) version of an AST node, and
|
||||||
flipped = false
|
// some `[true]`/`[false]` variant of the same AST exists. We
|
||||||
or
|
// include the canonical node because users identify guards by
|
||||||
// Recursive cases:
|
// their AST (`g.(Cfg::CallNode)` etc.), and the outcome-tagged
|
||||||
// if a guard node is a `not`-expression,
|
// variants are accessed by `outcomeOfGuard` below.
|
||||||
// the operand is also a guard node, but with inverted polarity.
|
exists(Cfg::ControlFlowNode outcome |
|
||||||
exists(UnaryExprNode notNode |
|
outcome.getNode() = this.getNode() and
|
||||||
result = notNode.getOperand() and
|
(outcome.isAfterTrue(_) or outcome.isAfterFalse(_))
|
||||||
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
|
||||||
// we flip `flipped` according to `should_flip` via the formula `flipped xor should_flip`.
|
// Or: this IS one of the outcome-tagged variants, supporting
|
||||||
flipped in [true, false] and
|
// users who want to query the split point directly.
|
||||||
cmpNode = guardNode(conditionBlock, flipped.booleanXor(should_flip))
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node that controls whether other nodes are evaluated.
|
* Holds if some execution that arrives at `outcomeNode` corresponds
|
||||||
|
* to `guard` having evaluated to `branch`.
|
||||||
*
|
*
|
||||||
* The field `flipped` allows us to match `GuardNode`s underneath
|
* For a direct guard `if g:`, the outcome node is `g` itself with
|
||||||
* `not`-expressions and still choose the appropriate branch.
|
* `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.
|
||||||
*/
|
*/
|
||||||
class GuardNode extends ControlFlowNode {
|
private predicate outcomeOfGuard(
|
||||||
ConditionBlock conditionBlock;
|
Cfg::ControlFlowNode guard, Cfg::ControlFlowNode outcomeNode, boolean branch
|
||||||
boolean flipped;
|
) {
|
||||||
|
// Base case: the guard itself splits — the outcome node is the
|
||||||
GuardNode() { this = guardNode(conditionBlock, flipped) }
|
// first node of an outcome BB, with matching outcome label.
|
||||||
|
// (The shared CFG also marks inner expressions with outcome flags
|
||||||
/** Holds if this guard controls block `b` upon evaluating to `branch`. */
|
// for analysis purposes, but only "splitting" nodes — those that
|
||||||
predicate controlsBlock(BasicBlock b, boolean branch) {
|
// actually start an outcome BB — are valid guards on their own.)
|
||||||
branch in [true, false] and
|
// The guard must be the canonical AST representative for its AST:
|
||||||
conditionBlock.controls(b, branch.booleanXor(flipped))
|
// each AST expression has at most one canonical representative, and
|
||||||
}
|
// admitting non-canonical outcome-tagged variants (e.g. `After l [false]`
|
||||||
|
// when the canonical is `After l [true]`) would let them spuriously
|
||||||
|
// "control" the `[true]`-tagged BB via the AST-matching join below.
|
||||||
|
Cfg::isCanonicalAstNodeRepresentative(guard) and
|
||||||
|
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())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the guard `g` validates `node` upon evaluating to `branch`.
|
* Holds if the guard `g` validates `node` upon evaluating to `branch`.
|
||||||
*/
|
*/
|
||||||
signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch);
|
signature predicate guardChecksSig(GuardNode g, Cfg::ControlFlowNode node, boolean branch);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a set of barrier nodes for a guard that validates a node.
|
* Provides a set of barrier nodes for a guard that validates a node.
|
||||||
@@ -670,7 +714,9 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
|||||||
result = ParameterizedBarrierGuard<Unit, extendedGuardChecks/4>::getABarrierNode(_)
|
result = ParameterizedBarrierGuard<Unit, extendedGuardChecks/4>::getABarrierNode(_)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate extendedGuardChecks(GuardNode g, ControlFlowNode node, boolean branch, Unit u) {
|
private predicate extendedGuardChecks(
|
||||||
|
GuardNode g, Cfg::ControlFlowNode node, boolean branch, Unit u
|
||||||
|
) {
|
||||||
guardChecks(g, node, branch) and
|
guardChecks(g, node, branch) and
|
||||||
u = u
|
u = u
|
||||||
}
|
}
|
||||||
@@ -680,7 +726,7 @@ bindingset[this]
|
|||||||
private signature class ParamSig;
|
private signature class ParamSig;
|
||||||
|
|
||||||
private module WithParam<ParamSig P> {
|
private module WithParam<ParamSig P> {
|
||||||
signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch, P param);
|
signature predicate guardChecksSig(GuardNode g, Cfg::ControlFlowNode node, boolean branch, P param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -693,10 +739,19 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
|
|||||||
/** Gets a node that is safely guarded by the given guard check with parameter `param`. */
|
/** Gets a node that is safely guarded by the given guard check with parameter `param`. */
|
||||||
overlay[global]
|
overlay[global]
|
||||||
ExprNode getABarrierNode(P param) {
|
ExprNode getABarrierNode(P param) {
|
||||||
exists(GuardNode g, EssaDefinition def, ControlFlowNode node, boolean branch |
|
exists(GuardNode g, SsaImpl::EssaDefinition def, Cfg::ControlFlowNode node, boolean branch |
|
||||||
AdjacentUses::useOfDef(def, node) and
|
SsaImpl::AdjacentUses::useOfDef(def, node) and
|
||||||
guardChecks(g, node, branch, param) and
|
guardChecks(g, node, branch, param) and
|
||||||
AdjacentUses::useOfDef(def, result.asCfgNode()) and
|
SsaImpl::AdjacentUses::useOfDef(def, result.asCfgNode()) and
|
||||||
|
// The protected use must be a different SSA position than the test
|
||||||
|
// position itself: in the shared CFG the test expression's
|
||||||
|
// canonical CFG node is the firstNode of its own true-branch BB
|
||||||
|
// (`controlsBlock` is reflexive on dominance), and that canonical
|
||||||
|
// node is also the SSA-use position on the def-use chain leading
|
||||||
|
// to the actual branch uses. Without this guard, the test
|
||||||
|
// position would be returned as a barrier and block flow before
|
||||||
|
// it can reach the genuine uses in either branch.
|
||||||
|
node != result.asCfgNode() and
|
||||||
g.controlsBlock(result.asCfgNode().getBasicBlock(), branch)
|
g.controlsBlock(result.asCfgNode().getBasicBlock(), branch)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -712,7 +767,7 @@ module ExternalBarrierGuard {
|
|||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
|
|
||||||
overlay[global]
|
overlay[global]
|
||||||
private predicate guardCheck(GuardNode g, ControlFlowNode node, boolean branch, string kind) {
|
private predicate guardCheck(GuardNode g, Cfg::ControlFlowNode node, boolean branch, string kind) {
|
||||||
exists(API::CallNode call, API::Node parameter |
|
exists(API::CallNode call, API::Node parameter |
|
||||||
parameter = call.getAParameter() and
|
parameter = call.getAParameter() and
|
||||||
parameter = ModelOutput::getABarrierGuardNode(kind, branch)
|
parameter = ModelOutput::getABarrierGuardNode(kind, branch)
|
||||||
@@ -748,10 +803,10 @@ newtype TContent =
|
|||||||
TSetElementContent() or
|
TSetElementContent() or
|
||||||
/** An element of a tuple at a specific index. */
|
/** An element of a tuple at a specific index. */
|
||||||
TTupleElementContent(int index) {
|
TTupleElementContent(int index) {
|
||||||
exists(any(TupleNode tn).getElement(index))
|
exists(any(Cfg::TupleNode tn).getElement(index))
|
||||||
or
|
or
|
||||||
// Arguments can overflow and end up in the starred parameter tuple.
|
// Arguments can overflow and end up in the starred parameter tuple.
|
||||||
exists(any(CallNode cn).getArg(index))
|
exists(any(Cfg::CallNode cn).getArg(index))
|
||||||
or
|
or
|
||||||
// since flow summaries might use tuples, we ensure that we at least have valid
|
// 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
|
// TTupleElementContent for the 0..7 (7 was picked to match `small_tuple` in
|
||||||
@@ -768,10 +823,14 @@ newtype TContent =
|
|||||||
or
|
or
|
||||||
// d["key"] = ...
|
// d["key"] = ...
|
||||||
key =
|
key =
|
||||||
any(SubscriptNode sub | sub.isStore() | sub.getIndex().getNode().(StringLiteral).getText())
|
any(Cfg::SubscriptNode sub |
|
||||||
|
sub.isStore()
|
||||||
|
|
|
||||||
|
sub.getIndex().getNode().(StringLiteral).getText()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
// d.setdefault("key", ...)
|
// d.setdefault("key", ...)
|
||||||
exists(CallNode call | call.getFunction().(AttrNode).getName() = "setdefault" |
|
exists(Cfg::CallNode call | call.getFunction().(Cfg::AttrNode).getName() = "setdefault" |
|
||||||
key = call.getArg(0).getNode().(StringLiteral).getText()
|
key = call.getArg(0).getNode().(StringLiteral).getText()
|
||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
|
|||||||
@@ -5,17 +5,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.internal.ImportStar
|
private import semmle.python.dataflow.new.internal.ImportStar
|
||||||
private import semmle.python.dataflow.new.TypeTracking
|
private import semmle.python.dataflow.new.TypeTracking
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `init` is a package's `__init__.py` and `var` is a global variable in
|
* Holds if the name of `var` refers to a submodule of a package and `init` is
|
||||||
* `init` whose name matches a submodule of the package.
|
* the `__init__` module of that package. Locally inlined replacement for the
|
||||||
*
|
* legacy `SsaSource::init_module_submodule_defn` so that this module has no
|
||||||
* Inlined from `SsaSource::init_module_submodule_defn` to avoid pulling
|
* direct dependency on `semmle.python.essa.SsaDefinitions`.
|
||||||
* `semmle.python.essa.SsaDefinitions` into the new dataflow stack.
|
|
||||||
*/
|
*/
|
||||||
private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) {
|
private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) {
|
||||||
init.isPackageInit() and
|
init.isPackageInit() and
|
||||||
@@ -81,13 +82,19 @@ module ImportResolution {
|
|||||||
* Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed
|
* Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed
|
||||||
* for import resolution.
|
* for import resolution.
|
||||||
*/
|
*/
|
||||||
private predicate allowedEssaImportStep(EssaDefinition defFrom, EssaDefinition defTo) {
|
private predicate allowedEssaImportStep(
|
||||||
|
SsaImpl::EssaDefinition defFrom, SsaImpl::EssaDefinition defTo
|
||||||
|
) {
|
||||||
// to handle definitions guarded by if-then-else
|
// to handle definitions guarded by if-then-else
|
||||||
defFrom = defTo.(PhiFunction).getAnInput()
|
defFrom = defTo.(SsaImpl::PhiFunction).getAnInput()
|
||||||
or
|
or
|
||||||
// refined variable
|
// to handle uncertain writes such as `from X import *`, which create an
|
||||||
// example: https://github.com/nvbn/thefuck/blob/ceeaeab94b5df5a4fe9d94d61e4f6b0bbea96378/thefuck/utils.py#L25-L45
|
// uncertain SSA definition for every name in the importing scope. The
|
||||||
defFrom = defTo.(EssaNodeRefinement).getInput().getDefinition()
|
// 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.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,30 +111,32 @@ module ImportResolution {
|
|||||||
// Definitions made inside `m` itself
|
// Definitions made inside `m` itself
|
||||||
//
|
//
|
||||||
// for code such as `foo = ...; foo.bar = ...` there will be TWO
|
// for code such as `foo = ...; foo.bar = ...` there will be TWO
|
||||||
// EssaDefinition/EssaVariable. One for `foo = ...` (AssignmentDefinition) and one
|
// SsaImpl::EssaDefinition/SsaImpl::EssaVariable. One for `foo = ...` (SsaImpl::AssignmentDefinition) and one
|
||||||
// for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The
|
// for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The
|
||||||
// EssaNodeRefinement is the one that will reach the end of the module (normal
|
// EssaNodeRefinement is the one that will reach the end of the module (normal
|
||||||
// exit).
|
// exit).
|
||||||
//
|
//
|
||||||
// However, we cannot just use the EssaNodeRefinement as the `val`, because the
|
// 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
|
// 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 EssaDefinition/EssaVariable that
|
// EssaNodes. So we need to go back from the SsaImpl::EssaDefinition/SsaImpl::EssaVariable that
|
||||||
// reaches the end of the module, to the first definition of the variable, and then
|
// 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
|
// track forwards using use-use flow to find a suitable CFG node that has flow into
|
||||||
// it from use-use flow.
|
// it from use-use flow.
|
||||||
exists(EssaVariable lastUseVar, EssaVariable firstDef |
|
exists(SsaImpl::EssaVariable lastUseVar, SsaImpl::EssaVariable firstDef |
|
||||||
lastUseVar.getName() = name and
|
lastUseVar.getName() = name and
|
||||||
// we ignore special variable $ introduced by our analysis (not used for anything)
|
// 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?
|
// we ignore special variable * introduced by `from <pkg> import *` -- TODO: understand why we even have this?
|
||||||
not name in ["$", "*"] and
|
not name in ["$", "*"] and
|
||||||
lastUseVar.getAUse() = m.getANormalExit() and
|
exists(Cfg::ControlFlowNode exit |
|
||||||
|
exit.isNormalExit() and exit.getScope() = m and lastUseVar.getAUse() = exit
|
||||||
|
) and
|
||||||
allowedEssaImportStep*(firstDef, lastUseVar) and
|
allowedEssaImportStep*(firstDef, lastUseVar) and
|
||||||
not allowedEssaImportStep(_, firstDef)
|
not allowedEssaImportStep(_, firstDef)
|
||||||
|
|
|
|
||||||
not LocalFlow::defToFirstUse(firstDef, _) and
|
not LocalFlow::defToFirstUse(firstDef, _) and
|
||||||
val.asCfgNode() = firstDef.getDefinition().(EssaNodeDefinition).getDefiningNode()
|
val.asCfgNode() = firstDef.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode()
|
||||||
or
|
or
|
||||||
exists(ControlFlowNode mid, ControlFlowNode end |
|
exists(Cfg::ControlFlowNode mid, Cfg::ControlFlowNode end |
|
||||||
LocalFlow::defToFirstUse(firstDef, mid) and
|
LocalFlow::defToFirstUse(firstDef, mid) and
|
||||||
LocalFlow::useToNextUse*(mid, end) and
|
LocalFlow::useToNextUse*(mid, end) and
|
||||||
not LocalFlow::useToNextUse(end, _) and
|
not LocalFlow::useToNextUse(end, _) and
|
||||||
@@ -155,9 +164,9 @@ module ImportResolution {
|
|||||||
* handles simple cases where we can statically tell that this is the case.
|
* handles simple cases where we can statically tell that this is the case.
|
||||||
*/
|
*/
|
||||||
private predicate all_mentions_name(Module m, string name) {
|
private predicate all_mentions_name(Module m, string name) {
|
||||||
exists(DefinitionNode def, SequenceNode n |
|
exists(Cfg::DefinitionNode def, Cfg::SequenceNode n |
|
||||||
def.getValue() = n and
|
def.getValue() = n and
|
||||||
def.(NameNode).getId() = "__all__" and
|
def.(Cfg::NameNode).getId() = "__all__" and
|
||||||
def.getScope() = m and
|
def.getScope() = m and
|
||||||
any(StringLiteral s | s.getText() = name) = n.getAnElement().getNode()
|
any(StringLiteral s | s.getText() = name) = n.getAnElement().getNode()
|
||||||
)
|
)
|
||||||
@@ -170,18 +179,20 @@ module ImportResolution {
|
|||||||
*/
|
*/
|
||||||
private predicate no_or_complicated_all(Module m) {
|
private predicate no_or_complicated_all(Module m) {
|
||||||
// No mention of `__all__` in the module
|
// No mention of `__all__` in the module
|
||||||
not exists(DefinitionNode def | def.getScope() = m and def.(NameNode).getId() = "__all__")
|
not exists(Cfg::DefinitionNode def |
|
||||||
|
def.getScope() = m and def.(Cfg::NameNode).getId() = "__all__"
|
||||||
|
)
|
||||||
or
|
or
|
||||||
// `__all__` is set to a non-sequence value
|
// `__all__` is set to a non-sequence value
|
||||||
exists(DefinitionNode def |
|
exists(Cfg::DefinitionNode def |
|
||||||
def.(NameNode).getId() = "__all__" and
|
def.(Cfg::NameNode).getId() = "__all__" and
|
||||||
def.getScope() = m and
|
def.getScope() = m and
|
||||||
not def.getValue() instanceof SequenceNode
|
not def.getValue() instanceof Cfg::SequenceNode
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// `__all__` is used in some way that doesn't involve storing a value in it. This usually means
|
// `__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.
|
// it is being mutated through `append` or `extend`, which we don't handle.
|
||||||
exists(NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
|
exists(Cfg::NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate potential_module_export(Module m, string name) {
|
private predicate potential_module_export(Module m, string name) {
|
||||||
@@ -189,7 +200,7 @@ module ImportResolution {
|
|||||||
or
|
or
|
||||||
no_or_complicated_all(m) and
|
no_or_complicated_all(m) and
|
||||||
(
|
(
|
||||||
exists(NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
|
exists(Cfg::NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
|
||||||
or
|
or
|
||||||
exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m)
|
exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m)
|
||||||
)
|
)
|
||||||
@@ -219,12 +230,12 @@ module ImportResolution {
|
|||||||
|
|
||||||
/** Gets a module that may have been added to `sys.modules`. */
|
/** Gets a module that may have been added to `sys.modules`. */
|
||||||
private Module sys_modules_module_with_name(string name) {
|
private Module sys_modules_module_with_name(string name) {
|
||||||
exists(ControlFlowNode n, DataFlow::Node mod |
|
exists(Cfg::ControlFlowNode n, DataFlow::Node mod |
|
||||||
exists(SubscriptNode sub |
|
exists(Cfg::SubscriptNode sub |
|
||||||
sub.getObject() = sys_modules_reference().asCfgNode() and
|
sub.getObject() = sys_modules_reference().asCfgNode() and
|
||||||
sub.getIndex() = n and
|
sub.getIndex() = n and
|
||||||
n.getNode().(StringLiteral).getText() = name and
|
n.getNode().(StringLiteral).getText() = name and
|
||||||
sub.(DefinitionNode).getValue() = mod.asCfgNode() and
|
sub.(Cfg::DefinitionNode).getValue() = mod.asCfgNode() and
|
||||||
mod = getModuleReference(result)
|
mod = getModuleReference(result)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -336,11 +347,11 @@ module ImportResolution {
|
|||||||
// name as a submodule, we always consider that this attribute _could_ be a
|
// 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
|
// reference to the submodule, even if we don't know that the submodule has been
|
||||||
// imported yet.
|
// imported yet.
|
||||||
exists(string submodule, Module package, EssaVariable var |
|
exists(string submodule, Module package, SsaImpl::EssaVariable var |
|
||||||
submodule = var.getName() and
|
submodule = var.getName() and
|
||||||
initModuleSubmoduleDefn(var.getSourceVariable(), package) and
|
initModuleSubmoduleDefn(var.getSourceVariable().getVariable(), package) and
|
||||||
m = getModuleFromName(package.getPackageName() + "." + submodule) and
|
m = getModuleFromName(package.getPackageName() + "." + submodule) and
|
||||||
result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode()
|
result.asCfgNode() = var.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ overlay[local]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
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.Builtins
|
||||||
private import semmle.python.dataflow.new.internal.ImportResolution
|
private import semmle.python.dataflow.new.internal.ImportResolution
|
||||||
private import semmle.python.dataflow.new.DataFlow
|
private import semmle.python.dataflow.new.DataFlow
|
||||||
@@ -15,7 +16,7 @@ module ImportStar {
|
|||||||
*/
|
*/
|
||||||
overlay[local]
|
overlay[local]
|
||||||
cached
|
cached
|
||||||
predicate namePossiblyDefinedInImportStar(NameNode n, string name, Scope s) {
|
predicate namePossiblyDefinedInImportStar(Cfg::NameNode n, string name, Scope s) {
|
||||||
n.isLoad() and
|
n.isLoad() and
|
||||||
name = n.getId() and
|
name = n.getId() and
|
||||||
s = n.getScope().getEnclosingScope*() and
|
s = n.getScope().getEnclosingScope*() and
|
||||||
@@ -52,7 +53,7 @@ module ImportStar {
|
|||||||
/** Holds if a global variable called `name` is assigned a value in the module `m`. */
|
/** Holds if a global variable called `name` is assigned a value in the module `m`. */
|
||||||
cached
|
cached
|
||||||
predicate globalNameDefinedInModule(string name, Module m) {
|
predicate globalNameDefinedInModule(string name, Module m) {
|
||||||
exists(NameNode n |
|
exists(Cfg::NameNode n |
|
||||||
not exists(LocalVariable v | n.defines(v)) and
|
not exists(LocalVariable v | n.defines(v)) and
|
||||||
n.isStore() and
|
n.isStore() and
|
||||||
name = n.getId() and
|
name = n.getId() and
|
||||||
@@ -66,7 +67,7 @@ module ImportStar {
|
|||||||
*/
|
*/
|
||||||
overlay[global]
|
overlay[global]
|
||||||
cached
|
cached
|
||||||
predicate importStarResolvesTo(NameNode n, Module m) {
|
predicate importStarResolvesTo(Cfg::NameNode n, Module m) {
|
||||||
m = getStarImported+(n.getEnclosingModule()) and
|
m = getStarImported+(n.getEnclosingModule()) and
|
||||||
globalNameDefinedInModule(n.getId(), m) and
|
globalNameDefinedInModule(n.getId(), m) and
|
||||||
not isDefinedLocally(n.getNode())
|
not isDefinedLocally(n.getNode())
|
||||||
@@ -99,7 +100,7 @@ module ImportStar {
|
|||||||
*/
|
*/
|
||||||
overlay[local]
|
overlay[local]
|
||||||
cached
|
cached
|
||||||
ControlFlowNode potentialImportStarBase(Scope s) {
|
Cfg::ControlFlowNode potentialImportStarBase(Scope s) {
|
||||||
result = any(ImportStarNode n | n.getScope() = s).getModule()
|
result = any(Cfg::ImportStarNode n | n.getScope() = s).getModule()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,6 +170,8 @@ overlay[local]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
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
|
private import DataFlowPublic
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,7 +180,7 @@ private import DataFlowPublic
|
|||||||
* This class abstracts away the differing representations of comprehensions and
|
* This class abstracts away the differing representations of comprehensions and
|
||||||
* for statements.
|
* for statements.
|
||||||
*/
|
*/
|
||||||
class ForTarget extends ControlFlowNode {
|
class ForTarget extends Cfg::ControlFlowNode {
|
||||||
Expr source;
|
Expr source;
|
||||||
|
|
||||||
ForTarget() {
|
ForTarget() {
|
||||||
@@ -198,7 +200,7 @@ class ForTarget extends ControlFlowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The LHS of an assignment, it also records the assigned value. */
|
/** The LHS of an assignment, it also records the assigned value. */
|
||||||
class AssignmentTarget extends ControlFlowNode {
|
class AssignmentTarget extends Cfg::ControlFlowNode {
|
||||||
Expr value;
|
Expr value;
|
||||||
|
|
||||||
AssignmentTarget() {
|
AssignmentTarget() {
|
||||||
@@ -209,7 +211,7 @@ class AssignmentTarget extends ControlFlowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A direct (or top-level) target of an unpacking assignment. */
|
/** A direct (or top-level) target of an unpacking assignment. */
|
||||||
class UnpackingAssignmentDirectTarget extends ControlFlowNode instanceof SequenceNode {
|
class UnpackingAssignmentDirectTarget extends Cfg::ControlFlowNode instanceof Cfg::SequenceNode {
|
||||||
Expr value;
|
Expr value;
|
||||||
|
|
||||||
UnpackingAssignmentDirectTarget() {
|
UnpackingAssignmentDirectTarget() {
|
||||||
@@ -222,7 +224,7 @@ class UnpackingAssignmentDirectTarget extends ControlFlowNode instanceof Sequenc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A (possibly recursive) target of an unpacking assignment. */
|
/** A (possibly recursive) target of an unpacking assignment. */
|
||||||
class UnpackingAssignmentTarget extends ControlFlowNode {
|
class UnpackingAssignmentTarget extends Cfg::ControlFlowNode {
|
||||||
UnpackingAssignmentTarget() {
|
UnpackingAssignmentTarget() {
|
||||||
this instanceof UnpackingAssignmentDirectTarget
|
this instanceof UnpackingAssignmentDirectTarget
|
||||||
or
|
or
|
||||||
@@ -231,10 +233,11 @@ class UnpackingAssignmentTarget extends ControlFlowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A (possibly recursive) target of an unpacking assignment which is also a sequence. */
|
/** A (possibly recursive) target of an unpacking assignment which is also a sequence. */
|
||||||
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof SequenceNode {
|
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof Cfg::SequenceNode
|
||||||
ControlFlowNode getElement(int i) { result = super.getElement(i) }
|
{
|
||||||
|
Cfg::ControlFlowNode getElement(int i) { result = super.getElement(i) }
|
||||||
|
|
||||||
ControlFlowNode getAnElement() { result = this.getElement(_) }
|
Cfg::ControlFlowNode getAnElement() { result = this.getElement(_) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -255,7 +258,7 @@ predicate iterableUnpackingAssignmentFlowStep(Node nodeFrom, Node nodeTo) {
|
|||||||
predicate iterableUnpackingForReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
|
predicate iterableUnpackingForReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
|
||||||
exists(ForTarget target |
|
exists(ForTarget target |
|
||||||
nodeFrom.getNode().getNode() = target.getSource() and
|
nodeFrom.getNode().getNode() = target.getSource() and
|
||||||
target instanceof SequenceNode and
|
target instanceof Cfg::SequenceNode and
|
||||||
nodeTo = TIterableSequenceNode(target)
|
nodeTo = TIterableSequenceNode(target)
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
@@ -323,11 +326,11 @@ predicate iterableUnpackingConvertingStoreStep(Node nodeFrom, Content c, Node no
|
|||||||
*/
|
*/
|
||||||
predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||||
exists(
|
exists(
|
||||||
UnpackingAssignmentSequenceTarget target, int index, ControlFlowNode element, int starIndex
|
UnpackingAssignmentSequenceTarget target, int index, Cfg::ControlFlowNode element, int starIndex
|
||||||
|
|
|
|
||||||
target.getElement(starIndex) instanceof StarredNode
|
target.getElement(starIndex) instanceof Cfg::StarredNode
|
||||||
or
|
or
|
||||||
not exists(target.getAnElement().(StarredNode)) and
|
not exists(target.getAnElement().(Cfg::StarredNode)) and
|
||||||
starIndex = -1
|
starIndex = -1
|
||||||
|
|
|
|
||||||
nodeFrom.(CfgNode).getNode() = target and
|
nodeFrom.(CfgNode).getNode() = target and
|
||||||
@@ -342,18 +345,18 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
|
|||||||
else c.(TupleElementContent).getIndex() >= index - 1
|
else c.(TupleElementContent).getIndex() >= index - 1
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
if element instanceof SequenceNode
|
if element instanceof Cfg::SequenceNode
|
||||||
then
|
then
|
||||||
// Step 5b
|
// Step 5b
|
||||||
nodeTo = TIterableSequenceNode(element)
|
nodeTo = TIterableSequenceNode(element)
|
||||||
else
|
else
|
||||||
if element instanceof StarredNode
|
if element instanceof Cfg::StarredNode
|
||||||
then
|
then
|
||||||
// Step 5c
|
// Step 5c
|
||||||
nodeTo = TIterableElementNode(element)
|
nodeTo = TIterableElementNode(element)
|
||||||
else
|
else
|
||||||
// Step 5a
|
// Step 5a
|
||||||
exists(MultiAssignmentDefinition mad | element = mad.getDefiningNode() |
|
exists(SsaImpl::MultiAssignmentDefinition mad | element = mad.getDefiningNode() |
|
||||||
nodeTo.(CfgNode).getNode() = element
|
nodeTo.(CfgNode).getNode() = element
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -366,7 +369,7 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
|
|||||||
* content type `ListElementContent`.
|
* content type `ListElementContent`.
|
||||||
*/
|
*/
|
||||||
predicate iterableUnpackingStarredElementStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
predicate iterableUnpackingStarredElementStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||||
exists(ControlFlowNode starred, MultiAssignmentDefinition mad |
|
exists(Cfg::ControlFlowNode starred, SsaImpl::MultiAssignmentDefinition mad |
|
||||||
starred.getNode() instanceof Starred and
|
starred.getNode() instanceof Starred and
|
||||||
starred = mad.getDefiningNode()
|
starred = mad.getDefiningNode()
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ overlay[local]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import DataFlowPublic
|
import DataFlowPublic
|
||||||
private import DataFlowPrivate
|
private import DataFlowPrivate
|
||||||
private import semmle.python.internal.CachedStages
|
private import semmle.python.internal.CachedStages
|
||||||
@@ -314,7 +315,7 @@ private module Cached {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate subscript(LocalSourceNode node, CfgNode subscript, CfgNode index) {
|
predicate subscript(LocalSourceNode node, CfgNode subscript, CfgNode index) {
|
||||||
exists(CfgNode seq, SubscriptNode subscriptNode | subscriptNode = subscript.getNode() |
|
exists(CfgNode seq, Cfg::SubscriptNode subscriptNode | subscriptNode = subscript.getNode() |
|
||||||
node.flowsTo(seq) and
|
node.flowsTo(seq) and
|
||||||
seq.getNode() = subscriptNode.getObject() and
|
seq.getNode() = subscriptNode.getObject() and
|
||||||
index.getNode() = subscriptNode.getIndex()
|
index.getNode() = subscriptNode.getIndex()
|
||||||
|
|||||||
@@ -91,9 +91,7 @@ predicate matchAsFlowStep(Node nodeFrom, Node nodeTo) {
|
|||||||
or
|
or
|
||||||
// the interior pattern flows to the alias
|
// the interior pattern flows to the alias
|
||||||
nodeFrom.(CfgNode).getNode().getNode() = subject.getPattern() and
|
nodeFrom.(CfgNode).getNode().getNode() = subject.getPattern() and
|
||||||
exists(PatternAliasDefinition pad | pad.getDefiningNode().getNode() = alias |
|
nodeTo.(CfgNode).getNode().getNode() = alias
|
||||||
nodeTo.(CfgNode).getNode() = pad.getDefiningNode()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +122,9 @@ predicate matchLiteralFlowStep(Node nodeFrom, Node nodeTo) {
|
|||||||
* syntax (toplevel): `case var:`
|
* syntax (toplevel): `case var:`
|
||||||
*/
|
*/
|
||||||
predicate matchCaptureFlowStep(Node nodeFrom, Node nodeTo) {
|
predicate matchCaptureFlowStep(Node nodeFrom, Node nodeTo) {
|
||||||
exists(MatchCapturePattern capture, Name var | capture.getVariable() = var |
|
exists(MatchCapturePattern capture |
|
||||||
nodeFrom.(CfgNode).getNode().getNode() = capture and
|
nodeFrom.(CfgNode).getNode().getNode() = capture and
|
||||||
exists(PatternCaptureDefinition pcd | pcd.getDefiningNode().getNode() = var |
|
nodeTo.(CfgNode).getNode().getNode() = capture.getVariable()
|
||||||
nodeTo.(CfgNode).getNode() = pcd.getDefiningNode()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
547
python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll
Normal file
547
python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
/**
|
||||||
|
* 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,4 +1,6 @@
|
|||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
@@ -75,7 +77,7 @@ import Cached
|
|||||||
* and isn't a big problem in practice.
|
* and isn't a big problem in practice.
|
||||||
*/
|
*/
|
||||||
predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
||||||
exists(BinaryExprNode add | add = nodeTo.getNode() |
|
exists(Cfg::BinaryExprNode add | add = nodeTo.getNode() |
|
||||||
add.getOp() instanceof Add and add.getAnOperand() = nodeFrom.getNode()
|
add.getOp() instanceof Add and add.getAnOperand() = nodeFrom.getNode()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -84,7 +86,7 @@ predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
|||||||
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to subscripting.
|
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to subscripting.
|
||||||
*/
|
*/
|
||||||
predicate subscriptStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
predicate subscriptStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
|
||||||
nodeTo.getNode().(SubscriptNode).getObject() = nodeFrom.getNode()
|
nodeTo.getNode().(Cfg::SubscriptNode).getObject() = nodeFrom.getNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,15 +102,15 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
|
|||||||
(
|
(
|
||||||
call = API::builtin(["str", "bytes", "unicode"]).getACall()
|
call = API::builtin(["str", "bytes", "unicode"]).getACall()
|
||||||
or
|
or
|
||||||
call.getFunction().asCfgNode().(NameNode).getId() in ["str", "bytes", "unicode"]
|
call.getFunction().asCfgNode().(Cfg::NameNode).getId() in ["str", "bytes", "unicode"]
|
||||||
) and
|
) and
|
||||||
nodeFrom in [call.getArg(0), call.getArgByName("object")]
|
nodeFrom in [call.getArg(0), call.getArgByName("object")]
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// String methods. Note that this doesn't recognize `meth = "foo".upper; meth()`
|
// String methods. Note that this doesn't recognize `meth = "foo".upper; meth()`
|
||||||
exists(CallNode call, string method_name, ControlFlowNode object |
|
exists(Cfg::CallNode call, string method_name, Cfg::ControlFlowNode object |
|
||||||
call = nodeTo.getNode() and
|
call = nodeTo.getNode() and
|
||||||
object = call.getFunction().(AttrNode).getObject(method_name)
|
object = call.getFunction().(Cfg::AttrNode).getObject(method_name)
|
||||||
|
|
|
|
||||||
nodeFrom.getNode() = object and
|
nodeFrom.getNode() = object and
|
||||||
method_name in [
|
method_name in [
|
||||||
@@ -139,7 +141,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
// % formatting
|
// % formatting
|
||||||
exists(BinaryExprNode fmt | fmt = nodeTo.getNode() |
|
exists(Cfg::BinaryExprNode fmt | fmt = nodeTo.getNode() |
|
||||||
fmt.getOp() instanceof Mod and
|
fmt.getOp() instanceof Mod and
|
||||||
(
|
(
|
||||||
fmt.getLeft() = nodeFrom.getNode()
|
fmt.getLeft() = nodeFrom.getNode()
|
||||||
@@ -149,7 +151,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
// string multiplication -- `"foo" * 10`
|
// string multiplication -- `"foo" * 10`
|
||||||
exists(BinaryExprNode mult | mult = nodeTo.getNode() |
|
exists(Cfg::BinaryExprNode mult | mult = nodeTo.getNode() |
|
||||||
mult.getOp() instanceof Mult and
|
mult.getOp() instanceof Mult and
|
||||||
mult.getLeft() = nodeFrom.getNode()
|
mult.getLeft() = nodeFrom.getNode()
|
||||||
)
|
)
|
||||||
@@ -207,8 +209,8 @@ predicate awaitStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
|||||||
* the variable `f` is tainted if the result of `open("foo")` is tainted.
|
* the variable `f` is tainted if the result of `open("foo")` is tainted.
|
||||||
*/
|
*/
|
||||||
predicate asyncWithStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
predicate asyncWithStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
exists(With with, ControlFlowNode contextManager, ControlFlowNode var |
|
exists(With with, Cfg::ControlFlowNode contextManager, Cfg::ControlFlowNode var |
|
||||||
var = any(WithDefinition wd).getDefiningNode()
|
var = any(SsaImpl::WithDefinition wd).getDefiningNode()
|
||||||
|
|
|
|
||||||
nodeFrom.(DataFlow::CfgNode).getNode() = contextManager and
|
nodeFrom.(DataFlow::CfgNode).getNode() = contextManager and
|
||||||
nodeTo.(DataFlow::CfgNode).getNode() = var and
|
nodeTo.(DataFlow::CfgNode).getNode() = var and
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import codeql.util.Unit
|
|||||||
import codeql.typetracking.TypeTracking as Shared
|
import codeql.typetracking.TypeTracking as Shared
|
||||||
import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl
|
import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl
|
||||||
private import python
|
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.internal.CachedStages
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic
|
private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||||
@@ -94,8 +96,10 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
|||||||
Node returnOf(Node callable, SummaryComponent return) {
|
Node returnOf(Node callable, SummaryComponent return) {
|
||||||
return = FlowSummaryImpl::Private::SummaryComponent::return() and
|
return = FlowSummaryImpl::Private::SummaryComponent::return() and
|
||||||
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
|
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
|
||||||
result.asCfgNode() =
|
exists(Return ret |
|
||||||
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
|
ret.getScope() = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and
|
||||||
|
result.asCfgNode().getNode() = ret.getValue()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relating callables to nodes
|
// Relating callables to nodes
|
||||||
@@ -160,7 +164,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
|||||||
// ignore the flow steps from the synthetic sequence node to the real sequence node,
|
// 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
|
// since we only support one level of content in type-trackers, and the nested
|
||||||
// structure requires two levels at least to be useful.
|
// structure requires two levels at least to be useful.
|
||||||
not exists(SequenceNode outer |
|
not exists(Cfg::SequenceNode outer |
|
||||||
outer.getAnElement() = nodeTo.asCfgNode() and
|
outer.getAnElement() = nodeTo.asCfgNode() and
|
||||||
IterableUnpacking::iterableUnpackingTupleFlowStep(nodeFrom, nodeTo)
|
IterableUnpacking::iterableUnpackingTupleFlowStep(nodeFrom, nodeTo)
|
||||||
)
|
)
|
||||||
@@ -259,7 +263,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
|||||||
// Since we only support one level of content in type-trackers we don't actually
|
// 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,
|
// support `(aa, ab), (ba, bb) = ...`. Therefore we exclude the read-step from `(aa,
|
||||||
// ab)` to `aa` (since it is not needed).
|
// ab)` to `aa` (since it is not needed).
|
||||||
not exists(SequenceNode outer |
|
not exists(Cfg::SequenceNode outer |
|
||||||
outer.getAnElement() = nodeFrom.asCfgNode() and
|
outer.getAnElement() = nodeFrom.asCfgNode() and
|
||||||
IterableUnpacking::iterableUnpackingTupleFlowStep(_, nodeFrom)
|
IterableUnpacking::iterableUnpackingTupleFlowStep(_, nodeFrom)
|
||||||
) and
|
) and
|
||||||
@@ -269,7 +273,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
|||||||
IterableUnpacking::iterableUnpackingForReadStep(_, _, seq) and
|
IterableUnpacking::iterableUnpackingForReadStep(_, _, seq) and
|
||||||
IterableUnpacking::iterableUnpackingConvertingReadStep(seq, _, elem) and
|
IterableUnpacking::iterableUnpackingConvertingReadStep(seq, _, elem) and
|
||||||
IterableUnpacking::iterableUnpackingConvertingStoreStep(elem, _, nodeFrom) and
|
IterableUnpacking::iterableUnpackingConvertingStoreStep(elem, _, nodeFrom) and
|
||||||
nodeFrom.asCfgNode() instanceof SequenceNode
|
nodeFrom.asCfgNode() instanceof Cfg::SequenceNode
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, content)
|
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, content)
|
||||||
@@ -306,13 +310,15 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
|||||||
//
|
//
|
||||||
// nodeFrom is `expr`
|
// nodeFrom is `expr`
|
||||||
// nodeTo is entry node for `f`
|
// nodeTo is entry node for `f`
|
||||||
exists(ScopeEntryDefinition e, SsaSourceVariable var, DefinitionNode def |
|
exists(
|
||||||
|
SsaImpl::ScopeEntryDefinition e, SsaImpl::SsaSourceVariable var, Cfg::DefinitionNode def
|
||||||
|
|
|
||||||
e.getSourceVariable() = var and
|
e.getSourceVariable() = var and
|
||||||
var.hasDefiningNode(def)
|
def.getNode() = var.getVariable().getAStore()
|
||||||
|
|
|
|
||||||
nodeTo.(DataFlowPublic::ScopeEntryDefinitionNode).getDefinition() = e and
|
nodeTo.(DataFlowPublic::ScopeEntryDefinitionNode).getDefinition() = e and
|
||||||
nodeFrom.asCfgNode() = def and
|
nodeFrom.asCfgNode() = def and
|
||||||
var.getScope().getScope*() = nodeFrom.getScope()
|
var.getVariable().getScope().getScope*() = nodeFrom.getScope()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ overlay[local]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
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 DataFlowPublic
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||||
private import codeql.dataflow.VariableCapture as Shared
|
private import codeql.dataflow.VariableCapture as Shared
|
||||||
@@ -14,10 +17,10 @@ private import codeql.dataflow.VariableCapture as Shared
|
|||||||
// The first is the main implementation, the second is a performance motivated restriction.
|
// 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
|
// The restriction is to clear any `CapturedVariableContent` before writing a new one
|
||||||
// to avoid long access paths (see the link for a nice explanation).
|
// to avoid long access paths (see the link for a nice explanation).
|
||||||
private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBlock> {
|
private module CaptureInput implements Shared::InputSig<Location, CfgImpl::BasicBlock> {
|
||||||
private import python as PY
|
private import python as PY
|
||||||
|
|
||||||
additional class ExprCfgNode extends ControlFlowNode {
|
additional class ExprCfgNode extends Cfg::ControlFlowNode {
|
||||||
ExprCfgNode() { isExpressionNode(this) }
|
ExprCfgNode() { isExpressionNode(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +28,9 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
|||||||
predicate isConstructor() { none() }
|
predicate isConstructor() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
Callable basicBlockGetEnclosingCallable(Cfg::BasicBlock bb) { result = bb.getScope() }
|
Callable basicBlockGetEnclosingCallable(CfgImpl::BasicBlock bb) {
|
||||||
|
result = bb.getEnclosingCallable().asScope()
|
||||||
|
}
|
||||||
|
|
||||||
class CapturedVariable extends LocalVariable {
|
class CapturedVariable extends LocalVariable {
|
||||||
Function f;
|
Function f;
|
||||||
@@ -51,27 +56,29 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
|||||||
class CapturedParameter extends CapturedVariable {
|
class CapturedParameter extends CapturedVariable {
|
||||||
CapturedParameter() { this.isParameter() }
|
CapturedParameter() { this.isParameter() }
|
||||||
|
|
||||||
ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
|
Cfg::ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class Expr extends ExprCfgNode {
|
class Expr extends ExprCfgNode {
|
||||||
predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) }
|
predicate hasCfgNode(CfgImpl::BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class VariableWrite extends ControlFlowNode {
|
class VariableWrite extends Cfg::ControlFlowNode {
|
||||||
CapturedVariable v;
|
CapturedVariable v;
|
||||||
|
|
||||||
VariableWrite() { this = v.getAStore().getAFlowNode().(DefinitionNode).getValue() }
|
VariableWrite() {
|
||||||
|
exists(Cfg::DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue())
|
||||||
|
}
|
||||||
|
|
||||||
CapturedVariable getVariable() { result = v }
|
CapturedVariable getVariable() { result = v }
|
||||||
|
|
||||||
predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) }
|
predicate hasCfgNode(CfgImpl::BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class VariableRead extends Expr {
|
class VariableRead extends Expr {
|
||||||
CapturedVariable v;
|
CapturedVariable v;
|
||||||
|
|
||||||
VariableRead() { this = v.getALoad().getAFlowNode() }
|
VariableRead() { this.getNode() = v.getALoad() }
|
||||||
|
|
||||||
CapturedVariable getVariable() { result = v }
|
CapturedVariable getVariable() { result = v }
|
||||||
}
|
}
|
||||||
@@ -80,9 +87,14 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
|||||||
// TODO: Other languages have an extra case here looking like
|
// TODO: Other languages have an extra case here looking like
|
||||||
// simpleAstFlowStep(nodeFrom, nodeTo)
|
// simpleAstFlowStep(nodeFrom, nodeTo)
|
||||||
// we should investigate the potential benefit of adding that.
|
// we should investigate the potential benefit of adding that.
|
||||||
exists(SsaVariable def |
|
exists(SsaImpl::EssaVariable def |
|
||||||
def.getAUse() = nodeTo and
|
def.getAUse() = nodeTo and
|
||||||
def.getAnUltimateDefinition().getDefinition().(DefinitionNode).getValue() = nodeFrom
|
def.getAnUltimateDefinition()
|
||||||
|
.getDefinition()
|
||||||
|
.(SsaImpl::EssaNodeDefinition)
|
||||||
|
.getDefiningNode()
|
||||||
|
.(Cfg::DefinitionNode)
|
||||||
|
.getValue() = nodeFrom
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +119,7 @@ class CapturedVariable = CaptureInput::CapturedVariable;
|
|||||||
|
|
||||||
class ClosureExpr = CaptureInput::ClosureExpr;
|
class ClosureExpr = CaptureInput::ClosureExpr;
|
||||||
|
|
||||||
module Flow = Shared::Flow<Location, Cfg, CaptureInput>;
|
module Flow = Shared::Flow<Location, Cfg::CfgSigImpl, CaptureInput>;
|
||||||
|
|
||||||
private Flow::ClosureNode asClosureNode(Node n) {
|
private Flow::ClosureNode asClosureNode(Node n) {
|
||||||
result = n.(SynthCaptureNode).getSynthesizedCaptureNode()
|
result = n.(SynthCaptureNode).getSynthesizedCaptureNode()
|
||||||
|
|||||||
@@ -448,8 +448,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
|
|||||||
context = TNoParam() and
|
context = TNoParam() and
|
||||||
src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and
|
src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and
|
||||||
node.asCfgNode() = call and
|
node.asCfgNode() = call and
|
||||||
retval.asCfgNode() =
|
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
|
||||||
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
|
|
||||||
) and
|
) and
|
||||||
edgeLabel = "return"
|
edgeLabel = "return"
|
||||||
}
|
}
|
||||||
@@ -471,8 +470,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
|
|||||||
this.callContexts(call, src, pyfunc, context, callee) and
|
this.callContexts(call, src, pyfunc, context, callee) and
|
||||||
retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and
|
retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and
|
||||||
node.asCfgNode() = call and
|
node.asCfgNode() = call and
|
||||||
retval.asCfgNode() =
|
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
|
||||||
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
|
|
||||||
) and
|
) and
|
||||||
edgeLabel = "call"
|
edgeLabel = "call"
|
||||||
}
|
}
|
||||||
@@ -716,8 +714,10 @@ private class EssaTaintTracking extends string instanceof TaintTracking::Configu
|
|||||||
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
|
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
|
||||||
path.noAttribute()
|
path.noAttribute()
|
||||||
|
|
|
|
||||||
assign.getValue().getAFlowNode() = srcnode.asCfgNode() and
|
srcnode.asCfgNode().getNode() = assign.getValue() and
|
||||||
depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and
|
exists(SequenceNode left_parent | left_parent.getNode() = assign.getATarget() |
|
||||||
|
depth = iterable_unpacking_descent(left_parent, defn.getDefiningNode())
|
||||||
|
) and
|
||||||
kind = taint_at_depth(srckind, depth)
|
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
|
* - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1
|
||||||
*/
|
*/
|
||||||
int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) {
|
int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) {
|
||||||
exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and
|
exists(Assign a | left_parent.getNode() = a.getATarget().getASubExpression*()) and
|
||||||
left_parent.getAnElement() = left_defn and
|
left_parent.getAnElement() = left_defn and
|
||||||
// Handle `a, *b = some_iterable`
|
// Handle `a, *b = some_iterable`
|
||||||
if left_defn instanceof StarredNode then result = 0 else result = 1
|
if left_defn instanceof StarredNode then result = 0 else result = 1
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ module SsaSource {
|
|||||||
predicate with_definition(Variable v, ControlFlowNode defn) {
|
predicate with_definition(Variable v, ControlFlowNode defn) {
|
||||||
exists(With with, Name var |
|
exists(With with, Name var |
|
||||||
with.getOptionalVars() = var and
|
with.getOptionalVars() = var and
|
||||||
var.getAFlowNode() = defn
|
defn.getNode() = var
|
||||||
|
|
|
|
||||||
var = v.getAStore()
|
var = v.getAStore()
|
||||||
)
|
)
|
||||||
@@ -67,7 +67,7 @@ module SsaSource {
|
|||||||
predicate pattern_capture_definition(Variable v, ControlFlowNode defn) {
|
predicate pattern_capture_definition(Variable v, ControlFlowNode defn) {
|
||||||
exists(MatchCapturePattern capture, Name var |
|
exists(MatchCapturePattern capture, Name var |
|
||||||
capture.getVariable() = var and
|
capture.getVariable() = var and
|
||||||
var.getAFlowNode() = defn
|
defn.getNode() = var
|
||||||
|
|
|
|
||||||
var = v.getAStore()
|
var = v.getAStore()
|
||||||
)
|
)
|
||||||
@@ -78,7 +78,7 @@ module SsaSource {
|
|||||||
predicate pattern_alias_definition(Variable v, ControlFlowNode defn) {
|
predicate pattern_alias_definition(Variable v, ControlFlowNode defn) {
|
||||||
exists(MatchAsPattern pattern, Name var |
|
exists(MatchAsPattern pattern, Name var |
|
||||||
pattern.getAlias() = var and
|
pattern.getAlias() = var and
|
||||||
var.getAFlowNode() = defn
|
defn.getNode() = var
|
||||||
|
|
|
|
||||||
var = v.getAStore()
|
var = v.getAStore()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
@@ -59,7 +60,7 @@ module Bottle {
|
|||||||
|
|
||||||
override Parameter getARoutedParameter() { none() }
|
override Parameter getARoutedParameter() { none() }
|
||||||
|
|
||||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +74,10 @@ module Bottle {
|
|||||||
/** A response returned by a view callable. */
|
/** A response returned by a view callable. */
|
||||||
class BottleReturnResponse extends Http::Server::HttpResponse::Range {
|
class BottleReturnResponse extends Http::Server::HttpResponse::Range {
|
||||||
BottleReturnResponse() {
|
BottleReturnResponse() {
|
||||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode()
|
exists(Return ret |
|
||||||
|
ret.getScope() = any(View::ViewCallable vc) and
|
||||||
|
this.asCfgNode().getNode() = ret.getValue()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlow::Node getBody() { result = this }
|
override DataFlow::Node getBody() { result = this }
|
||||||
@@ -154,9 +158,9 @@ module Bottle {
|
|||||||
DataFlow::Node value;
|
DataFlow::Node value;
|
||||||
|
|
||||||
HeaderWriteSubscript() {
|
HeaderWriteSubscript() {
|
||||||
exists(SubscriptNode subscript |
|
exists(Cfg::SubscriptNode subscript |
|
||||||
this.asCfgNode() = subscript and
|
this.asCfgNode() = subscript and
|
||||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
name.asCfgNode() = subscript.getIndex() and
|
name.asCfgNode() = subscript.getIndex() and
|
||||||
subscript.getObject() = headers().asSource().asCfgNode()
|
subscript.getObject() = headers().asSource().asCfgNode()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
@@ -1305,7 +1306,7 @@ module PrivateDjango {
|
|||||||
dict.(DataFlow::MethodCallNode).calls(files, "dict")
|
dict.(DataFlow::MethodCallNode).calls(files, "dict")
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
this.asCfgNode().(SubscriptNode).getObject() = dict.asCfgNode()
|
this.asCfgNode().(Cfg::SubscriptNode).getObject() = dict.asCfgNode()
|
||||||
or
|
or
|
||||||
this.(DataFlow::MethodCallNode).calls(dict, "get")
|
this.(DataFlow::MethodCallNode).calls(dict, "get")
|
||||||
)
|
)
|
||||||
@@ -1314,7 +1315,7 @@ module PrivateDjango {
|
|||||||
exists(DataFlow::AttrRead files, DataFlow::MethodCallNode getlistCall |
|
exists(DataFlow::AttrRead files, DataFlow::MethodCallNode getlistCall |
|
||||||
files.accesses(instance(), "FILES") and
|
files.accesses(instance(), "FILES") and
|
||||||
getlistCall.calls(files, "getlist") and
|
getlistCall.calls(files, "getlist") and
|
||||||
this.asCfgNode().(SubscriptNode).getObject() = getlistCall.asCfgNode()
|
this.asCfgNode().(Cfg::SubscriptNode).getObject() = getlistCall.asCfgNode()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2216,7 +2217,7 @@ module PrivateDjango {
|
|||||||
DataFlow::Node value;
|
DataFlow::Node value;
|
||||||
|
|
||||||
DjangoResponseCookieSubscriptWrite() {
|
DjangoResponseCookieSubscriptWrite() {
|
||||||
exists(SubscriptNode subscript, DataFlow::AttrRead cookieLookup |
|
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead cookieLookup |
|
||||||
// To give `this` a value, we need to choose between either LHS or RHS,
|
// To give `this` a value, we need to choose between either LHS or RHS,
|
||||||
// and just go with the LHS
|
// and just go with the LHS
|
||||||
this.asCfgNode() = subscript
|
this.asCfgNode() = subscript
|
||||||
@@ -2228,7 +2229,7 @@ module PrivateDjango {
|
|||||||
|
|
|
|
||||||
cookieLookup.flowsTo(subscriptObj)
|
cookieLookup.flowsTo(subscriptObj)
|
||||||
) and
|
) and
|
||||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
index.asCfgNode() = subscript.getIndex()
|
index.asCfgNode() = subscript.getIndex()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2249,7 +2250,7 @@ module PrivateDjango {
|
|||||||
DataFlow::Node value;
|
DataFlow::Node value;
|
||||||
|
|
||||||
DjangoResponseHeaderSubscriptWrite() {
|
DjangoResponseHeaderSubscriptWrite() {
|
||||||
exists(SubscriptNode subscript, DataFlow::AttrRead headerLookup |
|
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead headerLookup |
|
||||||
// To give `this` a value, we need to choose between either LHS or RHS,
|
// To give `this` a value, we need to choose between either LHS or RHS,
|
||||||
// and just go with the LHS
|
// and just go with the LHS
|
||||||
this.asCfgNode() = subscript
|
this.asCfgNode() = subscript
|
||||||
@@ -2261,7 +2262,7 @@ module PrivateDjango {
|
|||||||
|
|
|
|
||||||
headerLookup.flowsTo(subscriptObj)
|
headerLookup.flowsTo(subscriptObj)
|
||||||
) and
|
) and
|
||||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
index.asCfgNode() = subscript.getIndex()
|
index.asCfgNode() = subscript.getIndex()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2284,14 +2285,14 @@ module PrivateDjango {
|
|||||||
DataFlow::Node value;
|
DataFlow::Node value;
|
||||||
|
|
||||||
DjangoResponseSubscriptWrite() {
|
DjangoResponseSubscriptWrite() {
|
||||||
exists(SubscriptNode subscript |
|
exists(Cfg::SubscriptNode subscript |
|
||||||
// To give `this` a value, we need to choose between either LHS or RHS,
|
// To give `this` a value, we need to choose between either LHS or RHS,
|
||||||
// and just go with the LHS
|
// and just go with the LHS
|
||||||
this.asCfgNode() = subscript
|
this.asCfgNode() = subscript
|
||||||
|
|
|
|
||||||
subscript.getObject() =
|
subscript.getObject() =
|
||||||
DjangoImpl::DjangoHttp::Response::HttpResponse::instance().asCfgNode() and
|
DjangoImpl::DjangoHttp::Response::HttpResponse::instance().asCfgNode() and
|
||||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
index.asCfgNode() = subscript.getIndex()
|
index.asCfgNode() = subscript.getIndex()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2426,7 +2427,7 @@ module PrivateDjango {
|
|||||||
/** Gets a reference to the result of calling the `as_view` classmethod of this class. */
|
/** Gets a reference to the result of calling the `as_view` classmethod of this class. */
|
||||||
private DataFlow::TypeTrackingNode asViewResult(DataFlow::TypeTracker t) {
|
private DataFlow::TypeTrackingNode asViewResult(DataFlow::TypeTracker t) {
|
||||||
t.start() and
|
t.start() and
|
||||||
result.asCfgNode().(CallNode).getFunction() = this.asViewRef().asCfgNode()
|
result.asCfgNode().(Cfg::CallNode).getFunction() = this.asViewRef().asCfgNode()
|
||||||
or
|
or
|
||||||
exists(DataFlow::TypeTracker t2 | result = this.asViewResult(t2).track(t2, t))
|
exists(DataFlow::TypeTracker t2 | result = this.asViewResult(t2).track(t2, t))
|
||||||
}
|
}
|
||||||
@@ -2872,7 +2873,10 @@ module PrivateDjango {
|
|||||||
DataFlow::CfgNode
|
DataFlow::CfgNode
|
||||||
{
|
{
|
||||||
DjangoRedirectViewGetRedirectUrlReturn() {
|
DjangoRedirectViewGetRedirectUrlReturn() {
|
||||||
node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode()
|
exists(Return ret |
|
||||||
|
ret.getScope() = any(GetRedirectUrlFunction f) and
|
||||||
|
node.getNode() = ret.getValue()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlow::Node getRedirectLocation() { result = this }
|
override DataFlow::Node getRedirectLocation() { result = this }
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
@@ -129,7 +130,7 @@ module FastApi {
|
|||||||
result in [this.getArg(0), this.getArgByName("path")]
|
result in [this.getArg(0), this.getArgByName("path")]
|
||||||
}
|
}
|
||||||
|
|
||||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||||
|
|
||||||
override string getFramework() { result = "FastAPI" }
|
override string getFramework() { result = "FastAPI" }
|
||||||
|
|
||||||
@@ -309,7 +310,10 @@ module FastApi {
|
|||||||
FastApiRouteSetup routeSetup;
|
FastApiRouteSetup routeSetup;
|
||||||
|
|
||||||
FastApiRequestHandlerReturn() {
|
FastApiRequestHandlerReturn() {
|
||||||
node = routeSetup.getARequestHandler().getAReturnValueFlowNode()
|
exists(Return ret |
|
||||||
|
ret.getScope() = routeSetup.getARequestHandler() and
|
||||||
|
node.getNode() = ret.getValue()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlow::Node getBody() { result = this }
|
override DataFlow::Node getBody() { result = this }
|
||||||
@@ -438,7 +442,7 @@ module FastApi {
|
|||||||
DataFlow::Node value;
|
DataFlow::Node value;
|
||||||
|
|
||||||
HeaderSubscriptWrite() {
|
HeaderSubscriptWrite() {
|
||||||
exists(SubscriptNode subscript, DataFlow::AttrRead headerLookup |
|
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead headerLookup |
|
||||||
// To give `this` a value, we need to choose between either LHS or RHS,
|
// To give `this` a value, we need to choose between either LHS or RHS,
|
||||||
// and just go with the LHS
|
// and just go with the LHS
|
||||||
this.asCfgNode() = subscript
|
this.asCfgNode() = subscript
|
||||||
@@ -447,7 +451,7 @@ module FastApi {
|
|||||||
exists(DataFlow::Node subscriptObj | subscriptObj.asCfgNode() = subscript.getObject() |
|
exists(DataFlow::Node subscriptObj | subscriptObj.asCfgNode() = subscript.getObject() |
|
||||||
headerLookup.flowsTo(subscriptObj)
|
headerLookup.flowsTo(subscriptObj)
|
||||||
) and
|
) and
|
||||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
index.asCfgNode() = subscript.getIndex()
|
index.asCfgNode() = subscript.getIndex()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ module Flask {
|
|||||||
result in [this.getArg(0), this.getArgByName("rule")]
|
result in [this.getArg(0), this.getArgByName("rule")]
|
||||||
}
|
}
|
||||||
|
|
||||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -536,7 +536,7 @@ module Flask {
|
|||||||
FlaskRouteHandlerReturn() {
|
FlaskRouteHandlerReturn() {
|
||||||
exists(Function routeHandler |
|
exists(Function routeHandler |
|
||||||
routeHandler = any(FlaskRouteSetup rs).getARequestHandler() and
|
routeHandler = any(FlaskRouteSetup rs).getARequestHandler() and
|
||||||
node = routeHandler.getAReturnValueFlowNode() and
|
exists(Return ret | ret.getScope() = routeHandler and node.getNode() = ret.getValue()) and
|
||||||
not this instanceof Flask::Response::InstanceSource
|
not this instanceof Flask::Response::InstanceSource
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ private module FlaskAdmin {
|
|||||||
result in [this.getArg(0), this.getArgByName("url")]
|
result in [this.getArg(0), this.getArgByName("url")]
|
||||||
}
|
}
|
||||||
|
|
||||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +71,7 @@ private module FlaskAdmin {
|
|||||||
|
|
||||||
override Function getARequestHandler() {
|
override Function getARequestHandler() {
|
||||||
exists(Flask::FlaskViewClass cls |
|
exists(Flask::FlaskViewClass cls |
|
||||||
cls.getADecorator().getAFlowNode() = node and
|
node.getNode() = cls.getADecorator() and
|
||||||
result = cls.getARequestHandler()
|
result = cls.getARequestHandler()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import python
|
import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import semmle.python.dataflow.new.RemoteFlowSources
|
import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
import semmle.python.dataflow.new.TaintTracking
|
import semmle.python.dataflow.new.TaintTracking
|
||||||
import semmle.python.ApiGraphs
|
import semmle.python.ApiGraphs
|
||||||
@@ -51,9 +52,9 @@ module Gradio {
|
|||||||
// limit only to lists of parameters given to `inputs`.
|
// limit only to lists of parameters given to `inputs`.
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode
|
call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode
|
||||||
or
|
or
|
||||||
call.getParameter(1).asSink().asCfgNode() instanceof ListNode
|
call.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
this = call.getKeywordParameter("inputs").getASubscript().getAValueReachingSink()
|
this = call.getKeywordParameter("inputs").getASubscript().getAValueReachingSink()
|
||||||
@@ -75,8 +76,8 @@ module Gradio {
|
|||||||
exists(GradioInput call |
|
exists(GradioInput call |
|
||||||
this = call.getParameter(0, "fn").getParameter(_).asSource() and
|
this = call.getParameter(0, "fn").getParameter(_).asSource() and
|
||||||
// exclude lists of parameters given to `inputs`
|
// exclude lists of parameters given to `inputs`
|
||||||
not call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode and
|
not call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode and
|
||||||
not call.getParameter(1).asSink().asCfgNode() instanceof ListNode
|
not call.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,16 +106,16 @@ module Gradio {
|
|||||||
// handle cases where there are multiple arguments passed as a list to `inputs`
|
// handle cases where there are multiple arguments passed as a list to `inputs`
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
node.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode
|
node.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode
|
||||||
or
|
or
|
||||||
node.getParameter(1).asSink().asCfgNode() instanceof ListNode
|
node.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
|
||||||
) and
|
) and
|
||||||
exists(int i | nodeTo = node.getParameter(0, "fn").getParameter(i).asSource() |
|
exists(int i | nodeTo = node.getParameter(0, "fn").getParameter(i).asSource() |
|
||||||
nodeFrom.asCfgNode() =
|
nodeFrom.asCfgNode() =
|
||||||
node.getKeywordParameter("inputs").asSink().asCfgNode().(ListNode).getElement(i)
|
node.getKeywordParameter("inputs").asSink().asCfgNode().(Cfg::ListNode).getElement(i)
|
||||||
or
|
or
|
||||||
nodeFrom.asCfgNode() =
|
nodeFrom.asCfgNode() =
|
||||||
node.getParameter(1).asSink().asCfgNode().(ListNode).getElement(i)
|
node.getParameter(1).asSink().asCfgNode().(Cfg::ListNode).getElement(i)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
@@ -46,7 +47,7 @@ module MarkupSafeModel {
|
|||||||
|
|
||||||
/** A direct instantiation of `markupsafe.Markup`. */
|
/** A direct instantiation of `markupsafe.Markup`. */
|
||||||
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
||||||
override CallNode node;
|
override Cfg::CallNode node;
|
||||||
|
|
||||||
ClassInstantiation() { this = classRef().getACall() }
|
ClassInstantiation() { this = classRef().getACall() }
|
||||||
}
|
}
|
||||||
@@ -64,7 +65,7 @@ module MarkupSafeModel {
|
|||||||
|
|
||||||
/** A string concatenation with a `markupsafe.Markup` involved. */
|
/** A string concatenation with a `markupsafe.Markup` involved. */
|
||||||
class StringConcat extends Markup::InstanceSource, DataFlow::CfgNode {
|
class StringConcat extends Markup::InstanceSource, DataFlow::CfgNode {
|
||||||
override BinaryExprNode node;
|
override Cfg::BinaryExprNode node;
|
||||||
|
|
||||||
StringConcat() {
|
StringConcat() {
|
||||||
node.getOp() instanceof Add and
|
node.getOp() instanceof Add and
|
||||||
@@ -79,7 +80,7 @@ module MarkupSafeModel {
|
|||||||
|
|
||||||
/** A %-style string format with `markupsafe.Markup` as the format string. */
|
/** A %-style string format with `markupsafe.Markup` as the format string. */
|
||||||
class PercentStringFormat extends Markup::InstanceSource, DataFlow::CfgNode {
|
class PercentStringFormat extends Markup::InstanceSource, DataFlow::CfgNode {
|
||||||
override BinaryExprNode node;
|
override Cfg::BinaryExprNode node;
|
||||||
|
|
||||||
PercentStringFormat() {
|
PercentStringFormat() {
|
||||||
node.getOp() instanceof Mod and
|
node.getOp() instanceof Mod and
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.frameworks.data.ModelsAsData
|
private import semmle.python.frameworks.data.ModelsAsData
|
||||||
@@ -56,7 +57,7 @@ module Pycurl {
|
|||||||
{
|
{
|
||||||
OutgoingRequestCall() {
|
OutgoingRequestCall() {
|
||||||
this = setopt().getACall() and
|
this = setopt().getACall() and
|
||||||
this.getArg(0).asCfgNode().(AttrNode).getName() = "URL"
|
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = "URL"
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlow::Node getAUrlPart() {
|
override DataFlow::Node getAUrlPart() {
|
||||||
@@ -81,7 +82,7 @@ module Pycurl {
|
|||||||
private class CurlSslCall extends Http::Client::Request::Range instanceof DataFlow::CallCfgNode {
|
private class CurlSslCall extends Http::Client::Request::Range instanceof DataFlow::CallCfgNode {
|
||||||
CurlSslCall() {
|
CurlSslCall() {
|
||||||
this = setopt().getACall() and
|
this = setopt().getACall() and
|
||||||
this.getArg(0).asCfgNode().(AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"]
|
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"]
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlow::Node getAUrlPart() { none() }
|
override DataFlow::Node getAUrlPart() { none() }
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
@@ -93,7 +94,7 @@ module Pydantic {
|
|||||||
// be a Pydantic model. So `model[0]` will be an overapproximation, but should not
|
// 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)
|
// really cause problems (since we don't expect real code to contain such accesses)
|
||||||
nodeFrom = instance() and
|
nodeFrom = instance() and
|
||||||
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
nodeTo.asCfgNode().(Cfg::SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -166,7 +166,10 @@ module Pyramid {
|
|||||||
/** A response returned by a view callable. */
|
/** A response returned by a view callable. */
|
||||||
private class PyramidReturnResponse extends Http::Server::HttpResponse::Range {
|
private class PyramidReturnResponse extends Http::Server::HttpResponse::Range {
|
||||||
PyramidReturnResponse() {
|
PyramidReturnResponse() {
|
||||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() and
|
exists(Return ret |
|
||||||
|
ret.getScope() = any(View::ViewCallable vc) and
|
||||||
|
this.asCfgNode().getNode() = ret.getValue()
|
||||||
|
) and
|
||||||
not this = instance()
|
not this = instance()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ overlay[local?]
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
@@ -1246,7 +1247,7 @@ module StdlibPrivate {
|
|||||||
/** An additional taint step for calls to `os.path.join` */
|
/** An additional taint step for calls to `os.path.join` */
|
||||||
private class OsPathJoinCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
private class OsPathJoinCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
exists(CallNode call |
|
exists(Cfg::CallNode call |
|
||||||
nodeTo.asCfgNode() = call and
|
nodeTo.asCfgNode() = call and
|
||||||
call = OS::OsPath::join().getACall().asCfgNode() and
|
call = OS::OsPath::join().getACall().asCfgNode() and
|
||||||
call.getAnArg() = nodeFrom.asCfgNode()
|
call.getAnArg() = nodeFrom.asCfgNode()
|
||||||
@@ -1317,13 +1318,13 @@ module StdlibPrivate {
|
|||||||
// run, so if we're able to, we only mark the first element as the command
|
// run, so if we're able to, we only mark the first element as the command
|
||||||
// (and not the arguments to the command).
|
// (and not the arguments to the command).
|
||||||
//
|
//
|
||||||
result.asCfgNode() = arg_args.asCfgNode().(SequenceNode).getElement(0)
|
result.asCfgNode() = arg_args.asCfgNode().(Cfg::SequenceNode).getElement(0)
|
||||||
or
|
or
|
||||||
// Either the "args" argument is not a sequence (which is valid) or we where
|
// 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
|
// just not able to figure it out. Simply mark the "args" argument as the
|
||||||
// command.
|
// command.
|
||||||
//
|
//
|
||||||
not arg_args.asCfgNode() instanceof SequenceNode and
|
not arg_args.asCfgNode() instanceof Cfg::SequenceNode and
|
||||||
result = arg_args
|
result = arg_args
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1542,7 +1543,7 @@ module StdlibPrivate {
|
|||||||
* See https://docs.python.org/3/library/functions.html#eval
|
* See https://docs.python.org/3/library/functions.html#eval
|
||||||
*/
|
*/
|
||||||
private class BuiltinsEvalCall extends CodeExecution::Range, DataFlow::CallCfgNode {
|
private class BuiltinsEvalCall extends CodeExecution::Range, DataFlow::CallCfgNode {
|
||||||
override CallNode node;
|
override Cfg::CallNode node;
|
||||||
|
|
||||||
BuiltinsEvalCall() { this = API::builtin("eval").getACall() }
|
BuiltinsEvalCall() { this = API::builtin("eval").getACall() }
|
||||||
|
|
||||||
@@ -1923,7 +1924,7 @@ module StdlibPrivate {
|
|||||||
nodeFrom = instance().getAValueReachableFromSource() and
|
nodeFrom = instance().getAValueReachableFromSource() and
|
||||||
nodeTo = [getvalueRef(), getfirstRef(), getlistRef()].getAValueReachableFromSource()
|
nodeTo = [getvalueRef(), getfirstRef(), getlistRef()].getAValueReachableFromSource()
|
||||||
or
|
or
|
||||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(CallNode).getFunction() and
|
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::CallNode).getFunction() and
|
||||||
(
|
(
|
||||||
nodeFrom = getvalueRef().getAValueReachableFromSource() and
|
nodeFrom = getvalueRef().getAValueReachableFromSource() and
|
||||||
nodeTo = getvalueResult().asSource()
|
nodeTo = getvalueResult().asSource()
|
||||||
@@ -1939,7 +1940,7 @@ module StdlibPrivate {
|
|||||||
nodeFrom in [
|
nodeFrom in [
|
||||||
instance().getAValueReachableFromSource(), fieldList().getAValueReachableFromSource()
|
instance().getAValueReachableFromSource(), fieldList().getAValueReachableFromSource()
|
||||||
] and
|
] and
|
||||||
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
nodeTo.asCfgNode().(Cfg::SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||||
or
|
or
|
||||||
// Attributes on Field
|
// Attributes on Field
|
||||||
nodeFrom = field().getAValueReachableFromSource() and
|
nodeFrom = field().getAValueReachableFromSource() and
|
||||||
@@ -2254,8 +2255,9 @@ module StdlibPrivate {
|
|||||||
DataFlow::CfgNode
|
DataFlow::CfgNode
|
||||||
{
|
{
|
||||||
WsgirefSimpleServerApplicationReturn() {
|
WsgirefSimpleServerApplicationReturn() {
|
||||||
exists(WsgirefSimpleServerApplication requestHandler |
|
exists(Return ret |
|
||||||
node = requestHandler.getAReturnValueFlowNode()
|
ret.getScope() = any(WsgirefSimpleServerApplication requestHandler) and
|
||||||
|
node.getNode() = ret.getValue()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2337,9 +2339,9 @@ module StdlibPrivate {
|
|||||||
DataFlow::Node value;
|
DataFlow::Node value;
|
||||||
|
|
||||||
HeaderWriteSubscript() {
|
HeaderWriteSubscript() {
|
||||||
exists(SubscriptNode subscript |
|
exists(Cfg::SubscriptNode subscript |
|
||||||
this.asCfgNode() = subscript and
|
this.asCfgNode() = subscript and
|
||||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
name.asCfgNode() = subscript.getIndex() and
|
name.asCfgNode() = subscript.getIndex() and
|
||||||
subscript.getObject() = instance().asCfgNode()
|
subscript.getObject() = instance().asCfgNode()
|
||||||
)
|
)
|
||||||
@@ -2681,7 +2683,7 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
// Data injection
|
// Data injection
|
||||||
// Special handling of the `/` operator
|
// Special handling of the `/` operator
|
||||||
exists(BinaryExprNode slash, DataFlow::Node pathOperand, DataFlow::TypeTracker t2 |
|
exists(Cfg::BinaryExprNode slash, DataFlow::Node pathOperand, DataFlow::TypeTracker t2 |
|
||||||
slash.getOp() instanceof Div and
|
slash.getOp() instanceof Div and
|
||||||
pathOperand.asCfgNode() = slash.getAnOperand() and
|
pathOperand.asCfgNode() = slash.getAnOperand() and
|
||||||
pathlibPath(t2).flowsTo(pathOperand) and
|
pathlibPath(t2).flowsTo(pathOperand) and
|
||||||
@@ -2806,7 +2808,7 @@ module StdlibPrivate {
|
|||||||
pathlibPath().flowsTo(nodeTo) and
|
pathlibPath().flowsTo(nodeTo) and
|
||||||
(
|
(
|
||||||
// Special handling of the `/` operator
|
// Special handling of the `/` operator
|
||||||
exists(BinaryExprNode slash, DataFlow::Node pathOperand |
|
exists(Cfg::BinaryExprNode slash, DataFlow::Node pathOperand |
|
||||||
slash.getOp() instanceof Div and
|
slash.getOp() instanceof Div and
|
||||||
pathOperand.asCfgNode() = slash.getAnOperand() and
|
pathOperand.asCfgNode() = slash.getAnOperand() and
|
||||||
pathlibPath().flowsTo(pathOperand)
|
pathlibPath().flowsTo(pathOperand)
|
||||||
@@ -4605,9 +4607,9 @@ module StdlibPrivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(CallNode c, string name, ControlFlowNode n, DataFlow::AttributeContent ac |
|
exists(Cfg::CallNode c, string name, Cfg::ControlFlowNode n, DataFlow::AttributeContent ac |
|
||||||
c.getFunction().(NameNode).getId() = "replace" or
|
c.getFunction().(Cfg::NameNode).getId() = "replace" or
|
||||||
c.getFunction().(AttrNode).getName() = "replace"
|
c.getFunction().(Cfg::AttrNode).getName() = "replace"
|
||||||
|
|
|
|
||||||
n = c.getArgByName(name) and
|
n = c.getArgByName(name) and
|
||||||
ac.getAttribute() = name and
|
ac.getAttribute() = name and
|
||||||
@@ -5151,10 +5153,10 @@ module StdlibPrivate {
|
|||||||
* See https://docs.python.org/3.9/library/stdtypes.html#str.startswith
|
* See https://docs.python.org/3.9/library/stdtypes.html#str.startswith
|
||||||
*/
|
*/
|
||||||
private class StartswithCall extends Path::SafeAccessCheck::Range {
|
private class StartswithCall extends Path::SafeAccessCheck::Range {
|
||||||
StartswithCall() { this.(CallNode).getFunction().(AttrNode).getName() = "startswith" }
|
StartswithCall() { this.(Cfg::CallNode).getFunction().(Cfg::AttrNode).getName() = "startswith" }
|
||||||
|
|
||||||
override predicate checks(ControlFlowNode node, boolean branch) {
|
override predicate checks(Cfg::ControlFlowNode node, boolean branch) {
|
||||||
node = this.(CallNode).getFunction().(AttrNode).getObject() and
|
node = this.(Cfg::CallNode).getFunction().(Cfg::AttrNode).getObject() and
|
||||||
branch = true
|
branch = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.security.dataflow.UrlRedirectCustomizations
|
private import semmle.python.security.dataflow.UrlRedirectCustomizations
|
||||||
@@ -91,7 +92,7 @@ private module Urllib {
|
|||||||
* A read of the `netloc` attribute of a parsed URL as returned by `urllib.parse.urlparse`,
|
* 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.
|
* which is being checked in a way that is relevant for URL redirection vulnerabilities.
|
||||||
*/
|
*/
|
||||||
private predicate netlocCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
private predicate netlocCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
|
||||||
exists(DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead netlocRead |
|
exists(DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead netlocRead |
|
||||||
urlParseCall = getUrlParseCall() and
|
urlParseCall = getUrlParseCall() and
|
||||||
netlocRead = urlParseCall.getAnAttributeRead("netloc") and
|
netlocRead = urlParseCall.getAnAttributeRead("netloc") and
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
@@ -72,9 +73,9 @@ module Tornado {
|
|||||||
DataFlow::Node value;
|
DataFlow::Node value;
|
||||||
|
|
||||||
TornadoHeaderSubscriptWrite() {
|
TornadoHeaderSubscriptWrite() {
|
||||||
exists(SubscriptNode subscript |
|
exists(Cfg::SubscriptNode subscript |
|
||||||
subscript.getObject() = instance().asCfgNode() and
|
subscript.getObject() = instance().asCfgNode() and
|
||||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
index.asCfgNode() = subscript.getIndex() and
|
index.asCfgNode() = subscript.getIndex() and
|
||||||
this.asCfgNode() = subscript
|
this.asCfgNode() = subscript
|
||||||
)
|
)
|
||||||
@@ -422,7 +423,7 @@ module Tornado {
|
|||||||
// be able to do something more structured for providing modeling of the members
|
// be able to do something more structured for providing modeling of the members
|
||||||
// of a container-object.
|
// of a container-object.
|
||||||
exists(DataFlow::AttrRead files | files.accesses(instance(), "cookies") |
|
exists(DataFlow::AttrRead files | files.accesses(instance(), "cookies") |
|
||||||
this.asCfgNode().(SubscriptNode).getObject() = files.asCfgNode()
|
this.asCfgNode().(Cfg::SubscriptNode).getObject() = files.asCfgNode()
|
||||||
or
|
or
|
||||||
this.(DataFlow::MethodCallNode).calls(files, "get")
|
this.(DataFlow::MethodCallNode).calls(files, "get")
|
||||||
)
|
)
|
||||||
@@ -479,20 +480,20 @@ module Tornado {
|
|||||||
// routing
|
// routing
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Gets a sequence that defines a number of route rules */
|
/** Gets a sequence that defines a number of route rules */
|
||||||
SequenceNode routeSetupRuleList() {
|
Cfg::SequenceNode routeSetupRuleList() {
|
||||||
exists(CallNode call |
|
exists(Cfg::CallNode call |
|
||||||
call = any(TornadoModule::Web::Application::ClassInstantiation c).asCfgNode()
|
call = any(TornadoModule::Web::Application::ClassInstantiation c).asCfgNode()
|
||||||
|
|
|
|
||||||
result in [call.getArg(0), call.getArgByName("handlers")]
|
result in [call.getArg(0), call.getArgByName("handlers")]
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(CallNode call |
|
exists(Cfg::CallNode call |
|
||||||
call.getFunction() = TornadoModule::Web::Application::add_handlers().asCfgNode()
|
call.getFunction() = TornadoModule::Web::Application::add_handlers().asCfgNode()
|
||||||
|
|
|
|
||||||
result in [call.getArg(1), call.getArgByName("host_handlers")]
|
result in [call.getArg(1), call.getArgByName("host_handlers")]
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
result = routeSetupRuleList().getElement(_).(TupleNode).getElement(1)
|
result = routeSetupRuleList().getElement(_).(Cfg::TupleNode).getElement(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A tornado route setup. */
|
/** A tornado route setup. */
|
||||||
@@ -515,12 +516,12 @@ module Tornado {
|
|||||||
|
|
||||||
/** A route setup using a tuple. */
|
/** A route setup using a tuple. */
|
||||||
private class TornadoTupleRouteSetup extends TornadoRouteSetup, DataFlow::CfgNode {
|
private class TornadoTupleRouteSetup extends TornadoRouteSetup, DataFlow::CfgNode {
|
||||||
override TupleNode node;
|
override Cfg::TupleNode node;
|
||||||
|
|
||||||
TornadoTupleRouteSetup() {
|
TornadoTupleRouteSetup() {
|
||||||
node = routeSetupRuleList().getElement(_) and
|
node = routeSetupRuleList().getElement(_) and
|
||||||
count(node.getElement(_)) = 2 and
|
count(node.getElement(_)) = 2 and
|
||||||
not node.getElement(1) instanceof SequenceNode
|
not node.getElement(1) instanceof Cfg::SequenceNode
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlow::Node getUrlPatternArg() { result.asCfgNode() = node.getElement(0) }
|
override DataFlow::Node getUrlPatternArg() { result.asCfgNode() = node.getElement(0) }
|
||||||
|
|||||||
@@ -182,7 +182,10 @@ private module Twisted {
|
|||||||
DataFlow::CfgNode
|
DataFlow::CfgNode
|
||||||
{
|
{
|
||||||
TwistedResourceRenderMethodReturn() {
|
TwistedResourceRenderMethodReturn() {
|
||||||
this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode()
|
exists(Return ret |
|
||||||
|
ret.getScope() = any(TwistedResourceRenderMethod meth) and
|
||||||
|
this.asCfgNode().getNode() = ret.getValue()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlow::Node getBody() { result = this }
|
override DataFlow::Node getBody() { result = this }
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
@@ -221,9 +222,9 @@ module Werkzeug {
|
|||||||
DataFlow::Node value;
|
DataFlow::Node value;
|
||||||
|
|
||||||
HeaderWriteSubscript() {
|
HeaderWriteSubscript() {
|
||||||
exists(SubscriptNode subscript |
|
exists(Cfg::SubscriptNode subscript |
|
||||||
this.asCfgNode() = subscript and
|
this.asCfgNode() = subscript and
|
||||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
|
||||||
name.asCfgNode() = subscript.getIndex() and
|
name.asCfgNode() = subscript.getIndex() and
|
||||||
subscript.getObject() = instance().asCfgNode()
|
subscript.getObject() = instance().asCfgNode()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
@@ -28,7 +29,7 @@ private module Yaml {
|
|||||||
* See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
|
* See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
|
||||||
*/
|
*/
|
||||||
private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
|
private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
|
||||||
override CallNode node;
|
override Cfg::CallNode node;
|
||||||
string func_name;
|
string func_name;
|
||||||
|
|
||||||
YamlLoadCall() {
|
YamlLoadCall() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.dataflow.new.TaintTracking
|
private import semmle.python.dataflow.new.TaintTracking
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
@@ -111,7 +112,7 @@ module Yarl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private predicate yarlUrlIsAbsoluteCall(
|
private predicate yarlUrlIsAbsoluteCall(
|
||||||
DataFlow::GuardNode g, ControlFlowNode node, boolean branch
|
DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch
|
||||||
) {
|
) {
|
||||||
exists(ClassInstantiation instance, DataFlow::MethodCallNode call |
|
exists(ClassInstantiation instance, DataFlow::MethodCallNode call |
|
||||||
call.calls(instance, "is_absolute") and
|
call.calls(instance, "is_absolute") and
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ private import semmle.python.dataflow.new.internal.ImportResolution
|
|||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.filters.Tests
|
private import semmle.python.filters.Tests
|
||||||
private import semmle.python.Module
|
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
|
// very much inspired by the draft at https://github.com/github/codeql/pull/5632
|
||||||
module NotExposed {
|
module NotExposed {
|
||||||
@@ -206,7 +207,7 @@ module NotExposed {
|
|||||||
string relevantName, Location loc
|
string relevantName, Location loc
|
||||||
) {
|
) {
|
||||||
loc = mod.getLocation() and
|
loc = mod.getLocation() and
|
||||||
exists(API::Node relevantClass, ControlFlowNode value |
|
exists(API::Node relevantClass, Cfg::ControlFlowNode value |
|
||||||
relevantClass = newOrExistingModeling(spec).getASubclass*() and
|
relevantClass = newOrExistingModeling(spec).getASubclass*() and
|
||||||
ImportResolution::module_export(mod, relevantName, def) and
|
ImportResolution::module_export(mod, relevantName, def) and
|
||||||
value = relevantClass.getAValueReachableFromSource().asCfgNode() and
|
value = relevantClass.getAValueReachableFromSource().asCfgNode() and
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ module Stages {
|
|||||||
or
|
or
|
||||||
exists(any(AstExtended::AstNode n).getParentNode())
|
exists(any(AstExtended::AstNode n).getParentNode())
|
||||||
or
|
or
|
||||||
exists(any(AstExtended::AstNode n).getAFlowNode())
|
exists(PyFlow::ControlFlowNode cfg, AstExtended::AstNode n | cfg.getNode() = n)
|
||||||
or
|
or
|
||||||
exists(any(PyFlow::BasicBlock b).getImmediateDominator())
|
exists(any(PyFlow::BasicBlock b).getImmediateDominator())
|
||||||
or
|
or
|
||||||
|
|||||||
@@ -56,8 +56,9 @@ abstract class CallableObjectInternal extends ObjectInternal {
|
|||||||
/** A Python function. */
|
/** A Python function. */
|
||||||
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
|
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
|
||||||
override Function getScope() {
|
override Function getScope() {
|
||||||
exists(CallableExpr expr |
|
exists(CallableExpr expr, ControlFlowNode exprCfg |
|
||||||
this = TPythonFunctionObject(expr.getAFlowNode()) and
|
exprCfg.getNode() = expr and
|
||||||
|
this = TPythonFunctionObject(exprCfg) and
|
||||||
result = expr.getInnerScope()
|
result = expr.getInnerScope()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -80,11 +81,12 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
||||||
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
|
exists(Function func, Return ret, ControlFlowNode rval, ControlFlowNode forigin |
|
||||||
func = this.getScope() and
|
func = this.getScope() and
|
||||||
callee.appliesToScope(func)
|
callee.appliesToScope(func)
|
||||||
|
|
|
|
||||||
rval = func.getAReturnValueFlowNode() and
|
ret.getScope() = func and
|
||||||
|
rval.getNode() = ret.getValue() and
|
||||||
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
|
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
|
||||||
origin = CfgOrigin::fromCfgNode(forigin)
|
origin = CfgOrigin::fromCfgNode(forigin)
|
||||||
)
|
)
|
||||||
@@ -160,10 +162,11 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BasicBlock blockReturningNone(Function func) {
|
private BasicBlock blockReturningNone(Function func) {
|
||||||
exists(Return ret |
|
exists(Return ret, ControlFlowNode ret_ |
|
||||||
not exists(ret.getValue()) and
|
not exists(ret.getValue()) and
|
||||||
ret.getScope() = func and
|
ret.getScope() = func and
|
||||||
result = ret.getAFlowNode().getBasicBlock()
|
ret_.getNode() = ret and
|
||||||
|
result = ret_.getBasicBlock()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,8 +113,9 @@ abstract class ClassObjectInternal extends ObjectInternal {
|
|||||||
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
|
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
|
||||||
/** Gets the scope for this Python class */
|
/** Gets the scope for this Python class */
|
||||||
Class getScope() {
|
Class getScope() {
|
||||||
exists(ClassExpr expr |
|
exists(ClassExpr expr, ControlFlowNode exprCfg |
|
||||||
this = TPythonClassObject(expr.getAFlowNode()) and
|
exprCfg.getNode() = expr and
|
||||||
|
this = TPythonClassObject(exprCfg) and
|
||||||
result = expr.getInnerScope()
|
result = expr.getInnerScope()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -745,7 +745,12 @@ class PythonFunctionValue extends FunctionValue {
|
|||||||
override int maxParameters() { result = this.getScope().getMaxPositionalArguments() }
|
override int maxParameters() { result = this.getScope().getMaxPositionalArguments() }
|
||||||
|
|
||||||
/** Gets a control flow node corresponding to a return statement in this function */
|
/** Gets a control flow node corresponding to a return statement in this function */
|
||||||
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
|
ControlFlowNode getAReturnedNode() {
|
||||||
|
exists(Return ret |
|
||||||
|
ret.getScope() = this.getScope() and
|
||||||
|
result.getNode() = ret.getValue()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
|
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
|
||||||
|
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ private PythonClassObjectInternal abcMetaClassObject() {
|
|||||||
private predicate neither_class_nor_static_method(Function f) {
|
private predicate neither_class_nor_static_method(Function f) {
|
||||||
not exists(f.getADecorator())
|
not exists(f.getADecorator())
|
||||||
or
|
or
|
||||||
exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() |
|
exists(ControlFlowNode deco | deco.getNode() = f.getADecorator() |
|
||||||
exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) |
|
exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) |
|
||||||
o != ObjectInternal::staticMethod() and
|
o != ObjectInternal::staticMethod() and
|
||||||
o != ObjectInternal::classMethod()
|
o != ObjectInternal::classMethod()
|
||||||
|
|||||||
@@ -711,7 +711,7 @@ private module InterModulePointsTo {
|
|||||||
ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin
|
ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin
|
||||||
) {
|
) {
|
||||||
exists(string name, ImportExpr i |
|
exists(string name, ImportExpr i |
|
||||||
i.getAFlowNode() = f and
|
f.getNode() = i and
|
||||||
i.getImportedModuleName() = name and
|
i.getImportedModuleName() = name and
|
||||||
PointsToInternal::module_imported_as(value, name) and
|
PointsToInternal::module_imported_as(value, name) and
|
||||||
origin = f and
|
origin = f and
|
||||||
@@ -2118,8 +2118,9 @@ module Types {
|
|||||||
result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0
|
result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0
|
||||||
or
|
or
|
||||||
exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() |
|
exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() |
|
||||||
exists(ObjectInternal base |
|
exists(ObjectInternal base, ControlFlowNode baseNode |
|
||||||
PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _)
|
baseNode.getNode() = pycls.getBase(n) and
|
||||||
|
PointsToInternal::pointsTo(baseNode, _, base, _)
|
||||||
|
|
|
|
||||||
result = base and base != ObjectInternal::unknown()
|
result = base and base != ObjectInternal::unknown()
|
||||||
or
|
or
|
||||||
@@ -2223,7 +2224,10 @@ module Types {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) {
|
private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) {
|
||||||
result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction()
|
exists(CallNode deco |
|
||||||
|
deco.getNode() = cls.getScope().getADecorator() and
|
||||||
|
result = deco.getFunction()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean has_six_add_metaclass(PythonClassObjectInternal cls) {
|
private boolean has_six_add_metaclass(PythonClassObjectInternal cls) {
|
||||||
@@ -2262,7 +2266,7 @@ module Types {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private EssaVariable metaclass_var(Class cls) {
|
private EssaVariable metaclass_var(Class cls) {
|
||||||
result.getASourceUse() = cls.getMetaClass().getAFlowNode()
|
result.getASourceUse().getNode() = cls.getMetaClass()
|
||||||
or
|
or
|
||||||
major_version() = 2 and
|
major_version() = 2 and
|
||||||
not exists(cls.getMetaClass()) and
|
not exists(cls.getMetaClass()) and
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import python
|
import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
private import semmle.python.dataflow.new.DataFlow
|
private import semmle.python.dataflow.new.DataFlow
|
||||||
private import semmle.python.Concepts as Concepts
|
private import semmle.python.Concepts as Concepts
|
||||||
private import semmle.python.regex
|
private import semmle.python.regex
|
||||||
@@ -78,7 +79,7 @@ private module FindRegexMode {
|
|||||||
t.start() and
|
t.start() and
|
||||||
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
|
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
|
||||||
or
|
or
|
||||||
exists(BinaryExprNode binop, DataFlow::Node operand |
|
exists(Cfg::BinaryExprNode binop, DataFlow::Node operand |
|
||||||
operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
|
operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
|
||||||
operand.asCfgNode() = binop.getAnOperand() and
|
operand.asCfgNode() = binop.getAnOperand() and
|
||||||
(binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and
|
(binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import python
|
import python
|
||||||
import semmle.python.dataflow.new.DataFlow
|
import semmle.python.dataflow.new.DataFlow
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL: Do not use.
|
* INTERNAL: Do not use.
|
||||||
@@ -29,7 +30,7 @@ private class TracebackFunctionCall extends ExceptionInfo, DataFlow::CallCfgNode
|
|||||||
private class CaughtException extends ExceptionInfo {
|
private class CaughtException extends ExceptionInfo {
|
||||||
CaughtException() {
|
CaughtException() {
|
||||||
this.asExpr() = any(ExceptStmt s).getName() and
|
this.asExpr() = any(ExceptStmt s).getName() and
|
||||||
this.asCfgNode() = any(EssaNodeDefinition def).getDefiningNode()
|
this.asCfgNode().(Cfg::NameNode).defines(_)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
|
|||||||
private import semmle.python.dataflow.new.BarrierGuards
|
private import semmle.python.dataflow.new.BarrierGuards
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
private import semmle.python.frameworks.data.internal.ApiGraphModels
|
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
|
* Provides default sources, sinks and sanitizers for detecting
|
||||||
@@ -95,7 +96,7 @@ module ServerSideRequestForgery {
|
|||||||
class StringConstructionAsFullUrlControlSanitizer extends FullUrlControlSanitizer {
|
class StringConstructionAsFullUrlControlSanitizer extends FullUrlControlSanitizer {
|
||||||
StringConstructionAsFullUrlControlSanitizer() {
|
StringConstructionAsFullUrlControlSanitizer() {
|
||||||
// string concat
|
// string concat
|
||||||
exists(BinaryExprNode add |
|
exists(Cfg::BinaryExprNode add |
|
||||||
add.getOp() instanceof Add and
|
add.getOp() instanceof Add and
|
||||||
add.getRight() = this.asCfgNode() and
|
add.getRight() = this.asCfgNode() and
|
||||||
not add.getLeft().getNode().(StringLiteral).getText().toLowerCase() in [
|
not add.getLeft().getNode().(StringLiteral).getText().toLowerCase() in [
|
||||||
@@ -104,7 +105,7 @@ module ServerSideRequestForgery {
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
// % formatting
|
// % formatting
|
||||||
exists(BinaryExprNode fmt |
|
exists(Cfg::BinaryExprNode fmt |
|
||||||
fmt.getOp() instanceof Mod and
|
fmt.getOp() instanceof Mod and
|
||||||
fmt.getRight() = this.asCfgNode() and
|
fmt.getRight() = this.asCfgNode() and
|
||||||
// detecting %-formatting is not super easy, so we simplify it to only handle
|
// detecting %-formatting is not super easy, so we simplify it to only handle
|
||||||
@@ -155,7 +156,9 @@ module ServerSideRequestForgery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate stringRestriction(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
private predicate stringRestriction(
|
||||||
|
DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch
|
||||||
|
) {
|
||||||
exists(DataFlow::MethodCallNode call, DataFlow::Node strNode |
|
exists(DataFlow::MethodCallNode call, DataFlow::Node strNode |
|
||||||
call.asCfgNode() = g and strNode.asCfgNode() = node
|
call.asCfgNode() = g and strNode.asCfgNode() = node
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
|
|||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.dataflow.new.BarrierGuards
|
private import semmle.python.dataflow.new.BarrierGuards
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides default sources, sinks and sanitizers for detecting
|
* Provides default sources, sinks and sanitizers for detecting
|
||||||
@@ -139,8 +140,8 @@ module TarSlip {
|
|||||||
* where `<check_path>` is any function matching `"%path"`.
|
* where `<check_path>` is any function matching `"%path"`.
|
||||||
* `info` is assumed to be a `TarInfo` instance.
|
* `info` is assumed to be a `TarInfo` instance.
|
||||||
*/
|
*/
|
||||||
predicate tarFileInfoSanitizer(DataFlow::GuardNode g, ControlFlowNode tarInfo, boolean branch) {
|
predicate tarFileInfoSanitizer(DataFlow::GuardNode g, Cfg::ControlFlowNode tarInfo, boolean branch) {
|
||||||
exists(CallNode call, AttrNode attr |
|
exists(Cfg::CallNode call, Cfg::AttrNode attr |
|
||||||
g = call and
|
g = call and
|
||||||
// We must test the name of the tar info object.
|
// We must test the name of the tar info object.
|
||||||
attr = call.getAnArg() and
|
attr = call.getAnArg() and
|
||||||
@@ -148,9 +149,9 @@ module TarSlip {
|
|||||||
attr.getObject() = tarInfo
|
attr.getObject() = tarInfo
|
||||||
|
|
|
|
||||||
// The assumption that any test that matches %path is a sanitizer might be too broad.
|
// The assumption that any test that matches %path is a sanitizer might be too broad.
|
||||||
call.getAChild*().(AttrNode).getName().matches("%path")
|
call.getAChild*().(Cfg::AttrNode).getName().matches("%path")
|
||||||
or
|
or
|
||||||
call.getAChild*().(NameNode).getId().matches("%path")
|
call.getAChild*().(Cfg::NameNode).getId().matches("%path")
|
||||||
) and
|
) and
|
||||||
branch = false
|
branch = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
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.DataFlow
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
@@ -111,7 +112,7 @@ module UrlRedirect {
|
|||||||
// Url redirection is a problem only if the user controls the prefix of the URL.
|
// 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
|
// TODO: This is a copy of the taint-sanitizer from the old points-to query, which doesn't
|
||||||
// cover formatting.
|
// cover formatting.
|
||||||
exists(BinaryExprNode string_concat | string_concat.getOp() instanceof Add |
|
exists(Cfg::BinaryExprNode string_concat | string_concat.getOp() instanceof Add |
|
||||||
string_concat.getRight() = this.asCfgNode()
|
string_concat.getRight() = this.asCfgNode()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ class ClassObject extends Object {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() }
|
ControlFlowNode declaredMetaClass() { result.getNode() = this.getPyClass().getMetaClass() }
|
||||||
|
|
||||||
/** Has type inference failed to compute the full class hierarchy for this class for the reason given. */
|
/** 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) }
|
predicate failedInference(string reason) { Types::failedInference(this.theClass(), reason) }
|
||||||
@@ -195,8 +195,9 @@ class ClassObject extends Object {
|
|||||||
* It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject.
|
* It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject.
|
||||||
*/
|
*/
|
||||||
Object getProbableSingletonInstance() {
|
Object getProbableSingletonInstance() {
|
||||||
exists(ControlFlowNodeWithPointsTo use, Expr origin |
|
exists(ControlFlowNodeWithPointsTo use, Expr origin, ControlFlowNode origin_ |
|
||||||
use.refersTo(result, this, origin.getAFlowNode())
|
origin_.getNode() = origin and
|
||||||
|
use.refersTo(result, this, origin_)
|
||||||
|
|
|
|
||||||
this.hasStaticallyUniqueInstance() and
|
this.hasStaticallyUniqueInstance() and
|
||||||
/* Ensure that original expression will be executed only one. */
|
/* Ensure that original expression will be executed only one. */
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ class ExceptFlowNodeWithPointsTo extends ExceptFlowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) {
|
private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) {
|
||||||
exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode())
|
exists(Tuple t | t = tuple.getOrigin() and result.getNode() = t.getAnElt())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ class RangeIterationVariableFact extends PointsToExtension {
|
|||||||
RangeIterationVariableFact() {
|
RangeIterationVariableFact() {
|
||||||
exists(For f, ControlFlowNode iterable |
|
exists(For f, ControlFlowNode iterable |
|
||||||
iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and
|
iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and
|
||||||
f.getIter().getAFlowNode() = iterable and
|
iterable.getNode() = f.getIter() and
|
||||||
f.getTarget().getAFlowNode() = this and
|
this.(ControlFlowNode).getNode() = f.getTarget() and
|
||||||
exists(ObjectInternal range |
|
exists(ObjectInternal range |
|
||||||
PointsTo::pointsTo(iterable, _, range, _) and
|
PointsTo::pointsTo(iterable, _, range, _) and
|
||||||
range.getClass() = ObjectInternal::builtin("range")
|
range.getClass() = ObjectInternal::builtin("range")
|
||||||
|
|||||||
@@ -137,7 +137,10 @@ class PyFunctionObject extends FunctionObject {
|
|||||||
|
|
||||||
/** Gets a control flow node corresponding to the value of a return statement */
|
/** Gets a control flow node corresponding to the value of a return statement */
|
||||||
ControlFlowNodeWithPointsTo getAReturnedNode() {
|
ControlFlowNodeWithPointsTo getAReturnedNode() {
|
||||||
result = this.getFunction().getAReturnValueFlowNode()
|
exists(Return ret |
|
||||||
|
ret.getScope() = this.getFunction() and
|
||||||
|
result.getNode() = ret.getValue()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override string descriptiveString() {
|
override string descriptiveString() {
|
||||||
@@ -170,7 +173,7 @@ class PyFunctionObject extends FunctionObject {
|
|||||||
predicate unconditionallyReturnsParameter(int n) {
|
predicate unconditionallyReturnsParameter(int n) {
|
||||||
exists(SsaVariable pvar |
|
exists(SsaVariable pvar |
|
||||||
exists(Parameter p | p = this.getFunction().getArg(n) |
|
exists(Parameter p | p = this.getFunction().getArg(n) |
|
||||||
p.asName().getAFlowNode() = pvar.getDefinition()
|
pvar.getDefinition().getNode() = p.asName()
|
||||||
) and
|
) and
|
||||||
exists(NameNode rval |
|
exists(NameNode rval |
|
||||||
rval = pvar.getAUse() and
|
rval = pvar.getAUse() and
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ class TupleObject extends SequenceObject {
|
|||||||
or
|
or
|
||||||
this instanceof TupleNode
|
this instanceof TupleNode
|
||||||
or
|
or
|
||||||
exists(Function func | func.getVararg().getAFlowNode() = this)
|
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +352,9 @@ module TupleObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NonEmptyTupleObject extends TupleObject {
|
class NonEmptyTupleObject extends TupleObject {
|
||||||
NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) }
|
NonEmptyTupleObject() {
|
||||||
|
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
|
||||||
|
}
|
||||||
|
|
||||||
override boolean booleanValue() { result = true }
|
override boolean booleanValue() { result = true }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import python
|
import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import semmle.python.dataflow.new.DataFlow
|
import semmle.python.dataflow.new.DataFlow
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||||
import FlowTest
|
import FlowTest
|
||||||
@@ -23,7 +24,7 @@ import MakeTest<MakeTestSig<MaximalFlowTest>>
|
|||||||
module MaximalFlowsConfig implements DataFlow::ConfigSig {
|
module MaximalFlowsConfig implements DataFlow::ConfigSig {
|
||||||
predicate isSource(DataFlow::Node node) {
|
predicate isSource(DataFlow::Node node) {
|
||||||
exists(node.getLocation().getFile().getRelativePath()) and
|
exists(node.getLocation().getFile().getRelativePath()) and
|
||||||
not node.asCfgNode() instanceof CallNode and
|
not node.asCfgNode() instanceof Cfg::CallNode and
|
||||||
not node.asCfgNode().getNode() instanceof Return and
|
not node.asCfgNode().getNode() instanceof Return and
|
||||||
not node instanceof DataFlow::ParameterNode and
|
not node instanceof DataFlow::ParameterNode and
|
||||||
not node instanceof DataFlow::PostUpdateNode and
|
not node instanceof DataFlow::PostUpdateNode and
|
||||||
@@ -34,9 +35,9 @@ module MaximalFlowsConfig implements DataFlow::ConfigSig {
|
|||||||
|
|
||||||
predicate isSink(DataFlow::Node node) {
|
predicate isSink(DataFlow::Node node) {
|
||||||
exists(node.getLocation().getFile().getRelativePath()) and
|
exists(node.getLocation().getFile().getRelativePath()) and
|
||||||
not any(CallNode c).getArg(_) = node.asCfgNode() and
|
not any(Cfg::CallNode c).getArg(_) = node.asCfgNode() and
|
||||||
not isArgumentNode(node, _, _) and
|
not isArgumentNode(node, _, _) and
|
||||||
not node.asCfgNode().(NameNode).getId().matches("SINK%") and
|
not node.asCfgNode().(Cfg::NameNode).getId().matches("SINK%") and
|
||||||
not DataFlow::localFlowStep(node, _)
|
not DataFlow::localFlowStep(node, _)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import python
|
import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import utils.test.dataflow.FlowTest
|
import utils.test.dataflow.FlowTest
|
||||||
import utils.test.dataflow.testConfig
|
import utils.test.dataflow.testConfig
|
||||||
private import semmle.python.dataflow.new.internal.PrintNode
|
private import semmle.python.dataflow.new.internal.PrintNode
|
||||||
@@ -19,7 +20,7 @@ query predicate missingAnnotationOnSink(Location location, string error, string
|
|||||||
TestConfig::isSink(sink) and
|
TestConfig::isSink(sink) and
|
||||||
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
|
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
|
||||||
exists(DataFlow::CallCfgNode call |
|
exists(DataFlow::CallCfgNode call |
|
||||||
call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
|
call.getFunction().asCfgNode().(Cfg::NameNode).getId() = "SINK" and
|
||||||
(sink = call.getArg(_) or sink = call.getArgByName(_))
|
(sink = call.getArg(_) or sink = call.getArgByName(_))
|
||||||
) and
|
) and
|
||||||
location = sink.getLocation() and
|
location = sink.getLocation() and
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import python
|
import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import utils.test.dataflow.FlowTest
|
import utils.test.dataflow.FlowTest
|
||||||
import utils.test.dataflow.testTaintConfig
|
import utils.test.dataflow.testTaintConfig
|
||||||
private import semmle.python.dataflow.new.internal.PrintNode
|
private import semmle.python.dataflow.new.internal.PrintNode
|
||||||
@@ -18,7 +19,7 @@ query predicate missingAnnotationOnSink(Location location, string error, string
|
|||||||
exists(DataFlow::Node sink |
|
exists(DataFlow::Node sink |
|
||||||
exists(DataFlow::CallCfgNode call |
|
exists(DataFlow::CallCfgNode call |
|
||||||
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
|
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
|
||||||
call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
|
call.getFunction().asCfgNode().(Cfg::NameNode).getId() = "SINK" and
|
||||||
(sink = call.getArg(_) or sink = call.getArgByName(_))
|
(sink = call.getArg(_) or sink = call.getArgByName(_))
|
||||||
) and
|
) and
|
||||||
location = sink.getLocation() and
|
location = sink.getLocation() and
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import python
|
import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import semmle.python.dataflow.new.DataFlow
|
import semmle.python.dataflow.new.DataFlow
|
||||||
import utils.test.InlineExpectationsTest
|
import utils.test.InlineExpectationsTest
|
||||||
private import semmle.python.dataflow.new.internal.PrintNode
|
private import semmle.python.dataflow.new.internal.PrintNode
|
||||||
@@ -49,7 +50,7 @@ private string fromValue(DataFlow::Node fromNode) {
|
|||||||
|
|
||||||
pragma[inline]
|
pragma[inline]
|
||||||
private string fromFunc(DataFlow::ArgumentNode fromNode) {
|
private string fromFunc(DataFlow::ArgumentNode fromNode) {
|
||||||
result = fromNode.getCall().getNode().(CallNode).getFunction().getNode().(Name).getId()
|
result = fromNode.getCall().getNode().(Cfg::CallNode).getFunction().getNode().(Name).getId()
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[inline]
|
pragma[inline]
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import python
|
import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
private import semmle.python.dataflow.new.internal.PrintNode
|
private import semmle.python.dataflow.new.internal.PrintNode
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
import utils.test.InlineExpectationsTest
|
import utils.test.InlineExpectationsTest
|
||||||
|
|
||||||
signature module UnresolvedCallExpectationsSig {
|
signature module UnresolvedCallExpectationsSig {
|
||||||
predicate unresolvedCall(CallNode call);
|
predicate unresolvedCall(Cfg::CallNode call);
|
||||||
}
|
}
|
||||||
|
|
||||||
module DefaultUnresolvedCallExpectations implements UnresolvedCallExpectationsSig {
|
module DefaultUnresolvedCallExpectations implements UnresolvedCallExpectationsSig {
|
||||||
predicate unresolvedCall(CallNode call) {
|
predicate unresolvedCall(Cfg::CallNode call) {
|
||||||
|
Cfg::isCanonicalAstNodeRepresentative(call) and
|
||||||
not exists(DataFlowPrivate::DataFlowCall dfc |
|
not exists(DataFlowPrivate::DataFlowCall dfc |
|
||||||
exists(dfc.getCallable()) and dfc.getNode() = call
|
exists(dfc.getCallable()) and dfc.getNode() = call
|
||||||
) and
|
) and
|
||||||
@@ -24,7 +26,7 @@ module MakeUnresolvedCallExpectations<UnresolvedCallExpectationsSig Impl> {
|
|||||||
|
|
||||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||||
exists(location.getFile().getRelativePath()) and
|
exists(location.getFile().getRelativePath()) and
|
||||||
exists(CallNode call | Impl::unresolvedCall(call) |
|
exists(Cfg::CallNode call | Impl::unresolvedCall(call) |
|
||||||
location = call.getLocation() and
|
location = call.getLocation() and
|
||||||
tag = "unresolved_call" and
|
tag = "unresolved_call" and
|
||||||
value = prettyExpr(call.getNode()) and
|
value = prettyExpr(call.getNode()) and
|
||||||
|
|||||||
@@ -21,11 +21,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import semmle.python.dataflow.new.DataFlow
|
import semmle.python.dataflow.new.DataFlow
|
||||||
|
|
||||||
module TestConfig implements DataFlow::ConfigSig {
|
module TestConfig implements DataFlow::ConfigSig {
|
||||||
predicate isSource(DataFlow::Node node) {
|
predicate isSource(DataFlow::Node node) {
|
||||||
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
|
node.(DataFlow::CfgNode).getNode().(Cfg::NameNode).getId() = "SOURCE"
|
||||||
or
|
or
|
||||||
node.(DataFlow::CfgNode).getNode().getNode().(StringLiteral).getS() = "source"
|
node.(DataFlow::CfgNode).getNode().getNode().(StringLiteral).getS() = "source"
|
||||||
or
|
or
|
||||||
@@ -37,7 +38,7 @@ module TestConfig implements DataFlow::ConfigSig {
|
|||||||
|
|
||||||
predicate isSink(DataFlow::Node node) {
|
predicate isSink(DataFlow::Node node) {
|
||||||
exists(DataFlow::CallCfgNode call |
|
exists(DataFlow::CallCfgNode call |
|
||||||
call.getFunction().asCfgNode().(NameNode).getId() in ["SINK", "SINK_F"] and
|
call.getFunction().asCfgNode().(Cfg::NameNode).getId() in ["SINK", "SINK_F"] and
|
||||||
(node = call.getArg(_) or node = call.getArgByName(_)) and
|
(node = call.getArg(_) or node = call.getArgByName(_)) and
|
||||||
not node = call.getArgByName("not_present_at_runtime")
|
not node = call.getArgByName("not_present_at_runtime")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,12 +21,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import python
|
private import python
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
import semmle.python.dataflow.new.DataFlow
|
import semmle.python.dataflow.new.DataFlow
|
||||||
import semmle.python.dataflow.new.TaintTracking
|
import semmle.python.dataflow.new.TaintTracking
|
||||||
|
|
||||||
module TestConfig implements DataFlow::ConfigSig {
|
module TestConfig implements DataFlow::ConfigSig {
|
||||||
predicate isSource(DataFlow::Node node) {
|
predicate isSource(DataFlow::Node node) {
|
||||||
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
|
node.(DataFlow::CfgNode).getNode().(Cfg::NameNode).getId() = "SOURCE"
|
||||||
or
|
or
|
||||||
node.(DataFlow::CfgNode).getNode().getNode().(StringLiteral).getS() = "source"
|
node.(DataFlow::CfgNode).getNode().getNode().(StringLiteral).getS() = "source"
|
||||||
or
|
or
|
||||||
@@ -37,8 +38,8 @@ module TestConfig implements DataFlow::ConfigSig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node node) {
|
predicate isSink(DataFlow::Node node) {
|
||||||
exists(CallNode call |
|
exists(Cfg::CallNode call |
|
||||||
call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and
|
call.getFunction().(Cfg::NameNode).getId() in ["SINK", "SINK_F"] and
|
||||||
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
|
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ class CheckClass extends ClassObject {
|
|||||||
self_dict = sub.getObject()
|
self_dict = sub.getObject()
|
||||||
or
|
or
|
||||||
/* Indirect assignment via temporary variable */
|
/* Indirect assignment via temporary variable */
|
||||||
exists(SsaVariable v |
|
exists(SsaVariable v, ControlFlowNode subObjCfg, ControlFlowNode selfDictCfg |
|
||||||
v.getAUse() = sub.getObject().getAFlowNode() and
|
subObjCfg.getNode() = sub.getObject() and selfDictCfg.getNode() = self_dict
|
||||||
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode()
|
|
|
||||||
|
v.getAUse() = subObjCfg and
|
||||||
|
v.getDefinition().(DefinitionNode).getValue() = selfDictCfg
|
||||||
)
|
)
|
||||||
) and
|
) and
|
||||||
a.getATarget() = sub and
|
a.getATarget() = sub and
|
||||||
@@ -62,9 +64,10 @@ class CheckClass extends ClassObject {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate monkeyPatched(string name) {
|
private predicate monkeyPatched(string name) {
|
||||||
exists(Attribute a |
|
exists(Attribute a, ControlFlowNode objCfg |
|
||||||
|
objCfg.getNode() = a.getObject() and
|
||||||
a.getCtx() instanceof Store and
|
a.getCtx() instanceof Store and
|
||||||
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and
|
PointsTo::points_to(objCfg, _, this, _, _) and
|
||||||
a.getName() = name
|
a.getName() = name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -84,9 +87,9 @@ class CheckClass extends ClassObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate interestingUndefined(SelfAttributeRead a) {
|
predicate interestingUndefined(SelfAttributeRead a) {
|
||||||
exists(string name | name = a.getName() |
|
exists(string name, ControlFlowNode aCfg | name = a.getName() and aCfg.getNode() = a |
|
||||||
this.interestingContext(a, name) and
|
this.interestingContext(a, name) and
|
||||||
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
|
not this.definedInBlock(aCfg.getBasicBlock(), name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +112,9 @@ class CheckClass extends ClassObject {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate definitionInBlock(BasicBlock b, string name) {
|
private predicate definitionInBlock(BasicBlock b, string name) {
|
||||||
exists(SelfAttributeStore sa |
|
exists(SelfAttributeStore sa, ControlFlowNode saCfg |
|
||||||
sa.getAFlowNode().getBasicBlock() = b and
|
saCfg.getNode() = sa and
|
||||||
|
saCfg.getBasicBlock() = b and
|
||||||
sa.getName() = name and
|
sa.getName() = name and
|
||||||
sa.getClass() = this.getPyClass()
|
sa.getClass() = this.getPyClass()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,7 +15,9 @@
|
|||||||
import python
|
import python
|
||||||
import semmle.python.ApiGraphs
|
import semmle.python.ApiGraphs
|
||||||
|
|
||||||
predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() }
|
predicate doesnt_reraise(ExceptStmt ex) {
|
||||||
|
exists(ControlFlowNode exCfg | exCfg.getNode() = ex | exCfg.getBasicBlock().reachesExit())
|
||||||
|
}
|
||||||
|
|
||||||
predicate catches_base_exception(ExceptStmt ex) {
|
predicate catches_base_exception(ExceptStmt ex) {
|
||||||
ex.getType() = API::builtin("BaseException").getAValueReachableFromSource().asExpr()
|
ex.getType() = API::builtin("BaseException").getAValueReachableFromSource().asExpr()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user