Merge branch 'main' into main

This commit is contained in:
Raul Garcia
2023-03-24 14:56:07 -07:00
committed by GitHub
409 changed files with 8741 additions and 3872 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Fixed module resolution so we allow imports of definitions that have had an attribute assigned to it, such as `class Foo; Foo.bar = 42`.

View File

@@ -0,0 +1,6 @@
---
category: deprecated
---
* The recently introduced new data flow and taint tracking APIs have had a
number of module and predicate renamings. The old APIs remain in place for
now.

View File

@@ -2,7 +2,7 @@
* Provides an implementation of global (interprocedural) data flow. This file
* re-exports the local (intraprocedural) data flow analysis from
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
* through the `Make` and `MakeWithState` modules.
* through the `Global` and `GlobalWithState` modules.
*/
private import DataFlowImplCommon
@@ -73,10 +73,10 @@ signature module ConfigSig {
*/
default FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
/** Holds if sources should be grouped in the result of `flowPath`. */
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
/** Holds if sinks should be grouped in the result of `flowPath`. */
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
@@ -166,10 +166,10 @@ signature module StateConfigSig {
*/
default FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
/** Holds if sources should be grouped in the result of `flowPath`. */
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
/** Holds if sinks should be grouped in the result of `flowPath`. */
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
@@ -182,15 +182,15 @@ signature module StateConfigSig {
}
/**
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* Gets the exploration limit for `partialFlow` and `partialFlowRev`
* measured in approximate number of interprocedural steps.
*/
signature int explorationLimitSig();
/**
* The output of a data flow computation.
* The output of a global data flow computation.
*/
signature module DataFlowSig {
signature module GlobalFlowSig {
/**
* A `Node` augmented with a call context (except for sinks) and an access path.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
@@ -203,28 +203,28 @@ signature module DataFlowSig {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate hasFlowPath(PathNode source, PathNode sink);
predicate flowPath(PathNode source, PathNode sink);
/**
* Holds if data can flow from `source` to `sink`.
*/
predicate hasFlow(Node source, Node sink);
predicate flow(Node source, Node sink);
/**
* Holds if data can flow from some source to `sink`.
*/
predicate hasFlowTo(Node sink);
predicate flowTo(Node sink);
/**
* Holds if data can flow from some source to `sink`.
*/
predicate hasFlowToExpr(DataFlowExpr sink);
predicate flowToExpr(DataFlowExpr sink);
}
/**
* Constructs a standard data flow computation.
* Constructs a global data flow computation.
*/
module Make<ConfigSig Config> implements DataFlowSig {
module Global<ConfigSig Config> implements GlobalFlowSig {
private module C implements FullStateConfigSig {
import DefaultState<Config>
import Config
@@ -233,10 +233,15 @@ module Make<ConfigSig Config> implements DataFlowSig {
import Impl<C>
}
/** DEPRECATED: Use `Global` instead. */
deprecated module Make<ConfigSig Config> implements GlobalFlowSig {
import Global<Config>
}
/**
* Constructs a data flow computation using flow state.
* Constructs a global data flow computation using flow state.
*/
module MakeWithState<StateConfigSig Config> implements DataFlowSig {
module GlobalWithState<StateConfigSig Config> implements GlobalFlowSig {
private module C implements FullStateConfigSig {
import Config
}
@@ -244,6 +249,11 @@ module MakeWithState<StateConfigSig Config> implements DataFlowSig {
import Impl<C>
}
/** DEPRECATED: Use `GlobalWithState` instead. */
deprecated module MakeWithState<StateConfigSig Config> implements GlobalFlowSig {
import GlobalWithState<Config>
}
signature class PathNodeSig {
/** Gets a textual representation of this element. */
string toString();

View File

@@ -91,10 +91,10 @@ signature module FullStateConfigSig {
*/
FlowFeature getAFeature();
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
/** Holds if sources should be grouped in the result of `flowPath`. */
predicate sourceGrouping(Node source, string sourceGroup);
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
/** Holds if sinks should be grouped in the result of `flowPath`. */
predicate sinkGrouping(Node sink, string sinkGroup);
/**
@@ -445,11 +445,7 @@ module Impl<FullStateConfigSig Config> {
}
private module Stage1 implements StageSig {
class Ap extends int {
// workaround for bad functionality-induced joins (happens when using `Unit`)
pragma[nomagic]
Ap() { this in [0 .. 1] and this < 1 }
}
class Ap = Unit;
private class Cc = boolean;
@@ -3633,7 +3629,7 @@ module Impl<FullStateConfigSig Config> {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate hasFlowPath(PathNode source, PathNode sink) {
predicate flowPath(PathNode source, PathNode sink) {
exists(PathNodeImpl flowsource, PathNodeImpl flowsink |
source = flowsource and sink = flowsink
|
@@ -3643,6 +3639,9 @@ module Impl<FullStateConfigSig Config> {
)
}
/** DEPRECATED: Use `flowPath` instead. */
deprecated predicate hasFlowPath = flowPath/2;
private predicate flowsTo(PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink) {
flowsource.isSource() and
flowsource.getNodeEx().asNode() = source and
@@ -3653,17 +3652,26 @@ module Impl<FullStateConfigSig Config> {
/**
* Holds if data can flow from `source` to `sink`.
*/
predicate hasFlow(Node source, Node sink) { flowsTo(_, _, source, sink) }
predicate flow(Node source, Node sink) { flowsTo(_, _, source, sink) }
/** DEPRECATED: Use `flow` instead. */
deprecated predicate hasFlow = flow/2;
/**
* Holds if data can flow from some source to `sink`.
*/
predicate hasFlowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() }
predicate flowTo(Node sink) { sink = any(PathNodeSink n).getNodeEx().asNode() }
/** DEPRECATED: Use `flowTo` instead. */
deprecated predicate hasFlowTo = flowTo/1;
/**
* Holds if data can flow from some source to `sink`.
*/
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) }
/** DEPRECATED: Use `flowToExpr` instead. */
deprecated predicate hasFlowToExpr = flowToExpr/1;
private predicate finalStats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples
@@ -4574,7 +4582,7 @@ module Impl<FullStateConfigSig Config> {
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*/
predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) {
predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) {
partialFlow(source, node) and
dist = node.getSourceDistance()
}
@@ -4594,7 +4602,7 @@ module Impl<FullStateConfigSig Config> {
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink) and
dist = node.getSinkDistance()
}

View File

@@ -1,5 +1,5 @@
/**
* DEPRECATED: Use `Make` and `MakeWithState` instead.
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides a `Configuration` class backwards-compatible interface to the data
* flow library.
@@ -388,7 +388,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
hasFlowPath(source, sink) and source.getConfiguration() = config
flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -1,5 +1,5 @@
/**
* DEPRECATED: Use `Make` and `MakeWithState` instead.
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides a `Configuration` class backwards-compatible interface to the data
* flow library.
@@ -388,7 +388,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
hasFlowPath(source, sink) and source.getConfiguration() = config
flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -1,5 +1,5 @@
/**
* DEPRECATED: Use `Make` and `MakeWithState` instead.
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides a `Configuration` class backwards-compatible interface to the data
* flow library.
@@ -388,7 +388,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
hasFlowPath(source, sink) and source.getConfiguration() = config
flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -1,5 +1,5 @@
/**
* DEPRECATED: Use `Make` and `MakeWithState` instead.
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides a `Configuration` class backwards-compatible interface to the data
* flow library.
@@ -388,7 +388,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
hasFlowPath(source, sink) and source.getConfiguration() = config
flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -140,10 +140,8 @@ private module LambdaFlow {
}
pragma[nomagic]
private TReturnPositionSimple viableReturnPosLambda(
DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
) {
result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
private TReturnPositionSimple viableReturnPosLambda(DataFlowCall call, ReturnKind kind) {
result = TReturnPositionSimple0(viableCallableLambda(call, _), kind)
}
private predicate viableReturnPosOutNonLambda(
@@ -155,11 +153,12 @@ private module LambdaFlow {
)
}
pragma[nomagic]
private predicate viableReturnPosOutLambda(
DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
DataFlowCall call, TReturnPositionSimple pos, OutNode out
) {
exists(ReturnKind kind |
pos = viableReturnPosLambda(call, lastCall, kind) and
pos = viableReturnPosLambda(call, kind) and
out = getAnOutNode(call, kind)
)
}
@@ -188,6 +187,7 @@ private module LambdaFlow {
else any()
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlow0(
DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
@@ -274,6 +274,7 @@ private module LambdaFlow {
)
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlowOut(
DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
@@ -285,7 +286,7 @@ private module LambdaFlow {
or
// non-linear recursion
revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
viableReturnPosOutLambda(call, _, pos, out)
viableReturnPosOutLambda(call, pos, out)
)
}

View File

@@ -1,5 +1,5 @@
/**
* DEPRECATED: Use `Make` and `MakeWithState` instead.
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides a `Configuration` class backwards-compatible interface to the data
* flow library.
@@ -388,7 +388,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
hasFlowPath(source, sink) and source.getConfiguration() = config
flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -109,6 +109,7 @@ module Public {
}
/** Gets the stack obtained by dropping the first `i` elements, if any. */
pragma[assume_small_delta]
SummaryComponentStack drop(int i) {
i = 0 and result = this
or

View File

@@ -65,31 +65,75 @@ private import semmle.python.dataflow.new.internal.DataFlowPrivate
*/
module ImportResolution {
/**
* Holds if the module `m` defines a name `name` by assigning `defn` to it. This is an
* overapproximation, as `name` may not in fact be exported (e.g. by defining an `__all__` that does
* not include `name`).
* Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed
* for import resolution.
*/
private predicate allowedEssaImportStep(EssaDefinition defFrom, EssaDefinition defTo) {
// to handle definitions guarded by if-then-else
defFrom = defTo.(PhiFunction).getAnInput()
or
// refined variable
// example: https://github.com/nvbn/thefuck/blob/ceeaeab94b5df5a4fe9d94d61e4f6b0bbea96378/thefuck/utils.py#L25-L45
defFrom = defTo.(EssaNodeRefinement).getInput().getDefinition()
}
/**
* Holds if the module `m` defines a name `name` with the value `val`. The value
* represents the value `name` will have at the end of the module (the last place we
* have def-use flow to).
*
* Note: The handling of re-exporting imports is a bit simplistic. We assume that if
* an import is made, it will be re-exported (which will not be the case if a new
* value is assigned to the name, or it is deleted).
*/
pragma[nomagic]
predicate module_export(Module m, string name, DataFlow::CfgNode defn) {
exists(EssaVariable v, EssaDefinition essaDef |
v.getName() = name and
v.getAUse() = ImportStar::getStarImported*(m).getANormalExit() and
(
essaDef = v.getDefinition()
or
// to handle definitions guarded by if-then-else
essaDef = v.getDefinition().(PhiFunction).getAnInput()
)
predicate module_export(Module m, string name, DataFlow::Node val) {
// Definitions made inside `m` itself
//
// for code such as `foo = ...; foo.bar = ...` there will be TWO
// EssaDefinition/EssaVariable. One for `foo = ...` (AssignmentDefinition) and one
// for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The
// EssaNodeRefinement is the one that will reach the end of the module (normal
// exit).
//
// However, we cannot just use the EssaNodeRefinement as the `val`, because the
// normal data-flow depends on use-use flow, and use-use flow targets CFG nodes not
// EssaNodes. So we need to go back from the EssaDefinition/EssaVariable that
// reaches the end of the module, to the first definition of the variable, and then
// track forwards using use-use flow to find a suitable CFG node that has flow into
// it from use-use flow.
exists(EssaVariable lastUseVar, EssaVariable firstDef |
lastUseVar.getName() = name and
// we ignore special variable $ introduced by our analysis (not used for anything)
// we ignore special variable * introduced by `from <pkg> import *` -- TODO: understand why we even have this?
not name in ["$", "*"] and
lastUseVar.getAUse() = m.getANormalExit() and
allowedEssaImportStep*(firstDef, lastUseVar) and
not allowedEssaImportStep(_, firstDef)
|
defn.getNode() = essaDef.(AssignmentDefinition).getValue()
not EssaFlow::defToFirstUse(firstDef, _) and
val.asVar() = firstDef
or
defn.getNode() = essaDef.(ArgumentRefinement).getArgument()
exists(ControlFlowNode mid, ControlFlowNode end |
EssaFlow::defToFirstUse(firstDef, mid) and
EssaFlow::useToNextUse*(mid, end) and
not EssaFlow::useToNextUse(end, _) and
val.asCfgNode() = end
)
)
or
// re-exports from `from <pkg> import *`
exists(Module importedFrom |
importedFrom = ImportStar::getStarImported(m) and
module_export(importedFrom, name, val) and
potential_module_export(importedFrom, name)
)
or
// re-exports from `import <pkg>` or `from <pkg> import <stuff>`
exists(Alias a |
defn.asExpr() = [a.getValue(), a.getValue().(ImportMember).getModule()] and
val.asExpr() = a.getValue() and
a.getAsname().(Name).getId() = name and
defn.getScope() = m
val.getScope() = m
)
}
@@ -263,9 +307,21 @@ module ImportResolution {
module_reexport(reexporter, attr_name, m)
)
or
// Submodules that are implicitly defined with relative imports of the form `from .foo import ...`.
// In practice, we create a definition for each module in a package, even if it is not imported.
// submodules of packages will be available as `<pkg>.<submodule>` after doing
// `import <pkg>.<submodule>` at least once in the program, or can be directly
// imported with `from <pkg> import <submodule>` (even with an empty
// `<pkg>.__init__` file).
//
// Until an import of `<pkg>.<submodule>` is executed, it is technically possible
// that `<pkg>.<submodule>` (or `from <pkg> import <submodule>`) can refer to an
// attribute set in `<pkg>.__init__`.
//
// Therefore, if there is an attribute defined in `<pkg>.__init__` with the same
// name as a submodule, we always consider that this attribute _could_ be a
// reference to the submodule, even if we don't know that the submodule has been
// imported yet.
exists(string submodule, Module package |
submodule = result.asVar().getName() and
SsaSource::init_module_submodule_defn(result.asVar().getSourceVariable(),
package.getEntryNode()) and
m = getModuleFromName(package.getPackageName() + "." + submodule)

View File

@@ -33,9 +33,9 @@ private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> imp
}
/**
* Constructs a standard taint tracking computation.
* Constructs a global taint tracking computation.
*/
module Make<DataFlow::ConfigSig Config> implements DataFlow::DataFlowSig {
module Global<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
private module Config0 implements DataFlowInternal::FullStateConfigSig {
import DataFlowInternal::DefaultState<Config>
import Config
@@ -48,10 +48,15 @@ module Make<DataFlow::ConfigSig Config> implements DataFlow::DataFlowSig {
import DataFlowInternal::Impl<C>
}
/** DEPRECATED: Use `Global` instead. */
deprecated module Make<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
import Global<Config>
}
/**
* Constructs a taint tracking computation using flow state.
* Constructs a global taint tracking computation using flow state.
*/
module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::DataFlowSig {
module GlobalWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
private module Config0 implements DataFlowInternal::FullStateConfigSig {
import Config
}
@@ -62,3 +67,8 @@ module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::DataF
import DataFlowInternal::Impl<C>
}
/** DEPRECATED: Use `GlobalWithState` instead. */
deprecated module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
import GlobalWithState<Config>
}

View File

@@ -112,7 +112,7 @@ module InsecureContextConfiguration2 implements DataFlow::StateConfigSig {
}
}
private module InsecureContextFlow = DataFlow::MakeWithState<InsecureContextConfiguration2>;
private module InsecureContextFlow = DataFlow::GlobalWithState<InsecureContextConfiguration2>;
/**
* Holds if `conectionCreation` marks the creation of a connection based on the contex
@@ -127,7 +127,7 @@ predicate unsafe_connection_creation_with_context(
) {
// Connection created from a context allowing `insecure_version`.
exists(InsecureContextFlow::PathNode src, InsecureContextFlow::PathNode sink |
InsecureContextFlow::hasFlowPath(src, sink) and
InsecureContextFlow::flowPath(src, sink) and
src.getNode() = contextOrigin and
sink.getNode() = connectionCreation and
sink.getState().allowsInsecureVersion(insecure_version) and

View File

@@ -0,0 +1 @@
../coverage/argumentRoutingTest.ql

View File

@@ -0,0 +1,54 @@
# Python 2 specific tests, like the one in coverage/classes.py
#
# User-defined methods, both instance methods and class methods, can be called in many non-standard ways
# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` method on a
# class `C` will be called by the syntactic construct `await c` when `c` is an instance of `C`.
#
# These tests should cover all the class calls that we hope to support.
# It is based on https://docs.python.org/3/reference/datamodel.html, and headings refer there.
#
# All functions starting with "test_" should run and execute `print("OK")` exactly once.
# This can be checked by running validTest.py.
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from testlib import expects
def SINK1(x):
pass
def SINK2(x):
pass
def SINK3(x):
pass
def SINK4(x):
pass
def OK():
print("OK")
# 3.3.8. Emulating numeric types
# object.__index__(self)
class With_index:
def __index__(self):
SINK1(self)
OK() # Call not found
return 0
def test_index():
import operator
with_index = With_index() #$ MISSING: arg1="SSA variable with_index" func=With_index.__index__
operator.index(with_index)

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1 --lang=2

View File

@@ -0,0 +1 @@
../coverage/argumentRoutingTest.ql

View File

@@ -0,0 +1,72 @@
# Python 3 specific tests, like the one in coverage/classes.py
#
# User-defined methods, both instance methods and class methods, can be called in many non-standard ways
# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` method on a
# class `C` will be called by the syntactic construct `await c` when `c` is an instance of `C`.
#
# These tests should cover all the class calls that we hope to support.
# It is based on https://docs.python.org/3/reference/datamodel.html, and headings refer there.
#
# All functions starting with "test_" should run and execute `print("OK")` exactly once.
# This can be checked by running validTest.py.
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from testlib import expects
def SINK1(x):
pass
def SINK2(x):
pass
def SINK3(x):
pass
def SINK4(x):
pass
def OK():
print("OK")
# 3.3.7. Emulating container types
# object.__length_hint__(self)
class With_length_hint:
def __length_hint__(self):
SINK1(self)
OK()
return 0
def test_length_hint():
import operator
with_length_hint = With_length_hint() #$ arg1="SSA variable with_length_hint" func=With_length_hint.__length_hint__
operator.length_hint(with_length_hint)
# 3.3.8. Emulating numeric types
# object.__index__(self)
class With_index:
def __index__(self):
SINK1(self)
OK() # Call not found
return 0
def test_index():
import operator
with_index = With_index() #$ arg1="SSA variable with_index" func=With_index.__index__
operator.index(with_index)

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1 --lang=3

View File

@@ -535,21 +535,6 @@ def test_len_if():
pass
# object.__length_hint__(self)
class With_length_hint:
def __length_hint__(self):
SINK1(self)
OK() # Call not found
return 0
def test_length_hint():
import operator
with_length_hint = With_length_hint() #$ MISSING: arg1="SSA variable with_length_hint" func=With_length_hint.__length_hint__
operator.length_hint(with_length_hint)
# object.__getitem__(self, key)
class With_getitem:
def __getitem__(self, key):
@@ -1378,13 +1363,6 @@ class With_index:
return 0
def test_index():
import operator
with_index = With_index() #$ MISSING: arg1="SSA variable with_index" func=With_index.__index__
operator.index(with_index)
def test_index_slicing():
with_index = With_index() #$ MISSING: arg1="SSA variable with_index" func=With_index.__index__
[0][with_index:1]

View File

@@ -64,9 +64,12 @@ if __name__ == "__main__":
check_tests_valid("coverage.test")
check_tests_valid("coverage.argumentPassing")
check_tests_valid("coverage.datamodel")
check_tests_valid("coverage-py2.classes")
check_tests_valid("coverage-py3.classes")
check_tests_valid("variable-capture.in")
check_tests_valid("variable-capture.nonlocal")
check_tests_valid("variable-capture.dict")
check_tests_valid("variable-capture.collections")
check_tests_valid("module-initialization.multiphase")
check_tests_valid("fieldflow.test")
check_tests_valid_after_version("match.test", (3, 10))

View File

@@ -31,7 +31,7 @@ def SINK_F(x):
print("OK")
l = [NONSOURCE]
SINK_F(l_mod[0])
SINK_F(l[0])
l_mod = [SOURCE for x in l]
SINK(l_mod[0]) #$ captured

View File

@@ -0,0 +1,160 @@
| attr_clash.__init__ | __file__ | attr_clash/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
| attr_clash.__init__ | __name__ | attr_clash/__init__.py:0:0:0:0 | GSSA Variable __name__ |
| attr_clash.__init__ | __package__ | attr_clash/__init__.py:0:0:0:0 | GSSA Variable __package__ |
| attr_clash.__init__ | clashing_attr | attr_clash/__init__.py:4:1:4:13 | GSSA Variable clashing_attr |
| attr_clash.__init__ | enter | attr_clash/__init__.py:2:1:2:5 | ControlFlowNode for enter |
| attr_clash.__init__ | exit | attr_clash/__init__.py:6:1:6:4 | ControlFlowNode for exit |
| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:4:6:4:13 | ControlFlowNode for __file__ |
| attr_clash.clashing_attr | __name__ | attr_clash/clashing_attr.py:0:0:0:0 | GSSA Variable __name__ |
| attr_clash.clashing_attr | __package__ | attr_clash/clashing_attr.py:0:0:0:0 | GSSA Variable __package__ |
| attr_clash.clashing_attr | enter | attr_clash/clashing_attr.py:2:1:2:5 | ControlFlowNode for enter |
| attr_clash.clashing_attr | exit | attr_clash/clashing_attr.py:4:1:4:4 | ControlFlowNode for exit |
| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:4:6:4:13 | ControlFlowNode for __file__ |
| attr_clash.non_clashing_submodule | __name__ | attr_clash/non_clashing_submodule.py:0:0:0:0 | GSSA Variable __name__ |
| attr_clash.non_clashing_submodule | __package__ | attr_clash/non_clashing_submodule.py:0:0:0:0 | GSSA Variable __package__ |
| attr_clash.non_clashing_submodule | enter | attr_clash/non_clashing_submodule.py:2:1:2:5 | ControlFlowNode for enter |
| attr_clash.non_clashing_submodule | exit | attr_clash/non_clashing_submodule.py:4:1:4:4 | ControlFlowNode for exit |
| bar | __file__ | bar.py:6:6:6:13 | ControlFlowNode for __file__ |
| bar | __name__ | bar.py:0:0:0:0 | GSSA Variable __name__ |
| bar | __package__ | bar.py:0:0:0:0 | GSSA Variable __package__ |
| bar | bar_attr | bar.py:4:1:4:8 | GSSA Variable bar_attr |
| bar | enter | bar.py:2:1:2:5 | ControlFlowNode for enter |
| bar | exit | bar.py:6:1:6:4 | ControlFlowNode for exit |
| baz | __file__ | baz.py:6:6:6:13 | ControlFlowNode for __file__ |
| baz | __name__ | baz.py:0:0:0:0 | GSSA Variable __name__ |
| baz | __package__ | baz.py:0:0:0:0 | GSSA Variable __package__ |
| baz | baz_attr | baz.py:4:1:4:8 | GSSA Variable baz_attr |
| baz | enter | baz.py:2:1:2:5 | ControlFlowNode for enter |
| baz | exit | baz.py:6:1:6:4 | ControlFlowNode for exit |
| block_flow_check | SOURCE | block_flow_check.py:12:25:12:30 | ControlFlowNode for SOURCE |
| block_flow_check | __file__ | block_flow_check.py:14:6:14:13 | ControlFlowNode for __file__ |
| block_flow_check | __name__ | block_flow_check.py:0:0:0:0 | GSSA Variable __name__ |
| block_flow_check | __package__ | block_flow_check.py:0:0:0:0 | GSSA Variable __package__ |
| block_flow_check | check | block_flow_check.py:12:1:12:5 | ControlFlowNode for check |
| block_flow_check | enter | block_flow_check.py:2:1:2:5 | ControlFlowNode for enter |
| block_flow_check | exit | block_flow_check.py:14:1:14:4 | ControlFlowNode for exit |
| block_flow_check | globals | block_flow_check.py:12:33:12:39 | ControlFlowNode for globals |
| block_flow_check | object | block_flow_check.py:4:14:4:19 | ControlFlowNode for object |
| block_flow_check | staticmethod | block_flow_check.py:0:0:0:0 | GSSA Variable staticmethod |
| foo | __file__ | foo.py:14:6:14:13 | ControlFlowNode for __file__ |
| foo | __name__ | foo.py:0:0:0:0 | GSSA Variable __name__ |
| foo | __package__ | foo.py:0:0:0:0 | GSSA Variable __package__ |
| foo | __private_foo_attr | foo.py:8:1:8:18 | GSSA Variable __private_foo_attr |
| foo | bar_reexported | foo.py:11:8:11:10 | ControlFlowNode for ImportExpr |
| foo | bar_reexported | foo.py:12:34:12:47 | ControlFlowNode for bar_reexported |
| foo | check | foo.py:12:1:12:5 | ControlFlowNode for check |
| foo | enter | foo.py:2:1:2:5 | ControlFlowNode for enter |
| foo | exit | foo.py:14:1:14:4 | ControlFlowNode for exit |
| foo | foo_attr | foo.py:5:1:5:8 | GSSA Variable foo_attr |
| foo | globals | foo.py:12:71:12:77 | ControlFlowNode for globals |
| generous_export | Exception | generous_export.py:16:11:16:19 | ControlFlowNode for Exception |
| generous_export | SOURCE | generous_export.py:15:11:15:16 | ControlFlowNode for SOURCE |
| generous_export | SOURCE | generous_export.py:20:25:20:30 | ControlFlowNode for SOURCE |
| generous_export | __file__ | generous_export.py:22:6:22:13 | ControlFlowNode for __file__ |
| generous_export | __name__ | generous_export.py:0:0:0:0 | GSSA Variable __name__ |
| generous_export | __package__ | generous_export.py:0:0:0:0 | GSSA Variable __package__ |
| generous_export | check | generous_export.py:20:1:20:5 | ControlFlowNode for check |
| generous_export | enter | generous_export.py:2:1:2:5 | ControlFlowNode for enter |
| generous_export | eval | generous_export.py:10:4:10:7 | ControlFlowNode for eval |
| generous_export | exit | generous_export.py:22:1:22:4 | ControlFlowNode for exit |
| generous_export | globals | generous_export.py:20:33:20:39 | ControlFlowNode for globals |
| generous_export | object | generous_export.py:4:14:4:19 | ControlFlowNode for object |
| generous_export | print | generous_export.py:15:5:15:9 | ControlFlowNode for print |
| generous_export | staticmethod | generous_export.py:0:0:0:0 | GSSA Variable staticmethod |
| has_defined_all | __all__ | has_defined_all.py:7:1:7:7 | GSSA Variable __all__ |
| has_defined_all | __file__ | has_defined_all.py:9:6:9:13 | ControlFlowNode for __file__ |
| has_defined_all | __name__ | has_defined_all.py:0:0:0:0 | GSSA Variable __name__ |
| has_defined_all | __package__ | has_defined_all.py:0:0:0:0 | GSSA Variable __package__ |
| has_defined_all | all_defined_bar | has_defined_all.py:5:1:5:15 | GSSA Variable all_defined_bar |
| has_defined_all | all_defined_foo | has_defined_all.py:4:1:4:15 | GSSA Variable all_defined_foo |
| has_defined_all | enter | has_defined_all.py:2:1:2:5 | ControlFlowNode for enter |
| has_defined_all | exit | has_defined_all.py:9:1:9:4 | ControlFlowNode for exit |
| has_defined_all_copy | __all__ | has_defined_all_copy.py:9:1:9:7 | GSSA Variable __all__ |
| has_defined_all_copy | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
| has_defined_all_copy | __name__ | has_defined_all_copy.py:0:0:0:0 | GSSA Variable __name__ |
| has_defined_all_copy | __package__ | has_defined_all_copy.py:0:0:0:0 | GSSA Variable __package__ |
| has_defined_all_copy | all_defined_bar_copy | has_defined_all_copy.py:7:1:7:20 | GSSA Variable all_defined_bar_copy |
| has_defined_all_copy | all_defined_foo_copy | has_defined_all_copy.py:6:1:6:20 | GSSA Variable all_defined_foo_copy |
| has_defined_all_copy | enter | has_defined_all_copy.py:4:1:4:5 | ControlFlowNode for enter |
| has_defined_all_copy | exit | has_defined_all_copy.py:11:1:11:4 | ControlFlowNode for exit |
| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:6:6:6:13 | ControlFlowNode for __file__ |
| has_defined_all_indirection | __name__ | has_defined_all_indirection.py:0:0:0:0 | GSSA Variable __name__ |
| has_defined_all_indirection | __package__ | has_defined_all_indirection.py:0:0:0:0 | GSSA Variable __package__ |
| has_defined_all_indirection | all_defined_foo_copy | has_defined_all_copy.py:6:1:6:20 | GSSA Variable all_defined_foo_copy |
| has_defined_all_indirection | enter | has_defined_all_indirection.py:2:1:2:5 | ControlFlowNode for enter |
| has_defined_all_indirection | exit | has_defined_all_indirection.py:6:1:6:4 | ControlFlowNode for exit |
| if_then_else | __file__ | if_then_else.py:16:6:16:13 | ControlFlowNode for __file__ |
| if_then_else | __name__ | if_then_else.py:0:0:0:0 | GSSA Variable __name__ |
| if_then_else | __package__ | if_then_else.py:0:0:0:0 | GSSA Variable __package__ |
| if_then_else | enter | if_then_else.py:2:1:2:5 | ControlFlowNode for enter |
| if_then_else | eval | if_then_else.py:11:8:11:11 | ControlFlowNode for eval |
| if_then_else | exit | if_then_else.py:16:1:16:4 | ControlFlowNode for exit |
| if_then_else | if_then_else_defined | if_then_else.py:7:5:7:24 | GSSA Variable if_then_else_defined |
| if_then_else | if_then_else_defined | if_then_else.py:12:9:12:28 | GSSA Variable if_then_else_defined |
| if_then_else | if_then_else_defined | if_then_else.py:14:9:14:28 | GSSA Variable if_then_else_defined |
| if_then_else_refined | SOURCE | if_then_else_refined.py:11:11:11:16 | ControlFlowNode for SOURCE |
| if_then_else_refined | SOURCE | if_then_else_refined.py:13:11:13:16 | ControlFlowNode for SOURCE |
| if_then_else_refined | __file__ | if_then_else_refined.py:19:6:19:13 | ControlFlowNode for __file__ |
| if_then_else_refined | __name__ | if_then_else_refined.py:0:0:0:0 | GSSA Variable __name__ |
| if_then_else_refined | __package__ | if_then_else_refined.py:0:0:0:0 | GSSA Variable __package__ |
| if_then_else_refined | check | if_then_else_refined.py:17:1:17:5 | ControlFlowNode for check |
| if_then_else_refined | enter | if_then_else_refined.py:4:1:4:5 | ControlFlowNode for enter |
| if_then_else_refined | eval | if_then_else_refined.py:10:4:10:7 | ControlFlowNode for eval |
| if_then_else_refined | exit | if_then_else_refined.py:19:1:19:4 | ControlFlowNode for exit |
| if_then_else_refined | globals | if_then_else_refined.py:17:24:17:30 | ControlFlowNode for globals |
| if_then_else_refined | src | if_then_else_refined.py:17:19:17:21 | ControlFlowNode for src |
| package.__init__ | __file__ | package/__init__.py:7:6:7:13 | ControlFlowNode for __file__ |
| package.__init__ | __name__ | package/__init__.py:0:0:0:0 | GSSA Variable __name__ |
| package.__init__ | __package__ | package/__init__.py:0:0:0:0 | GSSA Variable __package__ |
| package.__init__ | attr_used_in_subpackage | package/__init__.py:4:1:4:23 | GSSA Variable attr_used_in_subpackage |
| package.__init__ | enter | package/__init__.py:2:1:2:5 | ControlFlowNode for enter |
| package.__init__ | exit | package/__init__.py:7:1:7:4 | ControlFlowNode for exit |
| package.__init__ | package_attr | package/__init__.py:5:1:5:12 | GSSA Variable package_attr |
| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
| package.subpackage2.__init__ | __name__ | package/subpackage2/__init__.py:0:0:0:0 | GSSA Variable __name__ |
| package.subpackage2.__init__ | __package__ | package/subpackage2/__init__.py:0:0:0:0 | GSSA Variable __package__ |
| package.subpackage2.__init__ | enter | package/subpackage2/__init__.py:2:1:2:5 | ControlFlowNode for enter |
| package.subpackage2.__init__ | exit | package/subpackage2/__init__.py:6:1:6:4 | ControlFlowNode for exit |
| package.subpackage2.__init__ | subpackage2_attr | package/subpackage2/__init__.py:4:1:4:16 | GSSA Variable subpackage2_attr |
| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:14:6:14:13 | ControlFlowNode for __file__ |
| package.subpackage.__init__ | __name__ | package/subpackage/__init__.py:0:0:0:0 | GSSA Variable __name__ |
| package.subpackage.__init__ | __package__ | package/subpackage/__init__.py:0:0:0:0 | GSSA Variable __package__ |
| package.subpackage.__init__ | check | package/subpackage/__init__.py:12:1:12:5 | ControlFlowNode for check |
| package.subpackage.__init__ | enter | package/subpackage/__init__.py:2:1:2:5 | ControlFlowNode for enter |
| package.subpackage.__init__ | exit | package/subpackage/__init__.py:14:1:14:4 | ControlFlowNode for exit |
| package.subpackage.__init__ | globals | package/subpackage/__init__.py:12:79:12:85 | ControlFlowNode for globals |
| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:7:16:7:55 | ControlFlowNode for ImportMember |
| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:8:24:8:36 | ControlFlowNode for imported_attr |
| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:24:11:38 | ControlFlowNode for ImportMember |
| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:24:11:38 | GSSA Variable irrelevant_attr |
| package.subpackage.__init__ | submodule | package/subpackage/__init__.py:12:35:12:43 | ControlFlowNode for submodule |
| package.subpackage.__init__ | subpackage_attr | package/subpackage/__init__.py:4:1:4:15 | GSSA Variable subpackage_attr |
| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:7:6:7:13 | ControlFlowNode for __file__ |
| package.subpackage.submodule | __name__ | package/subpackage/submodule.py:0:0:0:0 | GSSA Variable __name__ |
| package.subpackage.submodule | __package__ | package/subpackage/submodule.py:0:0:0:0 | GSSA Variable __package__ |
| package.subpackage.submodule | enter | package/subpackage/submodule.py:2:1:2:5 | ControlFlowNode for enter |
| package.subpackage.submodule | exit | package/subpackage/submodule.py:7:1:7:4 | ControlFlowNode for exit |
| package.subpackage.submodule | irrelevant_attr | package/subpackage/submodule.py:5:1:5:15 | GSSA Variable irrelevant_attr |
| package.subpackage.submodule | submodule_attr | package/subpackage/submodule.py:4:1:4:14 | GSSA Variable submodule_attr |
| refined | SOURCE | refined.py:12:25:12:30 | ControlFlowNode for SOURCE |
| refined | __file__ | refined.py:14:6:14:13 | ControlFlowNode for __file__ |
| refined | __name__ | refined.py:0:0:0:0 | GSSA Variable __name__ |
| refined | __package__ | refined.py:0:0:0:0 | GSSA Variable __package__ |
| refined | check | refined.py:12:1:12:5 | ControlFlowNode for check |
| refined | enter | refined.py:2:1:2:5 | ControlFlowNode for enter |
| refined | exit | refined.py:14:1:14:4 | ControlFlowNode for exit |
| refined | globals | refined.py:12:33:12:39 | ControlFlowNode for globals |
| refined | object | refined.py:4:14:4:19 | ControlFlowNode for object |
| simplistic_reexport | __file__ | simplistic_reexport.py:19:6:19:13 | ControlFlowNode for __file__ |
| simplistic_reexport | __name__ | simplistic_reexport.py:0:0:0:0 | GSSA Variable __name__ |
| simplistic_reexport | __package__ | simplistic_reexport.py:0:0:0:0 | GSSA Variable __package__ |
| simplistic_reexport | bar_attr | simplistic_reexport.py:6:17:6:24 | ControlFlowNode for ImportMember |
| simplistic_reexport | bar_attr | simplistic_reexport.py:10:19:10:26 | ControlFlowNode for bar_attr |
| simplistic_reexport | baz_attr | baz.py:4:1:4:8 | GSSA Variable baz_attr |
| simplistic_reexport | baz_attr | simplistic_reexport.py:17:19:17:26 | ControlFlowNode for baz_attr |
| simplistic_reexport | check | simplistic_reexport.py:17:1:17:5 | ControlFlowNode for check |
| simplistic_reexport | enter | baz.py:2:1:2:5 | ControlFlowNode for enter |
| simplistic_reexport | enter | simplistic_reexport.py:4:1:4:5 | ControlFlowNode for enter |
| simplistic_reexport | exit | baz.py:6:1:6:4 | ControlFlowNode for exit |
| simplistic_reexport | exit | simplistic_reexport.py:19:1:19:4 | ControlFlowNode for exit |
| simplistic_reexport | globals | simplistic_reexport.py:17:44:17:50 | ControlFlowNode for globals |

View File

@@ -0,0 +1,17 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.internal.ImportResolution
from Module m, string name, DataFlow::Node defn
where
ImportResolution::module_export(m, name, defn) and
exists(m.getLocation().getFile().getRelativePath()) and
not defn.getScope() = any(Module trace | trace.getName() = "trace") and
not m.getName() = "main" and
// Since we test on both Python 2 and Python 3, but `namespace_package` is not allowed
// on Python 2 because of the missing `__init__.py` files, we remove those results
// from Python 3 tests as well. One alternative is to only run these tests under
// Python 3, but that does not seems like a good solution -- we could easily miss a
// Python 2 only regression then :O
not m.getName() = "namespace_package.namespace_module"
select m.getName(), name, defn

View File

@@ -0,0 +1,6 @@
from trace import *
enter(__file__)
baz_attr = "baz_attr"
exit(__file__)

View File

@@ -0,0 +1,14 @@
from trace import *
enter(__file__)
class SOURCE(object):
@staticmethod
def block_flow(): pass
check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
SOURCE.block_flow()
check("SOURCE", SOURCE, SOURCE, globals())
exit(__file__)

View File

@@ -0,0 +1,22 @@
from trace import *
enter(__file__)
class SOURCE(object):
@staticmethod
def block_flow(): pass
check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
if eval("False"):
# With our current import resolution, this value for SOURCE will be considered to be
# a valid value at the end of this module, because it's the end of a use-use flow.
# This is clearly wrong, so our import resolution is a bit too generous on what is
# exported
print(SOURCE)
raise Exception()
SOURCE.block_flow()
check("SOURCE", SOURCE, SOURCE, globals())
exit(__file__)

View File

@@ -0,0 +1,9 @@
from trace import *
enter(__file__)
all_defined_foo = "all_defined_foo"
all_defined_bar = "all_defined_bar"
__all__ = ["all_defined_foo"]
exit(__file__)

View File

@@ -0,0 +1,11 @@
# a copy of `has_defined_all.py` that is imported by `has_defined_all_indirection.py`
# with its' own names such that we can check both `import *` without any cross-talk
from trace import *
enter(__file__)
all_defined_foo_copy = "all_defined_foo_copy"
all_defined_bar_copy = "all_defined_bar_copy"
__all__ = ["all_defined_foo_copy"]
exit(__file__)

View File

@@ -0,0 +1,6 @@
from trace import *
enter(__file__)
from has_defined_all_copy import *
exit(__file__)

View File

@@ -0,0 +1,19 @@
# combination of refined and if_then_else
from trace import *
enter(__file__)
class SOURCE(): pass
# definition based on "random" choice in this case it will always go the the if-branch,
# but our analysis is not able to figure this out
if eval("True"):
src = SOURCE
else:
src = SOURCE
src.foo = 42
check("src", src, src, globals()) #$ prints=SOURCE
exit(__file__)

View File

@@ -11,6 +11,9 @@ private class SourceString extends DataFlow::Node {
SourceString() {
this.asExpr().(StrConst).getText() = contents and
this.asExpr().getParent() instanceof Assign
or
this.asExpr().(ClassExpr).getInnerScope().getName() = "SOURCE" and
contents = "SOURCE"
}
string getContents() { result = contents }
@@ -63,6 +66,10 @@ private class ImportConfiguration extends DataFlow::Configuration {
override predicate isSink(DataFlow::Node sink) {
sink = API::moduleImport("trace").getMember("check").getACall().getArg(1)
}
override predicate isBarrier(DataFlow::Node node) {
exists(DataFlow::MethodCallNode call | call.calls(node, "block_flow"))
}
}
class ResolutionTest extends InlineExpectationsTest {

View File

@@ -84,6 +84,52 @@ from attr_clash import clashing_attr, non_clashing_submodule #$ imports=attr_cla
check("clashing_attr", clashing_attr, "clashing_attr", globals()) #$ prints=clashing_attr SPURIOUS: prints="<module attr_clash.clashing_attr>"
check("non_clashing_submodule", non_clashing_submodule, "<module attr_clash.non_clashing_submodule>", globals()) #$ prints="<module attr_clash.non_clashing_submodule>"
import attr_clash.clashing_attr as _doesnt_matter #$ imports=attr_clash.clashing_attr as=_doesnt_matter
from attr_clash import clashing_attr, non_clashing_submodule #$ imports=attr_clash.clashing_attr as=clashing_attr imports=attr_clash.non_clashing_submodule as=non_clashing_submodule
check("clashing_attr", clashing_attr, "<module attr_clash.clashing_attr>", globals()) #$ prints="<module attr_clash.clashing_attr>" SPURIOUS: prints=clashing_attr
# check that import * only imports the __all__ attributes
from has_defined_all import *
check("all_defined_foo", all_defined_foo, "all_defined_foo", globals()) #$ prints=all_defined_foo
try:
check("all_defined_bar", all_defined_bar, "all_defined_bar", globals()) #$ SPURIOUS: prints=all_defined_bar
raise Exception("Did not get expected NameError")
except NameError as e:
if "all_defined_bar" in str(e):
print("Got expected NameError:", e)
else:
raise
import has_defined_all # $ imports=has_defined_all as=has_defined_all
check("has_defined_all.all_defined_foo", has_defined_all.all_defined_foo, "all_defined_foo", globals()) #$ prints=all_defined_foo
check("has_defined_all.all_defined_bar", has_defined_all.all_defined_bar, "all_defined_bar", globals()) #$ prints=all_defined_bar
# same check as above, but going through one level of indirection (which can make a difference)
from has_defined_all_indirection import *
check("all_defined_foo_copy", all_defined_foo_copy, "all_defined_foo_copy", globals()) #$ prints=all_defined_foo_copy
try:
check("all_defined_bar_copy", all_defined_bar_copy, "all_defined_bar_copy", globals()) #$ SPURIOUS: prints=all_defined_bar_copy
raise Exception("Did not get expected NameError")
except NameError as e:
if "all_defined_bar_copy" in str(e):
print("Got expected NameError:", e)
else:
raise
# same check as above, but going through one level of indirection (which can make a difference)
import has_defined_all_indirection # $ imports=has_defined_all_indirection as=has_defined_all_indirection
check("has_defined_all_indirection.all_defined_foo_copy", has_defined_all_indirection.all_defined_foo_copy, "all_defined_foo_copy", globals()) #$ prints=all_defined_foo_copy
try:
check("has_defined_all_indirection.all_defined_bar_copy", has_defined_all_indirection.all_defined_bar_copy, "all_defined_bar_copy", globals())
raise Exception("Did not get expected AttributeError")
except AttributeError as e:
if "all_defined_bar_copy" in str(e):
print("Got expected AttributeError:", e)
else:
raise
# check that import * from an __init__ file works
from package.subpackage2 import *
@@ -93,6 +139,25 @@ check("subpackage2_attr", subpackage2_attr, "subpackage2_attr", globals()) #$ pr
from if_then_else import if_then_else_defined
check("if_then_else_defined", if_then_else_defined, "if_defined", globals()) #$ prints=if_defined prints=else_defined_1 prints=else_defined_2
# check that refined definitions are handled correctly
import refined # $ imports=refined as=refined
check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ prints=SOURCE
import if_then_else_refined # $ imports=if_then_else_refined as=if_then_else_refined
check("if_then_else_refined.src", if_then_else_refined.src, if_then_else_refined.src, globals()) #$ prints=SOURCE
import simplistic_reexport # $ imports=simplistic_reexport as=simplistic_reexport
check("simplistic_reexport.bar_attr", simplistic_reexport.bar_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints=bar_attr
check("simplistic_reexport.baz_attr", simplistic_reexport.baz_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints=baz_attr
# check that we don't treat all assignments as being exports
import block_flow_check #$ imports=block_flow_check as=block_flow_check
check("block_flow_check.SOURCE", block_flow_check.SOURCE, block_flow_check.SOURCE, globals())
# show that import resolution is a bit too generous with definitions
import generous_export #$ imports=generous_export as=generous_export
check("generous_export.SOURCE", generous_export.SOURCE, generous_export.SOURCE, globals()) #$ SPURIOUS: prints=SOURCE
exit(__file__)
print()

View File

@@ -0,0 +1,14 @@
from trace import *
enter(__file__)
class SOURCE(object): pass
check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
SOURCE.foo = 42
SOURCE.bar = 43
SOURCE.baz = 44
check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
exit(__file__)

View File

@@ -0,0 +1,19 @@
# we might consider anything imported to also be exported, but this is not the case
from trace import *
enter(__file__)
from bar import bar_attr
check("bar_attr", bar_attr, "bar_attr", globals()) #$ prints=bar_attr
bar_attr = "overwritten"
check("bar_attr", bar_attr, "overwritten", globals()) #$ prints=overwritten
from baz import *
check("baz_attr", baz_attr, "baz_attr", globals()) #$ MISSING: prints=baz_attr
baz_attr = "overwritten"
check("baz_attr", baz_attr, "overwritten", globals()) #$ prints=overwritten
exit(__file__)

View File

@@ -10,6 +10,12 @@
| InsecureProtocol.py:19:1:19:19 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version SSLv2 specified by $@. | InsecureProtocol.py:19:1:19:19 | ControlFlowNode for Attribute() | call to SSL.Context |
| InsecureProtocol.py:23:1:23:43 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version SSLv2 specified by $@. | InsecureProtocol.py:23:1:23:43 | ControlFlowNode for Attribute() | call to ssl.wrap_socket |
| InsecureProtocol.py:24:1:24:35 | ControlFlowNode for SSLContext() | Insecure SSL/TLS protocol version SSLv2 specified by $@. | InsecureProtocol.py:24:1:24:35 | ControlFlowNode for SSLContext() | call to SSLContext |
| import_all_one_file.py:25:14:25:45 | ControlFlowNode for copy_completely_insecure_context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | import_all_one_file.py:9:36:9:67 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_all_one_file.py:25:14:25:45 | ControlFlowNode for copy_completely_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_all_one_file.py:9:36:9:67 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_all_one_file.py:29:14:29:39 | ControlFlowNode for copy_also_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_all_one_file.py:12:30:12:61 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_use.py:13:14:13:40 | ControlFlowNode for completely_insecure_context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | import_def.py:7:31:7:62 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_use.py:13:14:13:40 | ControlFlowNode for completely_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_def.py:7:31:7:62 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_use.py:17:14:17:34 | ControlFlowNode for also_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_def.py:10:25:10:56 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
| pyOpenSSL_fluent.py:18:27:18:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | pyOpenSSL_fluent.py:15:15:15:44 | ControlFlowNode for Attribute() | call to SSL.Context |

View File

@@ -0,0 +1,30 @@
# use to compare alerts without import
import ssl
copy_secure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
copy_secure_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
# this is just to allow us to see how un-altered exports work
copy_completely_insecure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
# and an insecure export that is refined
copy_also_insecure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
copy_also_insecure_context.options |= ssl.OP_NO_TLSv1
import socket
hostname = 'www.python.org'
with socket.create_connection((hostname, 443)) as sock:
with copy_secure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())
with socket.create_connection((hostname, 443)) as sock:
with copy_completely_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())
with socket.create_connection((hostname, 443)) as sock:
with copy_also_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())

View File

@@ -0,0 +1,11 @@
import ssl
secure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
secure_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
# this is just to allow us to see how un-altered exports work
completely_insecure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
# and an insecure export that is refined
also_insecure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
also_insecure_context.options |= ssl.OP_NO_TLSv1

View File

@@ -0,0 +1,18 @@
# check that query works properly with imports
import socket
from import_def import secure_context, completely_insecure_context, also_insecure_context
hostname = 'www.python.org'
with socket.create_connection((hostname, 443)) as sock:
with secure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())
with socket.create_connection((hostname, 443)) as sock:
with completely_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())
with socket.create_connection((hostname, 443)) as sock:
with also_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())