Compare commits

..

33 Commits

Author SHA1 Message Date
yoff
b783ed69c5 Python: model exception edges for raise-prone expressions inside try/with
The new CFG previously only emitted exception edges for explicit `raise`
and `assert` statements. As a result, code that became reachable only
via the exception path of an arbitrary expression (e.g., the body of an
`except` handler following a try-body whose `call()` could raise) was
classified as dead, breaking analyses like StackTraceExposure,
FileNotAlwaysClosed, ExceptionInfo, UseOfExit, and CatchingBaseException.

This commit adds a `mayThrow` predicate over expressions that are known
sources of implicit exceptions in Python (calls, attribute access,
subscripts, arithmetic/comparison operators, imports, await/yield/yield
from) plus `from m import *` at the statement level, and routes them
through the shared CFG's `beginAbruptCompletion(_, _, ExceptionSuccessor,
always=false)` hook.

The set of exception sources is restricted to nodes that are
syntactically inside a `try`/`with` statement in the same scope.
This mirrors Java's `ControlFlowGraph::mayThrow`, which only emits
exception edges where local handling can observe them — outside such
contexts, the edges add CFG complexity (weakening BarrierGuard
precision and breaking SSA continuity around augmented assignments and
subscript stores) without analysis benefit, since exceptions just
propagate to the function exit anyway.

Net effect on the test suite: ~100 alerts restored across the exception-
related query tests (StackTraceExposure +29, ExceptionInfo +17,
FileNotAlwaysClosed +52, UseOfExit +1, CatchingBaseException restored)
with no precision regressions. Affected `.expected` files and the
regression-guard `dead_under_no_raise.py` are updated accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 08:10:06 +00:00
yoff
5b9803e03c Python: switch dataflow library to new (shared) CFG + SSA
Flips the Python dataflow trunk from the legacy CFG (semmle/python/Flow.qll)
and legacy ESSA SSA (semmle/python/essa/*) to the new shared CFG facade
(semmle.python.controlflow.internal.Cfg) and the new SSA adapter
(semmle.python.dataflow.new.internal.SsaImpl), both introduced
additively in the preceding PRs in this stack.

This is the trunk-flip equivalent of the original draft PR #21894 (kept
around as documentation), rebased on top of the four preparatory PRs:

  P1: Remove AstNode.getAFlowNode() and rewrite callers (#21919).
  P2: Qualify Flow.qll's AST references with Py:: prefix (#21920).
  P3: Add new shared-CFG-backed control flow graph (#21921).
  P4: Add new shared-SSA-backed SSA adapter (#21923).

The Python dataflow library (semmle/python/dataflow/new/) now imports
the new CFG facade and SSA adapter. All CFG-typed predicates
(ControlFlowNode, CallNode, BasicBlock, NameNode, AttrNode, ...) are
qualified with the Cfg:: prefix; SSA references switch from
EssaVariable/EssaDefinition to SsaImpl::Definition/SourceVariable.

GuardNode is redesigned to use the new CFG's outcome-node model
(isAfterTrue / isAfterFalse) instead of the legacy ConditionBlock +
flipped indirection. Only BarrierGuard<...> is preserved as public
API.

Framework files (Bottle, FastApi, Django, Tornado, Pyramid, Stdlib,
...) are updated to take CFG nodes from the new facade.

A handful of dataflow consistency tweaks for the new CFG:
- Augmented-assignment targets are treated as both load and store.
- 'from X import *' produces uncertain SSA writes for unknown names.
- CFG nodes are canonicalised so dataflow does not see equivalent
  pre/post-order pairs as distinct nodes.

Two AST tweaks for the new CFG:
- AstNodeImpl: omit PEP 695 type-parameter names from
  FunctionDefExpr / ClassDefExpr children.
- ImportResolution: drop the legacy essa import.

Test churn (~175 files): reblessed library- and query-test .expected
files reflect slightly different CFG granularity, different toString
output, and a handful of true alert deltas in security queries.

Verification: all 367 lib + src + consistency-queries compile clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 08:04:01 +00:00
Copilot
b2ff09f70a Python: add new shared-SSA-backed SSA adapter
Preparatory refactor for the shared-CFG dataflow migration. Adds the
new Python SSA adapter additively, without changing any production
behaviour.

Library additions:

- semmle.python.dataflow.new.internal.SsaImpl — Python SSA
  implementation built on the new (shared) CFG. Mirrors the Java SSA
  adapter (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 new CFG facade's NameNode.defines/uses/deletes
  predicates (added in the preceding PR), which themselves are
  one-line bridges to AST-level Name.defines/uses/deletes.

  Implicit-entry definitions are inserted for non-local/global/builtin
  reads, captured variables, and (when needed) parameters.

Test additions:

- library-tests/dataflow-new-ssa/ — exercises the new SSA over a
  representative test corpus and checks expected def/use chains.

- library-tests/dataflow-new-ssa-vs-legacy/ — runs both new SSA and
  legacy ESSA over the same corpus and diffs the results, so any
  semantic divergence shows up as a test failure.

Production impact:

None. The new SSA adapter has zero callers in lib/ and src/ — the
legacy ESSA SSA (semmle/python/essa/*) remains the default. The
dataflow library is not migrated yet; that lands in a follow-up PR.

Verified by:
- All 367 lib + src + consistency-queries compile clean.
- All 641 ControlFlow + PointsTo + dataflow + essa + consistency
  library-tests pass.
- Both new dataflow-new-ssa[/vs-legacy] test packs pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 08:03:48 +00:00
Copilot
4aee0b3c87 Python: add new shared-CFG-backed control flow graph
Preparatory refactor for the shared-CFG dataflow migration. Adds the
new Python CFG library additively, without changing any production
behaviour.

Library additions:

- semmle.python.controlflow.internal.AstNodeImpl — mediates between
  the Python AST and the shared codeql.controlflow.ControlFlowGraph
  signature. Wraps Python's Stmt/Expr/Scope/Pattern and adds two
  synthetic kinds of node (BlockStmt for body slots, intermediate
  nodes for multi-operand boolean expressions).

- semmle.python.controlflow.internal.Cfg — public facade
  re-exposing the same API surface as semmle/python/Flow.qll
  (ControlFlowNode, CallNode, BasicBlock, NameNode, DefinitionNode,
  CompareNode, ...), backed by the shared CFG.

- lib/printCfgNew.ql — debug/visualisation query for the new CFG.

- consistency-queries/CfgConsistency.ql — consistency query running
  the shared CFG's standard checks against Python.

Shared library:

- shared.controlflow.ControlFlowGraph — adds two defaulted
  getWhileElse / getForeachElse predicates to AstSig so Python can
  model while-else / for-else (no behavioural change for other
  languages).

Test additions:

- ControlFlow/bindings/* — annotation-driven SSA-binding tests for
  the new CFG (annassign, compound, comprehension, decorated,
  except_handler, imports, match_pattern, parameters, simple,
  type_params, walrus_starred, with_stmt, dead_under_no_raise).

- ControlFlow/store-load/* — basic store/load coverage.

- ControlFlow/evaluation-order/NewCfg*.ql — mirrors of the existing
  OldCfg evaluation-order self-validation suite, run against the
  new CFG via NewCfgImpl.qll.

- Minor extensions to existing test_if.py / test_boolean.py +
  cosmetic .expected churn on a handful of OldCfg tests.

No dataflow, SSA, or production query is migrated yet — that lands in
follow-up PRs. The new CFG library has zero callers in lib/ and src/.

Verified by:
- All lib + src + consistency-queries compile clean (367 queries).
- All 56 ControlFlow library-tests pass.
- All 474 dataflow + PointsTo library-tests + consistency tests pass.
- syntax_error/CONSISTENCY/CfgConsistency passes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 08:03:29 +00:00
yoff
a2295e7216 Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-05 07:57:43 +00:00
Copilot
0623acc7f5 Python: qualify Flow.qll's AST references with Py:: prefix
Preparatory refactor for the shared-CFG dataflow migration. Switches
'import python' to 'import python as Py' inside Flow.qll, and qualifies
every AST-class reference (Expr, Bytes, Dict, AssignExpr, Compare,
Module, Scope, Call, Attribute, SsaVariable, AugAssign, etc.) with the
Py:: prefix.

Flow.qll's own CFG types (ControlFlowNode, BasicBlock, CallNode,
NameNode, DefinitionNode, CompareNode, ...) keep their unqualified
names — they remain the public CFG API exported from this file.

This is a semantic noop: the qualification was applied mechanically by
script and no name resolution changes. Verified by:
- All 361 lib/ + src/ queries compile clean.
- All 186 ControlFlow + PointsTo + dataflow library-tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 07:57:42 +00:00
yoff
dc5aa8e0f5 Python: deprecate Function.getAReturnValueFlowNode() and rewrite internal callers
Follow-up to the getAFlowNode deprecation in the same PR: same AST→legacy-CFG
bridge pattern. The 11 internal call sites (across objects/, types/,
frameworks/, and TypeTrackingImpl) are rewritten to bind a `Return ret`
explicitly, then constrain via `ret.getScope() = f and n.getNode() = ret.getValue()`.

The predicate itself is preserved with a deprecation note so external
users do not experience churn.

Semantic noop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 07:57:42 +00:00
Copilot
db1e5035b4 Python: deprecate AstNode.getAFlowNode() and rewrite internal callers
Preparatory refactor for the shared-CFG dataflow migration.

Deprecates the AstNode.getAFlowNode() cached predicate on the public
Python QL API and rewrites all ~140 internal callers across lib/, src/,
test/, and tools/ from `expr.getAFlowNode() = cfgNode` to
`cfgNode.getNode() = expr`, using ControlFlowNode.getNode() which
already exists in Flow.qll.

The predicate itself is preserved (with a deprecation note pointing at
the new pattern) so external users do not experience churn — they can
migrate at their own pace and the AST/CFG hierarchies still get the
intended untangling once the deprecation eventually elapses.

Semantic noop verified by:
- All 361 lib/ + src/ queries compile clean.
- All 122 ControlFlow + PointsTo library-tests pass.
- All 64 dataflow library-tests pass.
- All 113 Variables/Exceptions/Expressions/Statements/Functions/Imports/
  Security/CWE-798/ModificationOfParameterWithDefault query-tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 07:57:42 +00:00
yoff
7a3f546587 Python: inline init_module_submodule_defn into ImportResolution
The new-dataflow ImportResolution module only used
semmle.python.essa.SsaDefinitions for the 5-line helper predicate
SsaSource::init_module_submodule_defn. Inline it locally and drop the
dependency on legacy SsaDefinitions. This is the only remaining direct
import of semmle.python.essa.* in the new dataflow stack, so dropping
it makes the layering cleaner.

Semantic noop on the current SSA: SsaSourceVariable.getName() and
GlobalVariable.getId() both project the same DB column
(variable(_,_,result)), and the old call's 'init.getEntryNode() = f'
join was just constraining init = package via Scope.getEntryNode()'s
functional uniqueness. RA dump of accesses.ql confirms only the
expected predicate-rename shuffle; all 70 dataflow + ApiGraphs library
tests pass.

This factors out commit 8cab5a20f2 from the larger shared-CFG
migration #21925.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 07:57:42 +00:00
yoff
821325b7e5 Python: simplify decorator-detection predicates to pure AST match
The internal predicates that identify `@staticmethod`, `@classmethod` and
`@property` decorators previously required the decorator's `NameNode` to
satisfy `isGlobal()` (i.e. no SSA def reaches the decorator's name use).
That filter was correct but unnecessarily indirect: these three names
are builtins, and even when a class body redefines one, the class body
has not started executing at the decorator position, so Python uses the
builtin.

Match the decorator's AST `Name` directly instead, dropping the CFG/SSA
detour. The slight semantic change — `isGlobal()` would have rejected
module-level shadowing of these builtins — is negligible in practice
and explicitly documented in the change note.

`hasContextmanagerDecorator` and `hasOverloadDecorator` keep the
`NameNode.isGlobal()` check because their target names (`contextmanager`,
`overload`) are imported, not builtin, and local shadowing is a real
concern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 07:57:42 +00:00
yoff
4d2296d4f0 Shared CFG: add defaulted getWhileElse/getForeachElse/getCatchType to AstSig
Adds three new defaulted signature predicates to the shared CFG library:

- getWhileElse / getForeachElse: `else` block of a while/for loop, if
  any (used by Python's `while-else` / `for-else` constructs).
- getCatchType: type expression of a catch clause, if any (used by
  Python's `except SomeExpr:` where the catch type is a runtime
  expression that needs CFG evaluation).

Each predicate defaults to `none()`, so behaviour is unchanged for any
language that doesn't override it (verified by re-running
java/ql/test/library-tests/controlflow/).

The Make0 succession rules are extended:
- WhileStmt/ForeachStmt: route the loop-exit edge through the else
  block before reaching the after-position.
- CatchClause: route the matching-evaluation through the type
  expression (if present) before reaching the after-value position.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 07:57:37 +00:00
Owen Mansel-Chan
aaa3b363e1 Merge pull request #21929 from owen-mc/go/no-ret-functions
Go: Recognize more non-returning logging functions
2026-06-02 10:39:28 +01:00
Owen Mansel-Chan
9dbe9adb00 Update tests 2026-06-02 09:34:03 +01:00
Owen Mansel-Chan
703cea2b65 Model panicking log functions better 2026-06-02 01:32:00 +01:00
Owen Mansel-Chan
e6e8e3d005 Taint doesn't flow through panicking functions 2026-06-02 01:31:44 +01:00
Owen Mansel-Chan
adc9b7714b Accept changed test output 2026-06-02 00:57:06 +01:00
Owen Mansel-Chan
e706c5f444 Improve test for non-returning fns 2026-06-02 00:56:12 +01:00
Owen Mansel-Chan
8a1e6d4f64 Add missing QLDocs 2026-06-02 00:41:48 +01:00
Owen Mansel-Chan
1a747dd8be (Trivial) Fix QLDoc grammar 2026-06-02 00:39:25 +01:00
Owen Mansel-Chan
28bb1a6870 Add change note 2026-06-02 00:16:23 +01:00
Owen Mansel-Chan
45b1253b23 Improve glog and klog tests 2026-06-02 00:16:21 +01:00
Owen Mansel-Chan
c99dab1d71 Improve glog (and klog) modelling 2026-06-02 00:16:19 +01:00
Owen Mansel-Chan
f3e3647209 Improve noretFunctions test 2026-06-02 00:16:17 +01:00
Owen Mansel-Chan
8d099cbe38 Recognize more non-returning logging functions 2026-06-02 00:15:58 +01:00
Tom Hvitved
9618e9b35c Merge pull request #21873 from hvitved/local-name-resolution
Shared: Local name resolution library
2026-06-01 20:51:07 +02:00
Tom Hvitved
d2f474d998 Address review comments 2026-06-01 08:30:01 +02:00
Tom Hvitved
caae5a8bf1 Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-29 14:24:45 +02:00
Tom Hvitved
09371339d7 Ruby: Adopt shared local name resolution library 2026-05-29 09:06:14 +02:00
Tom Hvitved
7718fe40a0 Ruby: Add more variable tests 2026-05-28 10:50:15 +02:00
Tom Hvitved
aeb82858d7 Rust: Run codegen 2026-05-28 10:50:13 +02:00
Tom Hvitved
c08cf81665 Rust: Adopt shared local name resolution library 2026-05-28 10:50:10 +02:00
Tom Hvitved
e06158629e Rust: More local variable tests 2026-05-28 10:50:05 +02:00
Tom Hvitved
3e09961662 Shared: Add local name binding library 2026-05-28 10:50:03 +02:00
422 changed files with 16620 additions and 10725 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* More logging functions are now recognized as not returning or panicking.

View File

@@ -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() }
} }

View File

@@ -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() }
} }

View File

@@ -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 {

View File

@@ -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

View File

@@ -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())

View File

@@ -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
} }

View File

@@ -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
) )

View File

@@ -6,5 +6,6 @@ const text = "test"
var v []byte var v []byte
func main() { func main() {
glogTest(len(v))
stdlib() stdlib()
} }

View File

@@ -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{}) {}

View File

@@ -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{}) {}

View File

@@ -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

View File

@@ -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 |

View File

@@ -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()

View File

@@ -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)

View File

@@ -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. |

View File

@@ -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"

View File

@@ -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] |

View File

@@ -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

View File

@@ -0,0 +1,2 @@
import semmle.python.controlflow.internal.AstNodeImpl
import ControlFlow::Consistency

View File

@@ -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()

View File

@@ -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 |

View File

@@ -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

View 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.

View 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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View 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>

View File

@@ -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"]`

View File

@@ -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

View File

@@ -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);
} }
} }
} }

View File

@@ -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() }
} }

View File

@@ -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))) }

View File

@@ -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()

View File

@@ -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 */

View File

@@ -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_)
) )
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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)

View File

@@ -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()
} }

View File

@@ -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(_) }

View File

@@ -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

View File

@@ -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() }

View File

@@ -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()
) )
} }

View File

@@ -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

View File

@@ -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()
) )
} }

View File

@@ -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()
} }
} }

View File

@@ -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()
| |

View File

@@ -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()

View File

@@ -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()
)
) )
} }

View 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))
}
}

View File

@@ -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

View File

@@ -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()
) )
} }

View File

@@ -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()

View File

@@ -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

View File

@@ -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()
) )

View File

@@ -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()
) )

View File

@@ -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 }

View File

@@ -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()
) )
} }

View File

@@ -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
) )
} }

View File

@@ -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()
) )
} }

View File

@@ -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)
) )
) )
) )

View File

@@ -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

View File

@@ -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() }

View File

@@ -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()
} }
/** /**

View File

@@ -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()
} }

View File

@@ -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
} }
} }

View File

@@ -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

View File

@@ -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) }

View File

@@ -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 }

View File

@@ -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()
) )

View File

@@ -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() {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()
) )
} }

View File

@@ -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()
) )
} }

View File

@@ -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()) }

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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(_)
} }
} }

View File

@@ -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
| |

View File

@@ -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
} }

View File

@@ -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()
) )
} }

View File

@@ -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. */

View File

@@ -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())
} }
/** /**

View File

@@ -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")

View File

@@ -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

View File

@@ -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 }
} }

View File

@@ -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, _)
} }
} }

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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")
) )

View File

@@ -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()
) )
} }

View File

@@ -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()
) )

View File

@@ -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