mirror of
https://github.com/github/codeql.git
synced 2025-12-25 05:06:34 +01:00
Will need subsequent PRs fixing up test failures (due to deprecated methods moving around), but other than that everything should be straight-forward.
100 lines
2.5 KiB
Plaintext
100 lines
2.5 KiB
Plaintext
/**
|
|
* @name Modification of parameter with default
|
|
* @description Modifying the default value of a parameter can lead to unexpected
|
|
* results.
|
|
* @kind path-problem
|
|
* @tags reliability
|
|
* maintainability
|
|
* @problem.severity error
|
|
* @sub-severity low
|
|
* @precision high
|
|
* @id py/modification-of-default-value
|
|
*/
|
|
|
|
import python
|
|
import semmle.python.security.Paths
|
|
|
|
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(),
|
|
"Default value"
|