mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Python: straight port of query
The old query uses `pointsTo` to limit the sinks to methods on lists and dictionaries. That constraint is omitted here which could hurt performance.
This commit is contained in:
@@ -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,34 @@
|
||||
/**
|
||||
* 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 {
|
||||
Configuration() { this = "ModificationOfParameterWithDefault" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof BarrierGuard
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for detecting modifications of a parameters default value.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for detecting modifications of a parameters default value.
|
||||
*/
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for detecting modifications of a parameters default value.
|
||||
*/
|
||||
abstract class BarrierGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/** 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 source of remote user input, considered as a flow source.
|
||||
*/
|
||||
class MutableDefaultValue extends Source {
|
||||
boolean nonEmpty;
|
||||
|
||||
MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.asCfgNode().(NameNode).getNode()) }
|
||||
}
|
||||
|
||||
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__"
|
||||
}
|
||||
|
||||
/**
|
||||
* A mutation is considered a flow sink.
|
||||
*/
|
||||
class Mutation extends Sink {
|
||||
Mutation() {
|
||||
exists(AugAssign a | a.getTarget().getAFlowNode() = this.asCfgNode())
|
||||
or
|
||||
exists(Call c, Attribute a | c.getFunc() = a |
|
||||
a.getObject().getAFlowNode() = this.asCfgNode() and
|
||||
not safe_method(a.getName())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,39 @@
|
||||
edges
|
||||
| test.py:2:12:2:12 | empty mutable value | test.py:3:5:3:5 | empty mutable value |
|
||||
| test.py:7:14:7:14 | empty mutable value | test.py:9:14:9:14 | empty mutable value |
|
||||
| test.py:9:5:9:15 | empty mutable value | test.py:10:5:10:5 | empty mutable value |
|
||||
| test.py:9:14:9:14 | empty mutable value | test.py:9:5:9:15 | empty mutable value |
|
||||
| test.py:13:13:13:13 | empty mutable value | test.py:14:5:14:5 | empty mutable value |
|
||||
| test.py:18:14:18:14 | empty mutable value | test.py:19:13:19:13 | empty mutable value |
|
||||
| test.py:19:13:19:13 | empty mutable value | test.py:13:13:13:13 | empty mutable value |
|
||||
| test.py:23:14:23:14 | non-empty mutable value | test.py:24:5:24:5 | non-empty mutable value |
|
||||
| test.py:52:17:52:17 | empty mutable value | test.py:53:5:53:5 | empty mutable value |
|
||||
| test.py:57:26:57:26 | non-empty mutable value | test.py:58:5:58:5 | non-empty mutable value |
|
||||
| test.py:62:35:62:35 | non-empty mutable value | test.py:63:5:63:5 | non-empty mutable value |
|
||||
| test.py:66:21:66:21 | empty mutable value | test.py:67:5:67:5 | empty mutable value |
|
||||
| test.py:71:26:71:26 | empty mutable value | test.py:72:21:72:21 | empty mutable value |
|
||||
| test.py:72:21:72:21 | empty mutable value | test.py:66:21:66:21 | empty mutable value |
|
||||
| test.py:76:19:76:19 | empty mutable value | test.py:78:14:78:14 | empty mutable value |
|
||||
| test.py:78:5:78:15 | empty mutable value | test.py:79:5:79:5 | empty mutable value |
|
||||
| test.py:78:14:78:14 | empty mutable value | test.py:78:5:78:15 | empty mutable value |
|
||||
| test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l |
|
||||
| test.py:13:13:13:13 | ControlFlowNode for l | test.py:14:5:14:5 | ControlFlowNode for l |
|
||||
| test.py:18:14:18:14 | ControlFlowNode for l | test.py:19:13:19:13 | ControlFlowNode for l |
|
||||
| test.py:19:13:19:13 | ControlFlowNode for l | test.py:13:13:13:13 | ControlFlowNode for l |
|
||||
| test.py:23:14:23:14 | ControlFlowNode for l | test.py:24:5:24:5 | ControlFlowNode for l |
|
||||
| test.py:52:17:52:17 | ControlFlowNode for d | test.py:53:5:53:5 | ControlFlowNode for d |
|
||||
| test.py:57:26:57:26 | ControlFlowNode for d | test.py:58:5:58:5 | ControlFlowNode for d |
|
||||
| test.py:62:35:62:35 | ControlFlowNode for d | test.py:63:5:63:5 | ControlFlowNode for d |
|
||||
| test.py:66:21:66:21 | ControlFlowNode for d | test.py:67:5:67:5 | ControlFlowNode for d |
|
||||
| test.py:71:26:71:26 | ControlFlowNode for d | test.py:72:21:72:21 | ControlFlowNode for d |
|
||||
| test.py:72:21:72:21 | ControlFlowNode for d | test.py:66:21:66:21 | ControlFlowNode for d |
|
||||
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:13:13:13:13 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:14:5:14:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:18:14:18:14 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:19:13:19:13 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:23:14:23:14 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:24:5:24:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
|
||||
| test.py:52:17:52:17 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:53:5:53:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:57:26:57:26 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:58:5:58:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:62:35:62:35 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:63:5:63:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:66:21:66:21 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:67:5:67:5 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:71:26:71:26 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
| test.py:72:21:72:21 | ControlFlowNode for d | semmle.label | ControlFlowNode for d |
|
||||
#select
|
||||
| test.py:3:5:3:5 | l | test.py:2:12:2:12 | empty mutable value | test.py:3:5:3:5 | empty mutable value | $@ flows to here and is mutated. | test.py:2:12:2:12 | l | Default value |
|
||||
| test.py:10:5:10:5 | x | test.py:7:14:7:14 | empty mutable value | test.py:10:5:10:5 | empty mutable value | $@ flows to here and is mutated. | test.py:7:14:7:14 | l | Default value |
|
||||
| test.py:14:5:14:5 | l | test.py:18:14:18:14 | empty mutable value | test.py:14:5:14:5 | empty mutable value | $@ flows to here and is mutated. | test.py:18:14:18:14 | l | Default value |
|
||||
| test.py:24:5:24:5 | l | test.py:23:14:23:14 | non-empty mutable value | test.py:24:5:24:5 | non-empty mutable value | $@ flows to here and is mutated. | test.py:23:14:23:14 | l | Default value |
|
||||
| test.py:53:5:53:5 | d | test.py:52:17:52:17 | empty mutable value | test.py:53:5:53:5 | empty mutable value | $@ flows to here and is mutated. | test.py:52:17:52:17 | d | Default value |
|
||||
| test.py:58:5:58:5 | d | test.py:57:26:57:26 | non-empty mutable value | test.py:58:5:58:5 | non-empty mutable value | $@ flows to here and is mutated. | test.py:57:26:57:26 | d | Default value |
|
||||
| test.py:63:5:63:5 | d | test.py:62:35:62:35 | non-empty mutable value | test.py:63:5:63:5 | non-empty mutable value | $@ flows to here and is mutated. | test.py:62:35:62:35 | d | Default value |
|
||||
| test.py:67:5:67:5 | d | test.py:71:26:71:26 | empty mutable value | test.py:67:5:67:5 | empty mutable value | $@ flows to here and is mutated. | test.py:71:26:71:26 | d | Default value |
|
||||
| test.py:79:5:79:5 | x | test.py:76:19:76:19 | empty mutable value | test.py:79:5:79:5 | empty mutable value | $@ flows to here and is mutated. | test.py:76:19:76:19 | d | Default value |
|
||||
| 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:14:5:14:5 | ControlFlowNode for l | test.py:18:14:18:14 | ControlFlowNode for l | test.py:14:5:14:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:18:14:18:14 | ControlFlowNode for l | Default value |
|
||||
| test.py:24:5:24:5 | ControlFlowNode for l | test.py:23:14:23:14 | ControlFlowNode for l | test.py:24:5:24:5 | ControlFlowNode for l | $@ flows to here and is mutated. | test.py:23:14:23:14 | ControlFlowNode for l | Default value |
|
||||
| test.py:53:5:53:5 | ControlFlowNode for d | test.py:52:17:52:17 | ControlFlowNode for d | test.py:53:5:53:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:52:17:52:17 | ControlFlowNode for d | Default value |
|
||||
| test.py:58:5:58:5 | ControlFlowNode for d | test.py:57:26:57:26 | ControlFlowNode for d | test.py:58:5:58:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:57:26:57:26 | ControlFlowNode for d | Default value |
|
||||
| test.py:63:5:63:5 | ControlFlowNode for d | test.py:62:35:62:35 | ControlFlowNode for d | test.py:63:5:63:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:62:35:62:35 | ControlFlowNode for d | Default value |
|
||||
| test.py:67:5:67:5 | ControlFlowNode for d | test.py:71:26:71:26 | ControlFlowNode for d | test.py:67:5:67:5 | ControlFlowNode for d | $@ flows to here and is mutated. | test.py:71:26:71:26 | ControlFlowNode for d | Default value |
|
||||
|
||||
@@ -7,7 +7,7 @@ def simple(l = []):
|
||||
def includes(l = []):
|
||||
x = [0]
|
||||
x.extend(l)
|
||||
x.extend([1]) # FP
|
||||
x.extend([1])
|
||||
return x
|
||||
|
||||
def extends(l):
|
||||
@@ -76,5 +76,5 @@ def dict_deferred_method(d = {}):
|
||||
def dict_includes(d = {}):
|
||||
x = {}
|
||||
x.update(d)
|
||||
x.update({'a': 1}) # FP
|
||||
x.update({'a': 1})
|
||||
return x
|
||||
|
||||
Reference in New Issue
Block a user