mirror of
https://github.com/github/codeql.git
synced 2026-05-01 03:35:13 +02:00
Merge branch 'master' into python-encapsulate-builtins
This commit is contained in:
@@ -15,6 +15,14 @@
|
||||
import python
|
||||
import semmle.python.Comparisons
|
||||
|
||||
/* Holds if the comparison `comp` is of the complex form `a op b op c` and not of
|
||||
* the simple form `a op b`.
|
||||
*/
|
||||
private predicate is_complex(Expr comp) {
|
||||
exists(comp.(Compare).getOp(1))
|
||||
or
|
||||
is_complex(comp.(UnaryExpr).getOperand())
|
||||
}
|
||||
|
||||
/** A test is useless if for every block that it controls there is another test that is at least as
|
||||
* strict and also controls that block.
|
||||
@@ -22,7 +30,7 @@ import semmle.python.Comparisons
|
||||
private predicate useless_test(Comparison comp, ComparisonControlBlock controls, boolean isTrue) {
|
||||
controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and
|
||||
/* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */
|
||||
not exists(controls.getTest().getNode().(Compare).getOp(1))
|
||||
not is_complex(controls.getTest().getNode())
|
||||
}
|
||||
|
||||
private predicate useless_test_ast(AstNode comp, AstNode previous, boolean isTrue) {
|
||||
|
||||
@@ -15,10 +15,7 @@ import semmle.python.web.Http
|
||||
|
||||
|
||||
FunctionObject requestFunction() {
|
||||
exists(ModuleObject req |
|
||||
req.getName() = "requests" and
|
||||
result = req.attr(httpVerbLower())
|
||||
)
|
||||
result = ModuleObject::named("requests").attr(httpVerbLower())
|
||||
}
|
||||
|
||||
/** requests treats None as the default and all other "falsey" values as False */
|
||||
|
||||
@@ -13,25 +13,27 @@
|
||||
import python
|
||||
|
||||
FunctionObject ssl_wrap_socket() {
|
||||
result = any(ModuleObject ssl | ssl.getName() = "ssl").attr("wrap_socket")
|
||||
result = ModuleObject::named("ssl").attr("wrap_socket")
|
||||
}
|
||||
|
||||
ClassObject ssl_Context_class() {
|
||||
result = any(ModuleObject ssl | ssl.getName() = "ssl").attr("SSLContext")
|
||||
result = ModuleObject::named("ssl").attr("SSLContext")
|
||||
}
|
||||
|
||||
CallNode unsafe_call(string method_name) {
|
||||
result = ssl_wrap_socket().getACall() and
|
||||
not exists(result.getArgByName("ssl_version")) and
|
||||
method_name = "deprecated method ssl.wrap_socket"
|
||||
or
|
||||
result = ssl_Context_class().getACall() and
|
||||
not exists(result.getArgByName("protocol")) and
|
||||
not exists(result.getArg(0)) and
|
||||
method_name = "ssl.SSLContext"
|
||||
}
|
||||
|
||||
from CallNode call, string method_name
|
||||
where
|
||||
call = unsafe_call(method_name) and
|
||||
not exists(call.getArgByName("ssl_version"))
|
||||
call = unsafe_call(method_name)
|
||||
select call, "Call to " + method_name + " does not specify a protocol, which may result in an insecure default being used."
|
||||
|
||||
|
||||
|
||||
@@ -34,19 +34,23 @@ string insecure_version_name() {
|
||||
}
|
||||
|
||||
private ModuleObject the_ssl_module() {
|
||||
result = any(ModuleObject m | m.getName() = "ssl")
|
||||
result = ModuleObject::named("ssl")
|
||||
}
|
||||
|
||||
private ModuleObject the_pyOpenSSL_module() {
|
||||
result = any(ModuleObject m | m.getName() = "pyOpenSSL.SSL")
|
||||
result = ModuleObject::named("pyOpenSSL.SSL")
|
||||
}
|
||||
|
||||
/* A syntactic check for cases where points-to analysis cannot infer the presence of
|
||||
* a protocol constant, e.g. if it has been removed in later versions of the `ssl`
|
||||
* library.
|
||||
*/
|
||||
predicate probable_insecure_ssl_constant(CallNode call, string insecure_version) {
|
||||
exists(ControlFlowNode arg | arg = call.getArgByName("ssl_version") |
|
||||
bindingset[named_argument]
|
||||
predicate probable_insecure_ssl_constant(CallNode call, string insecure_version, string named_argument) {
|
||||
exists(ControlFlowNode arg |
|
||||
arg = call.getArgByName(named_argument) or
|
||||
arg = call.getArg(0)
|
||||
|
|
||||
arg.(AttrNode).getObject(insecure_version).refersTo(the_ssl_module())
|
||||
or
|
||||
arg.(NameNode).getId() = insecure_version and
|
||||
@@ -57,26 +61,28 @@ predicate probable_insecure_ssl_constant(CallNode call, string insecure_version)
|
||||
)
|
||||
}
|
||||
|
||||
predicate unsafe_ssl_wrap_socket_call(CallNode call, string method_name, string insecure_version) {
|
||||
predicate unsafe_ssl_wrap_socket_call(CallNode call, string method_name, string insecure_version, string named_argument) {
|
||||
(
|
||||
call = ssl_wrap_socket().getACall() and
|
||||
method_name = "deprecated method ssl.wrap_socket"
|
||||
method_name = "deprecated method ssl.wrap_socket" and
|
||||
named_argument = "ssl_version"
|
||||
or
|
||||
call = ssl_Context_class().getACall() and
|
||||
named_argument = "protocol" and
|
||||
method_name = "ssl.SSLContext"
|
||||
)
|
||||
and
|
||||
insecure_version = insecure_version_name()
|
||||
and
|
||||
(
|
||||
call.getArgByName("ssl_version").refersTo(the_ssl_module().attr(insecure_version))
|
||||
call.getArgByName(named_argument).refersTo(the_ssl_module().attr(insecure_version))
|
||||
or
|
||||
probable_insecure_ssl_constant(call, insecure_version)
|
||||
probable_insecure_ssl_constant(call, insecure_version, named_argument)
|
||||
)
|
||||
}
|
||||
|
||||
ClassObject the_pyOpenSSL_Context_class() {
|
||||
result = any(ModuleObject m | m.getName() = "pyOpenSSL.SSL").attr("Context")
|
||||
result = ModuleObject::named("pyOpenSSL.SSL").attr("Context")
|
||||
}
|
||||
|
||||
predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) {
|
||||
@@ -87,7 +93,7 @@ predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version)
|
||||
|
||||
from CallNode call, string method_name, string insecure_version
|
||||
where
|
||||
unsafe_ssl_wrap_socket_call(call, method_name, insecure_version)
|
||||
unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _)
|
||||
or
|
||||
unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context"
|
||||
select call, "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + "."
|
||||
|
||||
@@ -23,7 +23,7 @@ FunctionObject temporary_name_function(string mod, string function) {
|
||||
function = "tempnam"
|
||||
)
|
||||
) and
|
||||
result = any(ModuleObject m | m.getName() = mod).getAttribute(function)
|
||||
result = ModuleObject::named(mod).attr(function)
|
||||
}
|
||||
|
||||
from Call c, string mod, string function
|
||||
|
||||
@@ -35,12 +35,12 @@ string permissive_permission(int p) {
|
||||
}
|
||||
|
||||
predicate chmod_call(CallNode call, FunctionObject chmod, NumericObject num) {
|
||||
any(ModuleObject os | os.getName() = "os").attr("chmod") = chmod and
|
||||
ModuleObject::named("os").attr("chmod") = chmod and
|
||||
chmod.getACall() = call and call.getArg(1).refersTo(num)
|
||||
}
|
||||
|
||||
predicate open_call(CallNode call, FunctionObject open, NumericObject num) {
|
||||
any(ModuleObject os | os.getName() = "os").attr("open") = open and
|
||||
ModuleObject::named("os").attr("open") = open and
|
||||
open.getACall() = call and call.getArg(2).refersTo(num)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ predicate possible_reflective_name(string name) {
|
||||
or
|
||||
any(ClassObject c).getName() = name
|
||||
or
|
||||
any(ModuleObject m).getName() = name
|
||||
exists(ModuleObject::named(name))
|
||||
or
|
||||
exists(Object::builtin(name))
|
||||
}
|
||||
|
||||
@@ -95,6 +95,14 @@ predicate in_raises_test(Expr e) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if expression has the form of a Python 2 `print >> out, ...` statement */
|
||||
predicate python2_print(Expr e) {
|
||||
e.(BinaryExpr).getLeft().(Name).getId() = "print" and
|
||||
e.(BinaryExpr).getOp() instanceof RShift
|
||||
or
|
||||
python2_print(e.(Tuple).getElt(0))
|
||||
}
|
||||
|
||||
predicate no_effect(Expr e) {
|
||||
not e instanceof StrConst and
|
||||
not ((StrConst)e).isDocString() and
|
||||
@@ -107,7 +115,8 @@ predicate no_effect(Expr e) {
|
||||
not maybe_side_effecting_attribute(sub)
|
||||
) and
|
||||
not in_notebook(e) and
|
||||
not in_raises_test(e)
|
||||
not in_raises_test(e) and
|
||||
not python2_print(e)
|
||||
}
|
||||
|
||||
from ExprStmt stmt
|
||||
|
||||
3
python/ql/src/semmle/python/dataflow/TaintTracking.qll
Executable file
3
python/ql/src/semmle/python/dataflow/TaintTracking.qll
Executable file
@@ -0,0 +1,3 @@
|
||||
/* For compatibility with other language implementations */
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
@@ -51,6 +51,8 @@ ClassObject simple_types(Object obj) {
|
||||
obj.getOrigin() instanceof Module and result = theModuleType()
|
||||
or
|
||||
result.asBuiltin() = obj.asBuiltin().getClass()
|
||||
or
|
||||
obj = unknownValue() and result = theUnknownType()
|
||||
}
|
||||
|
||||
private ClassObject comprehension(Expr e) {
|
||||
|
||||
@@ -125,13 +125,6 @@ abstract class TaintKind extends string {
|
||||
*/
|
||||
predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar) { none() }
|
||||
|
||||
/** Holds if this kind of taint can start from `expr`.
|
||||
* In other words, is `expr` a source of this kind of taint.
|
||||
*/
|
||||
final predicate startsFrom(ControlFlowNode expr) {
|
||||
expr.(TaintSource).isSourceOf(this, _)
|
||||
}
|
||||
|
||||
/** Holds if this kind of taint "taints" `expr`.
|
||||
*/
|
||||
final predicate taints(ControlFlowNode expr) {
|
||||
@@ -196,16 +189,6 @@ class SequenceKind extends CollectionKind {
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
sequence_subscript_taint(tonode, fromnode, this, result)
|
||||
or
|
||||
result = this and
|
||||
(
|
||||
slice(fromnode, tonode) or
|
||||
tonode.(BinaryExprNode).getAnOperand() = fromnode
|
||||
)
|
||||
or
|
||||
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
exists(BinaryExprNode mod |
|
||||
mod = tonode and
|
||||
mod.getOp() instanceof Mod and
|
||||
@@ -213,8 +196,6 @@ class SequenceKind extends CollectionKind {
|
||||
result = this.getItem() and
|
||||
result.getClass() = theStrType()
|
||||
)
|
||||
or
|
||||
result = this and sequence_call(fromnode, tonode)
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
@@ -227,26 +208,42 @@ class SequenceKind extends CollectionKind {
|
||||
|
||||
}
|
||||
|
||||
/* Helper for getTaintForStep() */
|
||||
pragma [noinline]
|
||||
private predicate sequence_subscript_taint(SubscriptNode sub, ControlFlowNode obj, SequenceKind seq, TaintKind key) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
if sub.getNode().getIndex() instanceof Slice then
|
||||
seq = key
|
||||
else
|
||||
key = seq.getItem()
|
||||
|
||||
module SequenceKind {
|
||||
|
||||
predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.(BinaryExprNode).getAnOperand() = fromnode
|
||||
or
|
||||
TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
sequence_call(fromnode, tonode)
|
||||
or
|
||||
sequence_subscript_slice(fromnode, tonode)
|
||||
}
|
||||
|
||||
predicate itemFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
sequence_subscript_index(fromnode, tonode)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* tonode = fromnode[:] */
|
||||
private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
|
||||
exists(Slice all |
|
||||
all = tonode.getIndex().getNode() and
|
||||
not exists(all.getStart()) and not exists(all.getStop()) and
|
||||
tonode.getValue() = fromnode
|
||||
)
|
||||
|
||||
/* Helper for sequence flow steps */
|
||||
pragma [noinline]
|
||||
private predicate sequence_subscript_index(ControlFlowNode obj, SubscriptNode sub) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
not sub.getNode().getIndex() instanceof Slice
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate sequence_subscript_slice(ControlFlowNode obj, SubscriptNode sub) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
sub.getNode().getIndex() instanceof Slice
|
||||
}
|
||||
|
||||
|
||||
/** A taint kind representing a mapping of objects to kinds.
|
||||
* Typically a dict, but can include other mappings.
|
||||
*/
|
||||
@@ -262,20 +259,6 @@ class DictKind extends CollectionKind {
|
||||
result = valueKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = valueKind and
|
||||
tonode.(SubscriptNode).getValue() = fromnode and tonode.isLoad()
|
||||
or
|
||||
result = valueKind and
|
||||
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode
|
||||
or
|
||||
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
result = this and
|
||||
tonode.(CallNode).getFunction().refersTo(theDictType()) and
|
||||
tonode.(CallNode).getArg(0) = fromnode
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "get" and result = valueKind
|
||||
or
|
||||
@@ -291,6 +274,24 @@ class DictKind extends CollectionKind {
|
||||
}
|
||||
|
||||
|
||||
module DictKind {
|
||||
|
||||
predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
tonode.(CallNode).getFunction().refersTo(theDictType()) and
|
||||
tonode.(CallNode).getArg(0) = fromnode
|
||||
}
|
||||
|
||||
predicate valueFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.(SubscriptNode).getValue() = fromnode and tonode.isLoad()
|
||||
or
|
||||
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** A type of sanitizer of untrusted data.
|
||||
* Examples include sanitizers for http responses, for DB access or for shell commands.
|
||||
* Usually a sanitizer can only sanitize data for one particular use.
|
||||
@@ -318,6 +319,18 @@ abstract class Sanitizer extends string {
|
||||
|
||||
}
|
||||
|
||||
/** Hold if `sanitizer` is valid. A sanitizer is valid if there is
|
||||
* a `TaintTracking::Configuration` that declares `sanitizer` or
|
||||
* there are no `TaintTracking::Configuration`s.
|
||||
*/
|
||||
private predicate valid_sanitizer(Sanitizer sanitizer) {
|
||||
not exists(TaintTracking::Configuration c)
|
||||
or
|
||||
exists(DataFlow::Configuration c | c.isSanitizer(sanitizer))
|
||||
or
|
||||
exists(TaintTracking::Configuration c | c.isSanitizer(sanitizer))
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use DataFlowExtension instead.
|
||||
* An extension to taint-flow. For adding library or framework specific flows.
|
||||
* Examples include flow from a request to untrusted part of that request or
|
||||
@@ -584,12 +597,19 @@ private newtype TTaintedNode =
|
||||
n.(TaintSource).isSourceOf(kind, context)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Configuration config, TaintKind kind |
|
||||
taint = TaintFlowImplementation::TTrackedTaint(kind) and
|
||||
config.isSource(n) and context.getDepth() = 0 and
|
||||
kind instanceof GenericFlowType
|
||||
)
|
||||
or
|
||||
TaintFlowImplementation::step(_, taint, context, n) and
|
||||
exists(TaintKind kind |
|
||||
kind = taint.(TaintFlowImplementation::TrackedTaint).getKind()
|
||||
or
|
||||
kind = taint.(TaintFlowImplementation::TrackedAttribute).getKind(_) |
|
||||
not exists(Sanitizer sanitizer |
|
||||
valid_sanitizer(sanitizer) and
|
||||
sanitizer.sanitizingNode(kind, n)
|
||||
)
|
||||
)
|
||||
@@ -839,26 +859,37 @@ library module TaintFlowImplementation {
|
||||
or
|
||||
call_taint_step(fromnode, totaint, tocontext, tonode)
|
||||
or
|
||||
fromnode.getNode().(DataFlowNode).getASuccessorNode() = tonode and
|
||||
fromnode.getContext() = tocontext and
|
||||
totaint = fromnode.getTrackedValue()
|
||||
or
|
||||
exists(CallNode call |
|
||||
fromnode.getNode().(DataFlowNode).getAReturnSuccessorNode(call) = tonode and
|
||||
fromnode.getContext() = tocontext.getCallee(call) and
|
||||
exists(DataFlowNode fromnodenode |
|
||||
fromnodenode = fromnode.getNode() and
|
||||
(
|
||||
not exists(TaintTracking::Configuration c)
|
||||
or
|
||||
exists(DataFlow::Configuration c | c.isExtension(fromnodenode))
|
||||
or
|
||||
exists(TaintTracking::Configuration c | c.isExtension(fromnodenode))
|
||||
)
|
||||
|
|
||||
fromnodenode.getASuccessorNode() = tonode and
|
||||
fromnode.getContext() = tocontext and
|
||||
totaint = fromnode.getTrackedValue()
|
||||
)
|
||||
or
|
||||
exists(CallNode call |
|
||||
fromnode.getNode().(DataFlowNode).getACalleeSuccessorNode(call) = tonode and
|
||||
fromnode.getContext().getCallee(call) = tocontext and
|
||||
totaint = fromnode.getTrackedValue()
|
||||
)
|
||||
or
|
||||
exists(TaintKind tokind |
|
||||
fromnode.getNode().(DataFlowNode).getASuccessorNode(fromnode.getTaintKind(), tokind) = tonode and
|
||||
totaint = fromnode.getTrackedValue().toKind(tokind) and
|
||||
tocontext = fromnode.getContext()
|
||||
or
|
||||
exists(CallNode call |
|
||||
fromnodenode.getAReturnSuccessorNode(call) = tonode and
|
||||
fromnode.getContext() = tocontext.getCallee(call) and
|
||||
totaint = fromnode.getTrackedValue()
|
||||
)
|
||||
or
|
||||
exists(CallNode call |
|
||||
fromnodenode.getACalleeSuccessorNode(call) = tonode and
|
||||
fromnode.getContext().getCallee(call) = tocontext and
|
||||
totaint = fromnode.getTrackedValue()
|
||||
)
|
||||
or
|
||||
exists(TaintKind tokind |
|
||||
fromnodenode.getASuccessorNode(fromnode.getTaintKind(), tokind) = tonode and
|
||||
totaint = fromnode.getTrackedValue().toKind(tokind) and
|
||||
tocontext = fromnode.getContext()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(TaintKind tokind |
|
||||
@@ -867,6 +898,22 @@ library module TaintFlowImplementation {
|
||||
tocontext = fromnode.getContext()
|
||||
)
|
||||
or
|
||||
exists(SequenceKind fromkind |
|
||||
fromkind = fromnode.getTaintKind() and
|
||||
tocontext = fromnode.getContext() |
|
||||
totaint = fromnode.getTrackedValue() and SequenceKind::flowStep(fromnode.getNode(), tonode)
|
||||
or
|
||||
totaint = fromnode.getTrackedValue().toKind(fromkind.getItem()) and SequenceKind::itemFlowStep(fromnode.getNode(), tonode)
|
||||
)
|
||||
or
|
||||
exists(DictKind fromkind |
|
||||
fromkind = fromnode.getTaintKind() and
|
||||
tocontext = fromnode.getContext() |
|
||||
totaint = fromnode.getTrackedValue() and DictKind::flowStep(fromnode.getNode(), tonode)
|
||||
or
|
||||
totaint = fromnode.getTrackedValue().toKind(fromkind.getValue()) and DictKind::valueFlowStep(fromnode.getNode(), tonode)
|
||||
)
|
||||
or
|
||||
exists(TaintFlow flow, TaintKind tokind |
|
||||
flow.additionalFlowStep(fromnode.getNode(), fromnode.getTaintKind(), tonode, tokind) and
|
||||
totaint = fromnode.getTrackedValue().toKind(tokind) and
|
||||
@@ -1038,8 +1085,18 @@ library module TaintFlowImplementation {
|
||||
prev.(DataFlowVariable).getASuccessorVariable() = var
|
||||
)
|
||||
or
|
||||
origin.getNode().(DataFlowNode).getASuccessorVariable() = var and
|
||||
context = origin.getContext()
|
||||
exists(DataFlowNode originnode |
|
||||
originnode = origin.getNode() and
|
||||
(
|
||||
not exists(TaintTracking::Configuration c)
|
||||
or
|
||||
exists(DataFlow::Configuration c | c.isExtension(originnode))
|
||||
or
|
||||
exists(TaintTracking::Configuration c | c.isExtension(originnode))
|
||||
) and
|
||||
originnode.getASuccessorVariable() = var and
|
||||
context = origin.getContext()
|
||||
)
|
||||
or
|
||||
exists(TrackedTaint taint, EssaVariable prev |
|
||||
tainted_var(prev, context, origin) and
|
||||
@@ -1062,6 +1119,7 @@ library module TaintFlowImplementation {
|
||||
exists(TaintKind kind |
|
||||
kind = origin.getTaintKind() and
|
||||
not exists(Sanitizer san |
|
||||
valid_sanitizer(san) |
|
||||
san.sanitizingDefinition(kind, def)
|
||||
or
|
||||
san.sanitizingNode(kind, def.(EssaNodeDefinition).getDefiningNode())
|
||||
@@ -1184,6 +1242,7 @@ library module TaintFlowImplementation {
|
||||
exists(TaintKind kind |
|
||||
kind = origin.getTaintKind() |
|
||||
not exists(FunctionObject callee, Sanitizer sanitizer |
|
||||
valid_sanitizer(sanitizer) and
|
||||
callee.getACall() = call.getCall() and
|
||||
sanitizer.sanitizingCall(kind, callee)
|
||||
)
|
||||
@@ -1197,11 +1256,12 @@ library module TaintFlowImplementation {
|
||||
var = test.getInput() and
|
||||
tainted_var(var, context, origin) and
|
||||
not exists(Sanitizer sanitizer |
|
||||
valid_sanitizer(sanitizer) and
|
||||
sanitizer.sanitizingEdge(kind, test)
|
||||
)
|
||||
|
|
||||
not Filters::isinstance(test.getTest(), _, var.getSourceVariable().getAUse()) and
|
||||
not test.getTest() = var.getSourceVariable().getAUse()
|
||||
not boolean_filter(test.getTest(), var.getSourceVariable().getAUse())
|
||||
or
|
||||
exists(ControlFlowNode c, ClassObject cls |
|
||||
Filters::isinstance(test.getTest(), c, var.getSourceVariable().getAUse())
|
||||
@@ -1212,10 +1272,42 @@ library module TaintFlowImplementation {
|
||||
test.getSense() = false and not kind.getClass().getAnImproperSuperType() = cls
|
||||
)
|
||||
or
|
||||
test.getTest() = var.getSourceVariable().getAUse() and kind.booleanValue() = test.getSense()
|
||||
test.getSense() = test_evaluates(test.getTest(), var.getSourceVariable().getAUse(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the operand of a unary `not` expression. */
|
||||
private ControlFlowNode not_operand(ControlFlowNode expr) {
|
||||
expr.(UnaryExprNode).getNode().getOp() instanceof Not and
|
||||
result = expr.(UnaryExprNode).getOperand()
|
||||
}
|
||||
|
||||
/** Holds if `test` is the test in a branch and `use` is that test
|
||||
* with all the `not` prefixes removed.
|
||||
*/
|
||||
private predicate boolean_filter(ControlFlowNode test, ControlFlowNode use) {
|
||||
any(PyEdgeRefinement ref).getTest() = test and
|
||||
(
|
||||
use = test
|
||||
or
|
||||
exists(ControlFlowNode notuse |
|
||||
boolean_filter(test, notuse) and
|
||||
use = not_operand(notuse)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the boolean value that `test` evaluates to when `use` is tainted with `kind`
|
||||
* and `test` and `use` are part of a test in a branch.
|
||||
*/
|
||||
private boolean test_evaluates(ControlFlowNode test, ControlFlowNode use, TaintKind kind) {
|
||||
boolean_filter(_, use) and
|
||||
kind.taints(use) and
|
||||
test = use and result = kind.booleanValue()
|
||||
or
|
||||
result = test_evaluates(not_operand(test), use, kind).booleanNot()
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
predicate tainted_argument(ArgumentRefinement def, CallContext context, TaintedNode origin) {
|
||||
tainted_var(def.getInput(), context, origin)
|
||||
@@ -1246,6 +1338,7 @@ library module TaintFlowImplementation {
|
||||
var = uniphi.getInput() and
|
||||
tainted_var(var, context, origin) and
|
||||
not exists(Sanitizer sanitizer |
|
||||
valid_sanitizer(sanitizer) and
|
||||
sanitizer.sanitizingSingleEdge(kind, uniphi)
|
||||
)
|
||||
)
|
||||
@@ -1438,6 +1531,109 @@ class CallContext extends TCallContext {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Data flow module providing an interface compatible with
|
||||
* the other language implementations.
|
||||
*/
|
||||
module DataFlow {
|
||||
|
||||
class FlowType = TaintKind;
|
||||
|
||||
/** Generic taint kind, source and sink classes for convenience and
|
||||
* compatibility with other language libraries
|
||||
*/
|
||||
|
||||
class Node = ControlFlowNode;
|
||||
|
||||
class PathNode = TaintedNode;
|
||||
|
||||
class Extension = DataFlowExtension::DataFlowNode;
|
||||
|
||||
abstract class Configuration extends string {
|
||||
|
||||
bindingset[this]
|
||||
Configuration() { this = this }
|
||||
|
||||
abstract predicate isSource(Node source);
|
||||
|
||||
abstract predicate isSink(Node sink);
|
||||
|
||||
predicate isSanitizer(Sanitizer sanitizer) { none() }
|
||||
|
||||
predicate isExtension(Extension extension) { none() }
|
||||
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) {
|
||||
this.isSource(source.getNode()) and
|
||||
this.isSink(sink.getNode()) and
|
||||
source.getTaintKind() instanceof GenericFlowType and
|
||||
sink.getTaintKind() instanceof GenericFlowType
|
||||
}
|
||||
|
||||
predicate hasFlow(Node source, Node sink) {
|
||||
exists(PathNode psource, PathNode psink |
|
||||
psource.getNode() = source and
|
||||
psink.getNode() = sink and
|
||||
this.isSource(source) and
|
||||
this.isSink(sink) and
|
||||
this.hasFlowPath(psource, psink)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class GenericFlowType extends DataFlow::FlowType {
|
||||
|
||||
GenericFlowType() {
|
||||
this = "Generic taint kind" and
|
||||
exists(DataFlow::Configuration c)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module TaintTracking {
|
||||
|
||||
class Source = TaintSource;
|
||||
|
||||
class Sink = TaintSink;
|
||||
|
||||
class PathSource = TaintedPathSource;
|
||||
|
||||
class PathSink = TaintedPathSink;
|
||||
|
||||
class Extension = DataFlowExtension::DataFlowNode;
|
||||
|
||||
abstract class Configuration extends string {
|
||||
|
||||
bindingset[this]
|
||||
Configuration() { this = this }
|
||||
|
||||
abstract predicate isSource(Source source);
|
||||
|
||||
abstract predicate isSink(Sink sink);
|
||||
|
||||
predicate isSanitizer(Sanitizer sanitizer) { none() }
|
||||
|
||||
predicate isExtension(Extension extension) { none() }
|
||||
|
||||
predicate hasFlowPath(PathSource source, PathSink sink) {
|
||||
this.isSource(source.getNode()) and
|
||||
this.isSink(sink.getNode()) and
|
||||
source.flowsTo(sink)
|
||||
}
|
||||
|
||||
predicate hasFlow(Source source, Sink sink) {
|
||||
this.isSource(source) and
|
||||
this.isSink(sink) and
|
||||
source.flowsToSink(sink)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
pragma [noinline]
|
||||
private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) {
|
||||
dictnode.(DictNode).getAValue() = itemnode
|
||||
|
||||
@@ -83,7 +83,7 @@ class ShellCommand extends TaintSink {
|
||||
or
|
||||
exists(CallNode call |
|
||||
call.getAnArg() = this and
|
||||
call.getFunction().refersTo(any(ModuleObject commands | commands.getName() = "commands"))
|
||||
call.getFunction().refersTo(ModuleObject::named("commands"))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ private predicate json_subscript_taint(SubscriptNode sub, ControlFlowNode obj, E
|
||||
|
||||
private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
|
||||
exists(FunctionObject json_loads |
|
||||
any(ModuleObject json | json.getName() = "json").attr("loads") = json_loads and
|
||||
ModuleObject::named("json").attr("loads") = json_loads and
|
||||
json_loads.getACall() = tonode and tonode.getArg(0) = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
*
|
||||
* This should be considered an advance feature. Modifying the points-to analysis
|
||||
* can cause queries to give strange and misleading results, if not done with care.
|
||||
*
|
||||
* WARNING:
|
||||
* This module interacts with the internals of points-to analysis and
|
||||
* the classes here are more likely to change than the rest of the library.
|
||||
*/
|
||||
|
||||
import python
|
||||
@@ -34,6 +38,18 @@ abstract class CustomPointsToOriginFact extends CustomPointsToFact {
|
||||
|
||||
}
|
||||
|
||||
/* Custom points-to fact with inferred class */
|
||||
abstract class CustomPointsToObjectFact extends CustomPointsToFact {
|
||||
|
||||
abstract predicate pointsTo(Object value);
|
||||
|
||||
override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
this.pointsTo(value) and cls = simple_types(value) and origin = this and context.appliesTo(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** INTERNAL -- Do not use */
|
||||
abstract class CustomPointsToAttribute extends Object {
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ ModuleObject theBottleModule() {
|
||||
|
||||
/** The bottle.Bottle class */
|
||||
ClassObject theBottleClass() {
|
||||
result = ModuleObject::named("bottle").attr("Bottle")
|
||||
result = theBottleModule().attr("Bottle")
|
||||
}
|
||||
|
||||
/** Holds if `route` is routed to `func`
|
||||
|
||||
@@ -12,7 +12,7 @@ class DjangoDbCursor extends DbCursor {
|
||||
}
|
||||
|
||||
private Object theDjangoConnectionObject() {
|
||||
any(ModuleObject m | m.getName() = "django.db").attr("connection") = result
|
||||
ModuleObject::named("django.db").attr("connection") = result
|
||||
}
|
||||
|
||||
/** A kind of taint source representing sources of django cursor objects.
|
||||
@@ -38,7 +38,7 @@ class DjangoDbCursorSource extends DbConnectionSource {
|
||||
|
||||
|
||||
ClassObject theDjangoRawSqlClass() {
|
||||
result = any(ModuleObject m | m.getName() = "django.db.models.expressions").attr("RawSQL")
|
||||
result = ModuleObject::named("django.db.models.expressions").attr("RawSQL")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,7 @@ import semmle.python.web.Http
|
||||
class DjangoModel extends ClassObject {
|
||||
|
||||
DjangoModel() {
|
||||
any(ModuleObject m | m.getName() = "django.db.models").attr("Model") = this.getAnImproperSuperType()
|
||||
ModuleObject::named("django.db.models").attr("Model") = this.getAnImproperSuperType()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ private class DjangoFunctionBasedViewRequestArgument extends DjangoRequestSource
|
||||
private class DjangoView extends ClassObject {
|
||||
|
||||
DjangoView() {
|
||||
any(ModuleObject m | m.getName() = "django.views.generic").attr("View") = this.getAnImproperSuperType()
|
||||
ModuleObject::named("django.views.generic").attr("View") = this.getAnImproperSuperType()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ class DjangoClassBasedViewRequestArgument extends DjangoRequestSource {
|
||||
/* Function based views */
|
||||
predicate url_dispatch(CallNode call, ControlFlowNode regex, FunctionObject view) {
|
||||
exists(FunctionObject url |
|
||||
any(ModuleObject m | m.getName() = "django.conf.urls").attr("url") = url and
|
||||
ModuleObject::named("django.conf.urls").attr("url") = url and
|
||||
url.getArgumentForCall(call, 0) = regex and
|
||||
url.getArgumentForCall(call, 1).refersTo(view)
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ class DjangoResponse extends TaintKind {
|
||||
}
|
||||
|
||||
private ClassObject theDjangoHttpResponseClass() {
|
||||
result = any(ModuleObject m | m.getName() = "django.http.response").attr("HttpResponse") and
|
||||
result = ModuleObject::named("django.http.response").attr("HttpResponse") and
|
||||
not result = theDjangoHttpRedirectClass()
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class DjangoResponseWrite extends TaintSink {
|
||||
DjangoResponseWrite() {
|
||||
exists(AttrNode meth, CallNode call |
|
||||
call.getFunction() = meth and
|
||||
any(DjangoResponse repsonse).taints(meth.getObject("write")) and
|
||||
any(DjangoResponse response).taints(meth.getObject("write")) and
|
||||
this = call.getArg(0)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import python
|
||||
|
||||
FunctionObject redirect() {
|
||||
result = any(ModuleObject m | m.getName() = "django.shortcuts").attr("redirect")
|
||||
result = ModuleObject::named("django.shortcuts").attr("redirect")
|
||||
}
|
||||
|
||||
ClassObject theDjangoHttpRedirectClass() {
|
||||
result = any(ModuleObject m | m.getName() = "django.http.response").attr("HttpResponseRedirectBase")
|
||||
result = ModuleObject::named("django.http.response").attr("HttpResponseRedirectBase")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import semmle.python.web.Http
|
||||
|
||||
/** The falcon API class */
|
||||
ClassObject theFalconAPIClass() {
|
||||
result = ModuleObject::named("falcon").getAttribute("API")
|
||||
result = ModuleObject::named("falcon").attr("API")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import semmle.python.web.Http
|
||||
|
||||
/** The flask module */
|
||||
ModuleObject theFlaskModule() {
|
||||
result = any(ModuleObject m | m.getName() = "flask")
|
||||
result = ModuleObject::named("flask")
|
||||
}
|
||||
|
||||
/** The flask app class */
|
||||
@@ -13,7 +13,7 @@ ClassObject theFlaskClass() {
|
||||
|
||||
/** The flask MethodView class */
|
||||
ClassObject theFlaskMethodViewClass() {
|
||||
result = any(ModuleObject m | m.getName() = "flask.views").attr("MethodView")
|
||||
result = ModuleObject::named("flask.views").attr("MethodView")
|
||||
}
|
||||
|
||||
ClassObject theFlaskReponseClass() {
|
||||
|
||||
@@ -12,7 +12,7 @@ class PyramidRequest extends BaseWebobRequest {
|
||||
}
|
||||
|
||||
override ClassObject getClass() {
|
||||
result = any(ModuleObject m | m.getName() = "pyramid.request").attr("Request")
|
||||
result = ModuleObject::named("pyramid.request").attr("Request")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import python
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
private ClassObject theTornadoRequestHandlerClass() {
|
||||
result = any(ModuleObject m | m.getName() = "tornado.web").attr("RequestHandler")
|
||||
result = ModuleObject::named("tornado.web").attr("RequestHandler")
|
||||
}
|
||||
|
||||
ClassObject aTornadoRequestHandlerClass() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import python
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
private ClassObject theTurboGearsControllerClass() {
|
||||
result = ModuleObject::named("tg").getAttribute("TGController")
|
||||
result = ModuleObject::named("tg").attr("TGController")
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class TurboGearsControllerMethod extends Function {
|
||||
(
|
||||
decorator.(CallNode).getFunction().(NameNode).getId() = "expose"
|
||||
or
|
||||
decorator.refersTo(_, ModuleObject::named("tg").getAttribute("expose"), _)
|
||||
decorator.refersTo(_, ModuleObject::named("tg").attr("expose"), _)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import python
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
private ClassObject theTwistedHttpRequestClass() {
|
||||
result = any(ModuleObject m | m.getName() = "twisted.web.http").attr("Request")
|
||||
result = ModuleObject::named("twisted.web.http").attr("Request")
|
||||
}
|
||||
|
||||
private ClassObject theTwistedHttpResourceClass() {
|
||||
result = any(ModuleObject m | m.getName() = "twisted.web.resource").attr("Resource")
|
||||
result = ModuleObject::named("twisted.web.resource").attr("Resource")
|
||||
}
|
||||
|
||||
ClassObject aTwistedRequestHandlerClass() {
|
||||
|
||||
@@ -45,7 +45,7 @@ class WebobRequest extends BaseWebobRequest {
|
||||
}
|
||||
|
||||
override ClassObject getClass() {
|
||||
result = any(ModuleObject m | m.getName() = "webob.request").attr("Request")
|
||||
result = ModuleObject::named("webob.request").attr("Request")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user