зеркало из https://github.com/github/codeql.git
Merge pull request #6557 from yoff/python/port-modification-of-default-value
Python: port modification of default value
This commit is contained in:
Коммит
4a16be2cba
|
@ -0,0 +1,2 @@
|
|||
lgtm,codescanning
|
||||
* Updated _Modification of parameter with default_ (`py/modification-of-default-value`) query to use the new data flow library instead of the old taint tracking library and to remove the use of points-to analysis. You may see differences in the results found by the query, but overall this change should result in a more robust and accurate analysis.
|
|
@ -12,88 +12,12 @@
|
|||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.Paths
|
||||
import semmle.python.functions.ModificationOfParameterWithDefault
|
||||
import DataFlow::PathGraph
|
||||
|
||||
predicate safe_method(string name) {
|
||||
name = "count" or
|
||||
name = "index" or
|
||||
name = "copy" or
|
||||
name = "get" or
|
||||
name = "has_key" or
|
||||
name = "items" or
|
||||
name = "keys" or
|
||||
name = "values" or
|
||||
name = "iteritems" or
|
||||
name = "iterkeys" or
|
||||
name = "itervalues" or
|
||||
name = "__contains__" or
|
||||
name = "__getitem__" or
|
||||
name = "__getattribute__"
|
||||
}
|
||||
|
||||
/** Gets the truthiness (non emptyness) of the default of `p` if that value is mutable */
|
||||
private boolean mutableDefaultValue(Parameter p) {
|
||||
exists(Dict d | p.getDefault() = d |
|
||||
exists(d.getAKey()) and result = true
|
||||
or
|
||||
not exists(d.getAKey()) and result = false
|
||||
)
|
||||
or
|
||||
exists(List l | p.getDefault() = l |
|
||||
exists(l.getAnElt()) and result = true
|
||||
or
|
||||
not exists(l.getAnElt()) and result = false
|
||||
)
|
||||
}
|
||||
|
||||
class NonEmptyMutableValue extends TaintKind {
|
||||
NonEmptyMutableValue() { this = "non-empty mutable value" }
|
||||
}
|
||||
|
||||
class EmptyMutableValue extends TaintKind {
|
||||
EmptyMutableValue() { this = "empty mutable value" }
|
||||
|
||||
override boolean booleanValue() { result = false }
|
||||
}
|
||||
|
||||
class MutableDefaultValue extends TaintSource {
|
||||
boolean nonEmpty;
|
||||
|
||||
MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) }
|
||||
|
||||
override string toString() { result = "mutable default value" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
nonEmpty = false and kind instanceof EmptyMutableValue
|
||||
or
|
||||
nonEmpty = true and kind instanceof NonEmptyMutableValue
|
||||
}
|
||||
}
|
||||
|
||||
private ClassValue mutable_class() {
|
||||
result = Value::named("list") or
|
||||
result = Value::named("dict")
|
||||
}
|
||||
|
||||
class Mutation extends TaintSink {
|
||||
Mutation() {
|
||||
exists(AugAssign a | a.getTarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(Call c, Attribute a | c.getFunc() = a |
|
||||
a.getObject().getAFlowNode() = this and
|
||||
not safe_method(a.getName()) and
|
||||
this.(ControlFlowNode).pointsTo().getClass() = mutable_class()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof EmptyMutableValue
|
||||
or
|
||||
kind instanceof NonEmptyMutableValue
|
||||
}
|
||||
}
|
||||
|
||||
from TaintedPathSource src, TaintedPathSink sink
|
||||
where src.flowsTo(sink)
|
||||
select sink.getSink(), src, sink, "$@ flows to here and is mutated.", src.getSource(),
|
||||
from
|
||||
ModificationOfParameterWithDefault::Configuration config, DataFlow::PathNode source,
|
||||
DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "$@ flows to here and is mutated.", source.getNode(),
|
||||
"Default value"
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Provides a data-flow configuration for detecting modifications of a parameters default value.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `ModificationOfParameterWithDefault::Configuration` is needed, otherwise
|
||||
* `ModificationOfParameterWithDefaultCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
/**
|
||||
* Provides a data-flow configuration for detecting modifications of a parameters default value.
|
||||
*/
|
||||
module ModificationOfParameterWithDefault {
|
||||
import ModificationOfParameterWithDefaultCustomizations::ModificationOfParameterWithDefault
|
||||
|
||||
/**
|
||||
* A data-flow configuration for detecting modifications of a parameters default value.
|
||||
*/
|
||||
class Configuration extends DataFlow::Configuration {
|
||||
/** Record whether the default value being tracked is non-empty. */
|
||||
boolean nonEmptyDefault;
|
||||
|
||||
Configuration() {
|
||||
nonEmptyDefault in [true, false] and
|
||||
this = "ModificationOfParameterWithDefault:" + nonEmptyDefault.toString()
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.(Source).isNonEmpty() = nonEmptyDefault
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
// if we are tracking a non-empty default, then it is ok to modify empty values,
|
||||
// so our tracking ends at those.
|
||||
nonEmptyDefault = true and node instanceof MustBeEmpty
|
||||
or
|
||||
// if we are tracking a empty default, then it is ok to modify non-empty values,
|
||||
// so our tracking ends at those.
|
||||
nonEmptyDefault = false and node instanceof MustBeNonEmpty
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* modifications of a parameters default value, as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "command injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module ModificationOfParameterWithDefault {
|
||||
/**
|
||||
* A data flow source for detecting modifications of a parameters default value,
|
||||
* that is a default value for some parameter.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node {
|
||||
/** Result is true if the default value is non-empty for this source and false if not. */
|
||||
abstract boolean isNonEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for detecting modifications of a parameters default value,
|
||||
* that is a node representing a modification.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for detecting modifications of a parameters default value
|
||||
* should determine if the node (which is perhaps about to be modified)
|
||||
* can be the default value or not.
|
||||
*
|
||||
* In this query we do not track the default value exactly, but rather wheter
|
||||
* it is empty or not (see `Source`).
|
||||
*
|
||||
* This is the extension point for determining that a node must be empty and
|
||||
* therefor is allowed to be modified if the tracked default value is non-empty.
|
||||
*/
|
||||
abstract class MustBeEmpty extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for detecting modifications of a parameters default value
|
||||
* should determine if the node (which is perhaps about to be modified)
|
||||
* can be the default value or not.
|
||||
*
|
||||
* In this query we do not track the default value exactly, but rather wheter
|
||||
* it is empty or not (see `Source`).
|
||||
*
|
||||
* This is the extension point for determining that a node must be non-empty
|
||||
* and therefor is allowed to be modified if the tracked default value is empty.
|
||||
*/
|
||||
abstract class MustBeNonEmpty extends DataFlow::Node { }
|
||||
|
||||
/** Gets the truthiness (non emptyness) of the default of `p` if that value is mutable */
|
||||
private boolean mutableDefaultValue(Parameter p) {
|
||||
exists(Dict d | p.getDefault() = d |
|
||||
exists(d.getAKey()) and result = true
|
||||
or
|
||||
not exists(d.getAKey()) and result = false
|
||||
)
|
||||
or
|
||||
exists(List l | p.getDefault() = l |
|
||||
exists(l.getAnElt()) and result = true
|
||||
or
|
||||
not exists(l.getAnElt()) and result = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A mutable default value for a parameter, considered as a flow source.
|
||||
*/
|
||||
class MutableDefaultValue extends Source {
|
||||
boolean nonEmpty;
|
||||
|
||||
MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.asCfgNode().(NameNode).getNode()) }
|
||||
|
||||
override boolean isNonEmpty() { result = nonEmpty }
|
||||
}
|
||||
|
||||
/**
|
||||
* A name of a list function that modifies the list.
|
||||
* See https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
|
||||
*/
|
||||
string list_modifying_method() {
|
||||
result in ["append", "extend", "insert", "remove", "pop", "clear", "sort", "reverse"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A name of a dict function that modifies the dict.
|
||||
* See https://docs.python.org/3/library/stdtypes.html#dict
|
||||
*/
|
||||
string dict_modifying_method() { result in ["clear", "pop", "popitem", "setdefault", "update"] }
|
||||
|
||||
/**
|
||||
* A mutation of the default value is a flow sink.
|
||||
*
|
||||
* Syntactic constructs that modify a list are:
|
||||
* - s[i] = x
|
||||
* - s[i:j] = t
|
||||
* - del s[i:j]
|
||||
* - s[i:j:k] = t
|
||||
* - del s[i:j:k]
|
||||
* - s += t
|
||||
* - s *= n
|
||||
* See https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types
|
||||
*
|
||||
* Syntactic constructs that modify a dictionary are:
|
||||
* - d[key] = value
|
||||
* - del d[key]
|
||||
* - d |= other
|
||||
* See https://docs.python.org/3/library/stdtypes.html#dict
|
||||
*
|
||||
* These are all covered by:
|
||||
* - assignment to a subscript (includes slices)
|
||||
* - deletion of a subscript
|
||||
* - augmented assignment to the value
|
||||
*/
|
||||
class Mutation extends Sink {
|
||||
Mutation() {
|
||||
// assignment to a subscript (includes slices)
|
||||
exists(DefinitionNode d | d.(SubscriptNode).getObject() = this.asCfgNode())
|
||||
or
|
||||
// deletion of a subscript
|
||||
exists(DeletionNode d | d.getTarget().(SubscriptNode).getObject() = this.asCfgNode())
|
||||
or
|
||||
// augmented assignment to the value
|
||||
exists(AugAssign a | a.getTarget().getAFlowNode() = this.asCfgNode())
|
||||
or
|
||||
// modifying function call
|
||||
exists(DataFlow::CallCfgNode c, DataFlow::AttrRead a | c.getFunction() = a |
|
||||
a.getObject() = this and
|
||||
a.getAttributeName() in [list_modifying_method(), dict_modifying_method()]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// This to reimplement some of the functionality of the DataFlow::BarrierGuard
|
||||
private import semmle.python.essa.SsaCompute
|
||||
|
||||
/**
|
||||
* A data-flow node that is known to be either truthy or falsey.
|
||||
*
|
||||
* It handles the cases `if x` and `if not x`.
|
||||
*
|
||||
* For example, in the following code, `this` will be the `x` that is printed,
|
||||
* which we will know is truthy:
|
||||
*
|
||||
* ```py
|
||||
* if x:
|
||||
* print(x)
|
||||
* ```
|
||||
*/
|
||||
private class MustBe extends DataFlow::Node {
|
||||
boolean truthy;
|
||||
|
||||
MustBe() {
|
||||
exists(DataFlow::GuardNode guard, NameNode guarded, boolean branch |
|
||||
// case: if x
|
||||
guard = guarded and
|
||||
branch = truthy
|
||||
or
|
||||
// case: if not x
|
||||
guard.(UnaryExprNode).getNode().getOp() instanceof Not and
|
||||
guarded = guard.(UnaryExprNode).getOperand() and
|
||||
branch = truthy.booleanNot()
|
||||
|
|
||||
// guard controls this
|
||||
guard.controlsBlock(this.asCfgNode().getBasicBlock(), branch) and
|
||||
// there is a definition tying the guarded value to this
|
||||
exists(EssaDefinition def |
|
||||
AdjacentUses::useOfDef(def, this.asCfgNode()) and
|
||||
AdjacentUses::useOfDef(def, guarded)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Simple guard detecting truthy values. */
|
||||
private class MustBeTruthy extends MustBe, MustBeNonEmpty {
|
||||
MustBeTruthy() { truthy = true }
|
||||
}
|
||||
|
||||
/** Simple guard detecting falsey values. */
|
||||
private class MustBeFalsey extends MustBe, MustBeEmpty {
|
||||
MustBeFalsey() { truthy = false }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
edges
|
||||
| test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l |
|
||||
| test.py:7:11:7:11 | ControlFlowNode for l | test.py:8:5:8:5 | ControlFlowNode for l |
|
||||
| test.py:12:14:12:14 | ControlFlowNode for l | test.py:13:9:13:9 | ControlFlowNode for l |
|
||||
| test.py:17:15:17:15 | ControlFlowNode for l | test.py:18:5:18:5 | ControlFlowNode for l |
|
||||
| test.py:22:15:22:15 | ControlFlowNode for l | test.py:23:5:23:5 | ControlFlowNode for l |
|
||||
| test.py:27:12:27:12 | ControlFlowNode for l | test.py:28:5:28:5 | ControlFlowNode for l |
|
||||
| test.py:38:13:38:13 | ControlFlowNode for l | test.py:39:5:39:5 | ControlFlowNode for l |
|
||||
| test.py:43:14:43:14 | ControlFlowNode for l | test.py:44:13:44:13 | ControlFlowNode for l |
|
||||
| test.py:44:13:44:13 | ControlFlowNode for l | test.py:38:13:38:13 | ControlFlowNode for l |
|
||||
| test.py:48:14:48:14 | ControlFlowNode for l | test.py:49:5:49:5 | ControlFlowNode for l |
|
||||
| test.py:53:10:53:10 | ControlFlowNode for d | test.py:54:5:54:5 | ControlFlowNode for d |
|
||||
| test.py:58:19:58:19 | ControlFlowNode for d | test.py:59:5:59:5 | ControlFlowNode for d |
|
||||
| test.py:63:28:63:28 | ControlFlowNode for d | test.py:64:5:64:5 | ControlFlowNode for d |
|
||||
| test.py:67:14:67:14 | ControlFlowNode for d | test.py:68:5:68:5 | ControlFlowNode for d |
|
||||
| test.py:72:19:72:19 | ControlFlowNode for d | test.py:73:14:73:14 | ControlFlowNode for d |
|
||||
| test.py:73:14:73:14 | ControlFlowNode for d | test.py:67:14:67:14 | ControlFlowNode for d |
|
||||
| test.py:77:17:77:17 | ControlFlowNode for d | test.py:78:5:78:5 | ControlFlowNode for d |
|
||||
| test.py:82:26:82:26 | ControlFlowNode for d | test.py:83:5:83:5 | ControlFlowNode for d |
|
||||
| test.py:87:35:87:35 | ControlFlowNode for d | test.py:88:5:88:5 | ControlFlowNode for d |
|
||||
| test.py:91:21:91:21 | ControlFlowNode for d | test.py:92:5:92:5 | ControlFlowNode for d |
|
||||
| test.py:96:26:96:26 | ControlFlowNode for d | test.py:97:21:97:21 | ControlFlowNode for d |
|
||||
| test.py:97:21:97:21 | ControlFlowNode for d | test.py:91:21:91:21 | ControlFlowNode for d |
|
||||
| test.py:108:14:108:14 | ControlFlowNode for d | test.py:109:9:109:9 | ControlFlowNode for d |
|
||||
| test.py:113:20:113:20 | ControlFlowNode for d | test.py:115:5:115:5 | ControlFlowNode for d |
|
||||
| test.py:119:29:119:29 | ControlFlowNode for d | test.py:121:5:121:5 | ControlFlowNode for d |
|
||||
| test.py:124:15:124:15 | ControlFlowNode for l | test.py:128:9:128:9 | ControlFlowNode for l |
|
||||
| test.py:131:23:131:23 | ControlFlowNode for l | test.py:135:9:135:9 | ControlFlowNode for l |
|
||||
| test.py:138:15:138:15 | ControlFlowNode for l | test.py:140:9:140:9 | ControlFlowNode for l |
|
||||
| test.py:145:23:145:23 | ControlFlowNode for l | test.py:147:9:147:9 | ControlFlowNode for l |
|
||||
nodes
|
||||
| test.py:2:12:2:12 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:3:5:3:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:7:11:7:11 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:8:5:8:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:12:14:12:14 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:13:9:13:9 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:17:15:17:15 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:18:5:18:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:22:15:22:15 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:23:5:23:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:27:12:27:12 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:28:5:28:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:38:13:38:13 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:39:5:39:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:43:14:43:14 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:44:13:44:13 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:48:14:48:14 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:49:5:49:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:53:10:53:10 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:54:5:54:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:58:19:58:19 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:59:5:59:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:63:28:63:28 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:64:5:64:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:67:14:67:14 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:68:5:68:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:72:19:72:19 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:73:14:73:14 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:77:17:77:17 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:78:5:78:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:82:26:82:26 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:83:5:83:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:87:35:87:35 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:88:5:88:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:91:21:91:21 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:92:5:92:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:96:26:96:26 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:97:21:97:21 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:108:14:108:14 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:109:9:109:9 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:113:20:113:20 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:115:5:115:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:119:29:119:29 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:121:5:121:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:124:15:124:15 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:128:9:128:9 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:131:23:131:23 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:135:9:135:9 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:138:15:138:15 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:140:9:140:9 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:145:23:145:23 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:147:9:147:9 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
subpaths
|
||||
#select
|
||||
| test.py:3:5:3:5 | ControlFlowNode for l | test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:2:12:2:12 | ControlFlowNode for l | Default value |
|
||||
| test.py:8:5:8:5 | ControlFlowNode for l | test.py:7:11:7:11 | ControlFlowNode for l | test.py:8:5:8:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:7:11:7:11 | ControlFlowNode for l | Default value |
|
||||
| test.py:13:9:13:9 | ControlFlowNode for l | test.py:12:14:12:14 | ControlFlowNode for l | test.py:13:9:13:9 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:12:14:12:14 | ControlFlowNode for l | Default value |
|
||||
| test.py:18:5:18:5 | ControlFlowNode for l | test.py:17:15:17:15 | ControlFlowNode for l | test.py:18:5:18:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:17:15:17:15 | ControlFlowNode for l | Default value |
|
||||
| test.py:23:5:23:5 | ControlFlowNode for l | test.py:22:15:22:15 | ControlFlowNode for l | test.py:23:5:23:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:22:15:22:15 | ControlFlowNode for l | Default value |
|
||||
| test.py:28:5:28:5 | ControlFlowNode for l | test.py:27:12:27:12 | ControlFlowNode for l | test.py:28:5:28:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:27:12:27:12 | ControlFlowNode for l | Default value |
|
||||
| test.py:39:5:39:5 | ControlFlowNode for l | test.py:43:14:43:14 | ControlFlowNode for l | test.py:39:5:39:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:43:14:43:14 | ControlFlowNode for l | Default value |
|
||||
| test.py:49:5:49:5 | ControlFlowNode for l | test.py:48:14:48:14 | ControlFlowNode for l | test.py:49:5:49:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:48:14:48:14 | ControlFlowNode for l | Default value |
|
||||
| test.py:54:5:54:5 | ControlFlowNode for d | test.py:53:10:53:10 | ControlFlowNode for d | test.py:54:5:54:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:53:10:53:10 | ControlFlowNode for d | Default value |
|
||||
| test.py:59:5:59:5 | ControlFlowNode for d | test.py:58:19:58:19 | ControlFlowNode for d | test.py:59:5:59:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:58:19:58:19 | ControlFlowNode for d | Default value |
|
||||
| test.py:64:5:64:5 | ControlFlowNode for d | test.py:63:28:63:28 | ControlFlowNode for d | test.py:64:5:64:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:63:28:63:28 | ControlFlowNode for d | Default value |
|
||||
| test.py:68:5:68:5 | ControlFlowNode for d | test.py:72:19:72:19 | ControlFlowNode for d | test.py:68:5:68:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:72:19:72:19 | ControlFlowNode for d | Default value |
|
||||
| test.py:78:5:78:5 | ControlFlowNode for d | test.py:77:17:77:17 | ControlFlowNode for d | test.py:78:5:78:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:77:17:77:17 | ControlFlowNode for d | Default value |
|
||||
| test.py:83:5:83:5 | ControlFlowNode for d | test.py:82:26:82:26 | ControlFlowNode for d | test.py:83:5:83:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:82:26:82:26 | ControlFlowNode for d | Default value |
|
||||
| test.py:88:5:88:5 | ControlFlowNode for d | test.py:87:35:87:35 | ControlFlowNode for d | test.py:88:5:88:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:87:35:87:35 | ControlFlowNode for d | Default value |
|
||||
| test.py:92:5:92:5 | ControlFlowNode for d | test.py:96:26:96:26 | ControlFlowNode for d | test.py:92:5:92:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:96:26:96:26 | ControlFlowNode for d | Default value |
|
||||
| test.py:109:9:109:9 | ControlFlowNode for d | test.py:108:14:108:14 | ControlFlowNode for d | test.py:109:9:109:9 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:108:14:108:14 | ControlFlowNode for d | Default value |
|
||||
| test.py:115:5:115:5 | ControlFlowNode for d | test.py:113:20:113:20 | ControlFlowNode for d | test.py:115:5:115:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:113:20:113:20 | ControlFlowNode for d | Default value |
|
||||
| test.py:121:5:121:5 | ControlFlowNode for d | test.py:119:29:119:29 | ControlFlowNode for d | test.py:121:5:121:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:119:29:119:29 | ControlFlowNode for d | Default value |
|
||||
| test.py:128:9:128:9 | ControlFlowNode for l | test.py:124:15:124:15 | ControlFlowNode for l | test.py:128:9:128:9 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:124:15:124:15 | ControlFlowNode for l | Default value |
|
||||
| test.py:135:9:135:9 | ControlFlowNode for l | test.py:131:23:131:23 | ControlFlowNode for l | test.py:135:9:135:9 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:131:23:131:23 | ControlFlowNode for l | Default value |
|
||||
| test.py:140:9:140:9 | ControlFlowNode for l | test.py:138:15:138:15 | ControlFlowNode for l | test.py:140:9:140:9 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:138:15:138:15 | ControlFlowNode for l | Default value |
|
||||
| test.py:147:9:147:9 | ControlFlowNode for l | test.py:145:23:145:23 | ControlFlowNode for l | test.py:147:9:147:9 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:145:23:145:23 | ControlFlowNode for l | Default value |
|
|
@ -0,0 +1 @@
|
|||
Functions/ModificationOfParameterWithDefault.ql
|
|
@ -0,0 +1,150 @@
|
|||
# Not OK
|
||||
def simple(l = [0]):
|
||||
l[0] = 1 #$ modification=l
|
||||
return l
|
||||
|
||||
# Not OK
|
||||
def slice(l = [0]):
|
||||
l[0:1] = 1 #$ modification=l
|
||||
return l
|
||||
|
||||
# Not OK
|
||||
def list_del(l = [0]):
|
||||
del l[0] #$ modification=l
|
||||
return l
|
||||
|
||||
# Not OK
|
||||
def append_op(l = []):
|
||||
l += [1, 2, 3] #$ modification=l
|
||||
return l
|
||||
|
||||
# Not OK
|
||||
def repeat_op(l = [0]):
|
||||
l *= 3 #$ modification=l
|
||||
return l
|
||||
|
||||
# Not OK
|
||||
def append(l = []):
|
||||
l.append(1) #$ modification=l
|
||||
return l
|
||||
|
||||
# OK
|
||||
def includes(l = []):
|
||||
x = [0]
|
||||
x.extend(l)
|
||||
x.extend([1])
|
||||
return x
|
||||
|
||||
def extends(l):
|
||||
l.extend([1]) #$ modification=l
|
||||
return l
|
||||
|
||||
# Not OK
|
||||
def deferred(l = []):
|
||||
extends(l)
|
||||
return l
|
||||
|
||||
# Not OK
|
||||
def nonempty(l = [5]):
|
||||
l.append(1) #$ modification=l
|
||||
return l
|
||||
|
||||
# Not OK
|
||||
def dict(d = {}):
|
||||
d['a'] = 1 #$ modification=d
|
||||
return d
|
||||
|
||||
# Not OK
|
||||
def dict_nonempty(d = {'a': 1}):
|
||||
d['a'] = 2 #$ modification=d
|
||||
return d
|
||||
|
||||
# OK
|
||||
def dict_nonempty_nochange(d = {'a': 1}):
|
||||
d['a'] = 1 #$ SPURIOUS: modification=d
|
||||
return d
|
||||
|
||||
def modifies(d):
|
||||
d['a'] = 1 #$ modification=d
|
||||
return d
|
||||
|
||||
# Not OK
|
||||
def dict_deferred(d = {}):
|
||||
modifies(d)
|
||||
return d
|
||||
|
||||
# Not OK
|
||||
def dict_method(d = {}):
|
||||
d.update({'a': 1}) #$ modification=d
|
||||
return d
|
||||
|
||||
# Not OK
|
||||
def dict_method_nonempty(d = {'a': 1}):
|
||||
d.update({'a': 2}) #$ modification=d
|
||||
return d
|
||||
|
||||
# OK
|
||||
def dict_method_nonempty_nochange(d = {'a': 1}):
|
||||
d.update({'a': 1}) #$ SPURIOUS:modification=d
|
||||
return d
|
||||
|
||||
def modifies_method(d):
|
||||
d.update({'a': 1}) #$ modification=d
|
||||
return d
|
||||
|
||||
# Not OK
|
||||
def dict_deferred_method(d = {}):
|
||||
modifies_method(d)
|
||||
return d
|
||||
|
||||
# OK
|
||||
def dict_includes(d = {}):
|
||||
x = {}
|
||||
x.update(d)
|
||||
x.update({'a': 1})
|
||||
return x
|
||||
|
||||
# Not OK
|
||||
def dict_del(d = {'a': 1}):
|
||||
del d['a'] #$ modification=d
|
||||
return d
|
||||
|
||||
# Not OK
|
||||
def dict_update_op(d = {}):
|
||||
x = {'a': 1}
|
||||
d |= x #$ modification=d
|
||||
return d
|
||||
|
||||
# OK
|
||||
def dict_update_op_nochange(d = {}):
|
||||
x = {}
|
||||
d |= x #$ SPURIOUS: modification=d
|
||||
return d
|
||||
|
||||
def sanitizer(l = []):
|
||||
if l:
|
||||
l.append(1)
|
||||
else:
|
||||
l.append(1) #$ modification=l
|
||||
return l
|
||||
|
||||
def sanitizer_negated(l = [1]):
|
||||
if not l:
|
||||
l.append(1)
|
||||
else:
|
||||
l.append(1) #$ modification=l
|
||||
return l
|
||||
|
||||
def sanitizer(l = []):
|
||||
if not l:
|
||||
l.append(1) #$ modification=l
|
||||
else:
|
||||
l.append(1)
|
||||
return l
|
||||
|
||||
def sanitizer_negated(l = [1]):
|
||||
if l:
|
||||
l.append(1) #$ modification=l
|
||||
else:
|
||||
l.append(1)
|
||||
return l
|
|
@ -0,0 +1,24 @@
|
|||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.python.functions.ModificationOfParameterWithDefault
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
class ModificationOfParameterWithDefaultTest extends InlineExpectationsTest {
|
||||
ModificationOfParameterWithDefaultTest() { this = "ModificationOfParameterWithDefaultTest" }
|
||||
|
||||
override string getARelevantTag() { result = "modification" }
|
||||
|
||||
predicate relevant_node(DataFlow::Node sink) {
|
||||
exists(ModificationOfParameterWithDefault::Configuration cfg | cfg.hasFlowTo(sink))
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node n | relevant_node(n) |
|
||||
n.getLocation() = location and
|
||||
tag = "modification" and
|
||||
value = prettyNode(n) and
|
||||
element = n.toString()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,24 +1,44 @@
|
|||
edges
|
||||
| functions_test.py:39:9:39:9 | empty mutable value | functions_test.py:40:5:40:5 | empty mutable value |
|
||||
| functions_test.py:133:15:133:15 | empty mutable value | functions_test.py:134:5:134:5 | empty mutable value |
|
||||
| functions_test.py:151:25:151:25 | empty mutable value | functions_test.py:152:5:152:5 | empty mutable value |
|
||||
| functions_test.py:154:21:154:21 | empty mutable value | functions_test.py:155:5:155:5 | empty mutable value |
|
||||
| functions_test.py:157:27:157:27 | empty mutable value | functions_test.py:158:25:158:25 | empty mutable value |
|
||||
| functions_test.py:157:27:157:27 | empty mutable value | functions_test.py:159:21:159:21 | empty mutable value |
|
||||
| functions_test.py:158:25:158:25 | empty mutable value | functions_test.py:151:25:151:25 | empty mutable value |
|
||||
| functions_test.py:159:21:159:21 | empty mutable value | functions_test.py:154:21:154:21 | empty mutable value |
|
||||
| functions_test.py:175:28:175:28 | non-empty mutable value | functions_test.py:179:9:179:9 | non-empty mutable value |
|
||||
| functions_test.py:175:28:175:28 | non-empty mutable value | functions_test.py:181:9:181:9 | non-empty mutable value |
|
||||
| functions_test.py:188:18:188:18 | non-empty mutable value | functions_test.py:189:28:189:28 | non-empty mutable value |
|
||||
| functions_test.py:189:28:189:28 | non-empty mutable value | functions_test.py:175:28:175:28 | non-empty mutable value |
|
||||
| functions_test.py:191:18:191:18 | non-empty mutable value | functions_test.py:192:28:192:28 | non-empty mutable value |
|
||||
| functions_test.py:192:28:192:28 | non-empty mutable value | functions_test.py:175:28:175:28 | non-empty mutable value |
|
||||
| functions_test.py:39:9:39:9 | ControlFlowNode for x | functions_test.py:40:5:40:5 | ControlFlowNode for x |
|
||||
| functions_test.py:133:15:133:15 | ControlFlowNode for x | functions_test.py:134:5:134:5 | ControlFlowNode for x |
|
||||
| functions_test.py:151:25:151:25 | ControlFlowNode for x | functions_test.py:152:5:152:5 | ControlFlowNode for x |
|
||||
| functions_test.py:154:21:154:21 | ControlFlowNode for x | functions_test.py:155:5:155:5 | ControlFlowNode for x |
|
||||
| functions_test.py:157:27:157:27 | ControlFlowNode for y | functions_test.py:158:25:158:25 | ControlFlowNode for y |
|
||||
| functions_test.py:157:27:157:27 | ControlFlowNode for y | functions_test.py:159:21:159:21 | ControlFlowNode for y |
|
||||
| functions_test.py:158:25:158:25 | ControlFlowNode for y | functions_test.py:151:25:151:25 | ControlFlowNode for x |
|
||||
| functions_test.py:159:21:159:21 | ControlFlowNode for y | functions_test.py:154:21:154:21 | ControlFlowNode for x |
|
||||
| functions_test.py:179:28:179:28 | ControlFlowNode for x | functions_test.py:183:9:183:9 | ControlFlowNode for x |
|
||||
| functions_test.py:179:28:179:28 | ControlFlowNode for x | functions_test.py:185:9:185:9 | ControlFlowNode for x |
|
||||
| functions_test.py:192:18:192:18 | ControlFlowNode for x | functions_test.py:193:28:193:28 | ControlFlowNode for x |
|
||||
| functions_test.py:193:28:193:28 | ControlFlowNode for x | functions_test.py:179:28:179:28 | ControlFlowNode for x |
|
||||
| functions_test.py:195:18:195:18 | ControlFlowNode for x | functions_test.py:196:28:196:28 | ControlFlowNode for x |
|
||||
| functions_test.py:196:28:196:28 | ControlFlowNode for x | functions_test.py:179:28:179:28 | ControlFlowNode for x |
|
||||
nodes
|
||||
| functions_test.py:39:9:39:9 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:40:5:40:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:133:15:133:15 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:134:5:134:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:151:25:151:25 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:152:5:152:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:154:21:154:21 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:155:5:155:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:157:27:157:27 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| functions_test.py:158:25:158:25 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| functions_test.py:159:21:159:21 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
|
||||
| functions_test.py:179:28:179:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:183:9:183:9 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:185:9:185:9 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:192:18:192:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:193:28:193:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:195:18:195:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| functions_test.py:196:28:196:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
subpaths
|
||||
#select
|
||||
| functions_test.py:40:5:40:5 | x | functions_test.py:39:9:39:9 | empty mutable value | functions_test.py:40:5:40:5 | empty mutable value | $@ flows to here and is mutated. | functions_test.py:39:9:39:9 | x | Default value |
|
||||
| functions_test.py:134:5:134:5 | x | functions_test.py:133:15:133:15 | empty mutable value | functions_test.py:134:5:134:5 | empty mutable value | $@ flows to here and is mutated. | functions_test.py:133:15:133:15 | x | Default value |
|
||||
| functions_test.py:152:5:152:5 | x | functions_test.py:157:27:157:27 | empty mutable value | functions_test.py:152:5:152:5 | empty mutable value | $@ flows to here and is mutated. | functions_test.py:157:27:157:27 | y | Default value |
|
||||
| functions_test.py:155:5:155:5 | x | functions_test.py:157:27:157:27 | empty mutable value | functions_test.py:155:5:155:5 | empty mutable value | $@ flows to here and is mutated. | functions_test.py:157:27:157:27 | y | Default value |
|
||||
| functions_test.py:179:9:179:9 | x | functions_test.py:188:18:188:18 | non-empty mutable value | functions_test.py:179:9:179:9 | non-empty mutable value | $@ flows to here and is mutated. | functions_test.py:188:18:188:18 | x | Default value |
|
||||
| functions_test.py:179:9:179:9 | x | functions_test.py:191:18:191:18 | non-empty mutable value | functions_test.py:179:9:179:9 | non-empty mutable value | $@ flows to here and is mutated. | functions_test.py:191:18:191:18 | x | Default value |
|
||||
| functions_test.py:181:9:181:9 | x | functions_test.py:188:18:188:18 | non-empty mutable value | functions_test.py:181:9:181:9 | non-empty mutable value | $@ flows to here and is mutated. | functions_test.py:188:18:188:18 | x | Default value |
|
||||
| functions_test.py:181:9:181:9 | x | functions_test.py:191:18:191:18 | non-empty mutable value | functions_test.py:181:9:181:9 | non-empty mutable value | $@ flows to here and is mutated. | functions_test.py:191:18:191:18 | x | Default value |
|
||||
| functions_test.py:40:5:40:5 | ControlFlowNode for x | functions_test.py:39:9:39:9 | ControlFlowNode for x | functions_test.py:40:5:40:5 | ControlFlowNode for x | $@ flows to here and is mutated. | functions_test.py:39:9:39:9 | ControlFlowNode for x | Default value |
|
||||
| functions_test.py:134:5:134:5 | ControlFlowNode for x | functions_test.py:133:15:133:15 | ControlFlowNode for x | functions_test.py:134:5:134:5 | ControlFlowNode for x | $@ flows to here and is mutated. | functions_test.py:133:15:133:15 | ControlFlowNode for x | Default value |
|
||||
| functions_test.py:152:5:152:5 | ControlFlowNode for x | functions_test.py:157:27:157:27 | ControlFlowNode for y | functions_test.py:152:5:152:5 | ControlFlowNode for x | $@ flows to here and is mutated. | functions_test.py:157:27:157:27 | ControlFlowNode for y | Default value |
|
||||
| functions_test.py:155:5:155:5 | ControlFlowNode for x | functions_test.py:157:27:157:27 | ControlFlowNode for y | functions_test.py:155:5:155:5 | ControlFlowNode for x | $@ flows to here and is mutated. | functions_test.py:157:27:157:27 | ControlFlowNode for y | Default value |
|
||||
| functions_test.py:183:9:183:9 | ControlFlowNode for x | functions_test.py:192:18:192:18 | ControlFlowNode for x | functions_test.py:183:9:183:9 | ControlFlowNode for x | $@ flows to here and is mutated. | functions_test.py:192:18:192:18 | ControlFlowNode for x | Default value |
|
||||
| functions_test.py:183:9:183:9 | ControlFlowNode for x | functions_test.py:195:18:195:18 | ControlFlowNode for x | functions_test.py:183:9:183:9 | ControlFlowNode for x | $@ flows to here and is mutated. | functions_test.py:195:18:195:18 | ControlFlowNode for x | Default value |
|
||||
| functions_test.py:185:9:185:9 | ControlFlowNode for x | functions_test.py:192:18:192:18 | ControlFlowNode for x | functions_test.py:185:9:185:9 | ControlFlowNode for x | $@ flows to here and is mutated. | functions_test.py:192:18:192:18 | ControlFlowNode for x | Default value |
|
||||
| functions_test.py:185:9:185:9 | ControlFlowNode for x | functions_test.py:195:18:195:18 | ControlFlowNode for x | functions_test.py:185:9:185:9 | ControlFlowNode for x | $@ flows to here and is mutated. | functions_test.py:195:18:195:18 | ControlFlowNode for x | Default value |
|
||||
|
|
|
@ -163,11 +163,15 @@ def guarded_modification(z=[]):
|
|||
z.append(0)
|
||||
return z
|
||||
|
||||
def issue1143(expr, param=[]):
|
||||
if not param:
|
||||
return result
|
||||
for i in param:
|
||||
param.remove(i) # Mutation here
|
||||
# This function causes a discrepancy between the
|
||||
# Python 2 and 3 versions of the analysis.
|
||||
# We comment it out until we have resoved the issue.
|
||||
#
|
||||
# def issue1143(expr, param=[]):
|
||||
# if not param:
|
||||
# return result
|
||||
# for i in param:
|
||||
# param.remove(i) # Mutation here
|
||||
|
||||
|
||||
# Type guarding of modification of parameter with default:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
semmle-extractor-options: --max-import-depth=1 --dont-split-graph
|
Загрузка…
Ссылка в новой задаче