Merge pull request #6557 from yoff/python/port-modification-of-default-value

Python: port modification of default value
This commit is contained in:
Rasmus Wriedt Larsen 2021-09-21 10:12:12 +02:00 коммит произвёл GitHub
Родитель eaf05305ff 8ea7a28a77
Коммит 4a16be2cba
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 580 добавлений и 110 удалений

Просмотреть файл

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