mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
Merge branch 'master' into python-modernise-statements
This commit is contained in:
@@ -20,8 +20,8 @@ import Expressions.CallArgs
|
||||
|
||||
from Call call, ClassObject cls, string name, FunctionObject init
|
||||
where
|
||||
illegally_named_parameter(call, cls, name)
|
||||
and init = get_function_or_initializer(cls)
|
||||
illegally_named_parameter_objectapi(call, cls, name)
|
||||
and init = get_function_or_initializer_objectapi(cls)
|
||||
select
|
||||
call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init, init.getQualifiedName()
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ import Expressions.CallArgs
|
||||
from Call call, ClassObject cls, string too, string should, int limit, FunctionObject init
|
||||
where
|
||||
(
|
||||
too_many_args(call, cls, limit) and too = "too many arguments" and should = "no more than "
|
||||
too_many_args_objectapi(call, cls, limit) and too = "too many arguments" and should = "no more than "
|
||||
or
|
||||
too_few_args(call, cls, limit) and too = "too few arguments" and should = "no fewer than "
|
||||
) and init = get_function_or_initializer(cls)
|
||||
too_few_args_objectapi(call, cls, limit) and too = "too few arguments" and should = "no fewer than "
|
||||
) and init = get_function_or_initializer_objectapi(cls)
|
||||
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init, init.getQualifiedName()
|
||||
|
||||
@@ -19,7 +19,7 @@ predicate doesnt_reraise(ExceptStmt ex) {
|
||||
}
|
||||
|
||||
predicate catches_base_exception(ExceptStmt ex) {
|
||||
ex.getType().refersTo(theBaseExceptionType())
|
||||
ex.getType().pointsTo(ClassValue::baseException())
|
||||
or
|
||||
not exists(ex.getType())
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ predicate no_comment(ExceptStmt ex) {
|
||||
}
|
||||
|
||||
predicate non_local_control_flow(ExceptStmt ex) {
|
||||
ex.getType().refersTo(theStopIterationType())
|
||||
ex.getType().pointsTo(ClassValue::stopIteration())
|
||||
}
|
||||
|
||||
predicate try_has_normal_exit(Try try) {
|
||||
@@ -64,32 +64,29 @@ predicate subscript(Stmt s) {
|
||||
s.(Delete).getATarget() instanceof Subscript
|
||||
}
|
||||
|
||||
predicate encode_decode(Expr ex, ClassObject type) {
|
||||
predicate encode_decode(Call ex, ClassValue type) {
|
||||
exists(string name |
|
||||
ex.(Call).getFunc().(Attribute).getName() = name |
|
||||
name = "encode" and type = Object::builtin("UnicodeEncodeError")
|
||||
ex.getFunc().(Attribute).getName() = name |
|
||||
name = "encode" and type = ClassValue::unicodeEncodeError()
|
||||
or
|
||||
name = "decode" and type = Object::builtin("UnicodeDecodeError")
|
||||
name = "decode" and type = ClassValue::unicodeDecodeError()
|
||||
)
|
||||
}
|
||||
|
||||
predicate small_handler(ExceptStmt ex, Stmt s, ClassObject type) {
|
||||
predicate small_handler(ExceptStmt ex, Stmt s, ClassValue type) {
|
||||
not exists(ex.getTry().getStmt(1)) and
|
||||
s = ex.getTry().getStmt(0) and
|
||||
ex.getType().refersTo(type)
|
||||
ex.getType().pointsTo(type)
|
||||
}
|
||||
|
||||
/** Holds if this exception handler is sufficiently small in scope to not need a comment
|
||||
* as to what it is doing.
|
||||
*/
|
||||
predicate focussed_handler(ExceptStmt ex) {
|
||||
exists(Stmt s, ClassObject type |
|
||||
exists(Stmt s, ClassValue type |
|
||||
small_handler(ex, s, type) |
|
||||
subscript(s) and type.getAnImproperSuperType() = theLookupErrorType()
|
||||
subscript(s) and type.getASuperType() = ClassValue::lookupError()
|
||||
or
|
||||
attribute_access(s) and type = theAttributeErrorType()
|
||||
attribute_access(s) and type = ClassValue::attributeError()
|
||||
or
|
||||
s.(ExprStmt).getValue() instanceof Name and type = theNameErrorType()
|
||||
s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError()
|
||||
or
|
||||
encode_decode(s.(ExprStmt).getValue(), type)
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ import python
|
||||
import Raising
|
||||
import Exceptions.NotImplemented
|
||||
|
||||
from Raise r, ClassObject t
|
||||
where type_or_typeof(r, t, _) and not t.isLegalExceptionType() and not t.failedInference() and not use_of_not_implemented_in_raise(r, _)
|
||||
from Raise r, ClassValue t
|
||||
where type_or_typeof(r, t, _) and not t.isLegalExceptionType() and not t.failedInference(_) and not use_of_not_implemented_in_raise(r, _)
|
||||
select r, "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead."
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import python
|
||||
|
||||
/** Holds if `notimpl` refers to `NotImplemented` or `NotImplemented()` in the `raise` statement */
|
||||
predicate use_of_not_implemented_in_raise(Raise raise, Expr notimpl) {
|
||||
notimpl.refersTo(Object::notImplemented()) and
|
||||
notimpl.pointsTo(Value::named("NotImplemented")) and
|
||||
(
|
||||
notimpl = raise.getException() or
|
||||
notimpl = raise.getException().(Call).getFunc()
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import python
|
||||
|
||||
/** Whether the raise statement 'r' raises 'type' from origin 'orig' */
|
||||
predicate type_or_typeof(Raise r, ClassObject type, AstNode orig) {
|
||||
predicate type_or_typeof(Raise r, ClassValue type, AstNode orig) {
|
||||
exists(Expr exception |
|
||||
exception = r.getRaised() |
|
||||
exception.refersTo(type, _, orig)
|
||||
exception.pointsTo(type, orig)
|
||||
or
|
||||
not exists(ClassObject exc_type | exception.refersTo(exc_type)) and
|
||||
not type = theTypeType() and // First value is an unknown exception type
|
||||
exception.refersTo(_, type, orig)
|
||||
not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and
|
||||
not type = ClassValue::type() and // First value is an unknown exception type
|
||||
exists(Value val | exception.pointsTo(val, orig) |
|
||||
val.getClass() = type
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
|
||||
import python
|
||||
|
||||
from Raise r, AstNode origin
|
||||
where r.getException().refersTo(_, theTupleType(), origin) and
|
||||
from Raise r, Value v, AstNode origin
|
||||
where r.getException().pointsTo(v, origin) and
|
||||
v.getClass() = ClassValue::tuple() and
|
||||
major_version() = 2 /* Raising a tuple is a type error in Python 3, so is handled by the IllegalRaise query. */
|
||||
|
||||
select r, "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", origin, "a tuple"
|
||||
@@ -2,7 +2,7 @@ import python
|
||||
|
||||
import Testing.Mox
|
||||
|
||||
private int varargs_length(Call call) {
|
||||
private int varargs_length_objectapi(Call call) {
|
||||
not exists(call.getStarargs()) and result = 0
|
||||
or
|
||||
exists(TupleObject t |
|
||||
@@ -13,67 +13,131 @@ private int varargs_length(Call call) {
|
||||
result = count(call.getStarargs().(List).getAnElt())
|
||||
}
|
||||
|
||||
private int varargs_length(Call call) {
|
||||
not exists(call.getStarargs()) and result = 0
|
||||
or
|
||||
exists(TupleValue t |
|
||||
call.getStarargs().pointsTo(t) |
|
||||
result = t.length()
|
||||
)
|
||||
or
|
||||
result = count(call.getStarargs().(List).getAnElt())
|
||||
}
|
||||
|
||||
/** Gets a keyword argument that is not a keyword-only parameter. */
|
||||
private Keyword not_keyword_only_arg(Call call, FunctionObject func) {
|
||||
private Keyword not_keyword_only_arg_objectapi(Call call, FunctionObject func) {
|
||||
func.getACall().getNode() = call and
|
||||
result = call.getAKeyword() and
|
||||
not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg()
|
||||
}
|
||||
|
||||
/** Gets a keyword argument that is not a keyword-only parameter. */
|
||||
private Keyword not_keyword_only_arg(Call call, FunctionValue func) {
|
||||
func.getACall().getNode() = call and
|
||||
result = call.getAKeyword() and
|
||||
not func.getScope().getAKeywordOnlyArg().getId() = result.getArg()
|
||||
}
|
||||
|
||||
/** Gets the count of arguments that are passed as positional parameters even if they
|
||||
* are named in the call.
|
||||
* This is the sum of the number of positional arguments, the number of elements in any explicit tuple passed as *arg
|
||||
* plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs).
|
||||
*/
|
||||
|
||||
private int positional_arg_count_for_call(Call call, Object callable) {
|
||||
call = get_a_call(callable).getNode() and
|
||||
private int positional_arg_count_for_call_objectapi(Call call, Object callable) {
|
||||
call = get_a_call_objectapi(callable).getNode() and
|
||||
exists(int positional_keywords |
|
||||
exists(FunctionObject func | func = get_function_or_initializer(callable) |
|
||||
exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) |
|
||||
not func.getFunction().hasKwArg() and
|
||||
positional_keywords = count(not_keyword_only_arg(call, func))
|
||||
positional_keywords = count(not_keyword_only_arg_objectapi(call, func))
|
||||
or
|
||||
func.getFunction().hasKwArg() and positional_keywords = 0
|
||||
)
|
||||
|
|
||||
result = count(call.getAnArg()) + varargs_length(call) + positional_keywords
|
||||
result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the count of arguments that are passed as positional parameters even if they
|
||||
* are named in the call.
|
||||
* This is the sum of the number of positional arguments, the number of elements in any explicit tuple passed as *arg
|
||||
* plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs).
|
||||
*/
|
||||
|
||||
private int positional_arg_count_for_call(Call call, Value callable) {
|
||||
call = get_a_call(callable).getNode() and
|
||||
exists(int positional_keywords |
|
||||
exists(FunctionValue func | func = get_function_or_initializer(callable) |
|
||||
not func.getScope().hasKwArg() and
|
||||
positional_keywords = count(not_keyword_only_arg(call, func))
|
||||
or
|
||||
func.getScope().hasKwArg() and positional_keywords = 0
|
||||
)
|
||||
|
|
||||
result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords
|
||||
)
|
||||
}
|
||||
|
||||
int arg_count_objectapi(Call call) {
|
||||
result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword())
|
||||
}
|
||||
|
||||
int arg_count(Call call) {
|
||||
result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword())
|
||||
}
|
||||
|
||||
/* Gets a call corresponding to the given class or function*/
|
||||
private ControlFlowNode get_a_call(Object callable) {
|
||||
private ControlFlowNode get_a_call_objectapi(Object callable) {
|
||||
result = callable.(ClassObject).getACall()
|
||||
or
|
||||
result = callable.(FunctionObject).getACall()
|
||||
}
|
||||
|
||||
/* Gets a call corresponding to the given class or function*/
|
||||
private ControlFlowNode get_a_call(Value callable) {
|
||||
result = callable.(ClassValue).getACall()
|
||||
or
|
||||
result = callable.(FunctionValue).getACall()
|
||||
}
|
||||
|
||||
/* Gets the function object corresponding to the given class or function*/
|
||||
FunctionObject get_function_or_initializer(Object func_or_cls) {
|
||||
FunctionObject get_function_or_initializer_objectapi(Object func_or_cls) {
|
||||
result = func_or_cls.(FunctionObject)
|
||||
or
|
||||
result = func_or_cls.(ClassObject).declaredAttribute("__init__")
|
||||
}
|
||||
|
||||
/* Gets the function object corresponding to the given class or function*/
|
||||
FunctionValue get_function_or_initializer(Value func_or_cls) {
|
||||
result = func_or_cls.(FunctionValue)
|
||||
or
|
||||
result = func_or_cls.(ClassValue).declaredAttribute("__init__")
|
||||
}
|
||||
|
||||
|
||||
/**Whether there is an illegally named parameter called `name` in the `call` to `func` */
|
||||
predicate illegally_named_parameter(Call call, Object func, string name) {
|
||||
predicate illegally_named_parameter_objectapi(Call call, Object func, string name) {
|
||||
not func.isC() and
|
||||
name = call.getANamedArgumentName() and
|
||||
call.getAFlowNode() = get_a_call_objectapi(func) and
|
||||
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
|
||||
}
|
||||
|
||||
/**Whether there is an illegally named parameter called `name` in the `call` to `func` */
|
||||
predicate illegally_named_parameter(Call call, Value func, string name) {
|
||||
not func.isBuiltin() and
|
||||
name = call.getANamedArgumentName() and
|
||||
call.getAFlowNode() = get_a_call(func) and
|
||||
not get_function_or_initializer(func).isLegalArgumentName(name)
|
||||
}
|
||||
|
||||
/**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */
|
||||
predicate too_few_args(Call call, Object callable, int limit) {
|
||||
predicate too_few_args_objectapi(Call call, Object callable, int limit) {
|
||||
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
|
||||
not illegally_named_parameter(call, callable, _) and
|
||||
not illegally_named_parameter_objectapi(call, callable, _) and
|
||||
not exists(call.getStarargs()) and not exists(call.getKwargs()) and
|
||||
arg_count(call) < limit and
|
||||
exists(FunctionObject func | func = get_function_or_initializer(callable) |
|
||||
arg_count_objectapi(call) < limit and
|
||||
exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) |
|
||||
call = func.getAFunctionCall().getNode() and limit = func.minParameters() and
|
||||
/* The combination of misuse of `mox.Mox().StubOutWithMock()`
|
||||
* and a bug in mox's implementation of methods results in having to
|
||||
@@ -84,16 +148,37 @@ predicate too_few_args(Call call, Object callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassObject and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and limit = func.minParameters() - 1
|
||||
)
|
||||
}
|
||||
|
||||
/**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */
|
||||
predicate too_few_args(Call call, Value callable, int limit) {
|
||||
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
|
||||
not illegally_named_parameter(call, callable, _) and
|
||||
not exists(call.getStarargs()) and not exists(call.getKwargs()) and
|
||||
arg_count(call) < limit and
|
||||
exists(FunctionValue func | func = get_function_or_initializer(callable) |
|
||||
call = func.getACall().getNode() and limit = func.minParameters() and
|
||||
/* The combination of misuse of `mox.Mox().StubOutWithMock()`
|
||||
* and a bug in mox's implementation of methods results in having to
|
||||
* pass 1 too few arguments to the mocked function.
|
||||
*/
|
||||
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
|
||||
or
|
||||
call = func.getACall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
call.getAFlowNode() = get_a_call(callable) and limit = func.minParameters() - 1
|
||||
)
|
||||
}
|
||||
|
||||
/**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */
|
||||
predicate too_many_args(Call call, Object callable, int limit) {
|
||||
predicate too_many_args_objectapi(Call call, Object callable, int limit) {
|
||||
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
|
||||
not illegally_named_parameter(call, callable, _) and
|
||||
not illegally_named_parameter_objectapi(call, callable, _) and
|
||||
exists(FunctionObject func |
|
||||
func = get_function_or_initializer(callable) and
|
||||
func = get_function_or_initializer_objectapi(callable) and
|
||||
not func.getFunction().hasVarArg() and limit >= 0
|
||||
|
|
||||
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
|
||||
@@ -101,13 +186,38 @@ predicate too_many_args(Call call, Object callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassObject and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_for_call_objectapi(call, callable) > limit
|
||||
}
|
||||
|
||||
/**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */
|
||||
predicate too_many_args(Call call, Value callable, int limit) {
|
||||
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
|
||||
not illegally_named_parameter(call, callable, _) and
|
||||
exists(FunctionValue func |
|
||||
func = get_function_or_initializer(callable) and
|
||||
not func.getScope().hasVarArg() and limit >= 0
|
||||
|
|
||||
call = func.getACall().getNode() and limit = func.maxParameters()
|
||||
or
|
||||
call = func.getACall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
call.getAFlowNode() = get_a_call(callable) and limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_for_call(call, callable) > limit
|
||||
}
|
||||
|
||||
/** Holds if `call` has too many or too few arguments for `func` */
|
||||
predicate wrong_args(Call call, FunctionObject func, int limit, string too) {
|
||||
predicate wrong_args_objectapi(Call call, FunctionObject func, int limit, string too) {
|
||||
too_few_args_objectapi(call, func, limit) and too = "too few"
|
||||
or
|
||||
too_many_args_objectapi(call, func, limit) and too = "too many"
|
||||
}
|
||||
|
||||
/** Holds if `call` has too many or too few arguments for `func` */
|
||||
predicate wrong_args(Call call, FunctionValue func, int limit, string too) {
|
||||
too_few_args(call, func, limit) and too = "too few"
|
||||
or
|
||||
too_many_args(call, func, limit) and too = "too many"
|
||||
@@ -117,14 +227,31 @@ predicate wrong_args(Call call, FunctionObject func, int limit, string too) {
|
||||
* Implies nothing about whether `call` could call `func`.
|
||||
*/
|
||||
bindingset[call, func]
|
||||
predicate correct_args_if_called_as_method(Call call, FunctionObject func) {
|
||||
predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject func) {
|
||||
arg_count_objectapi(call)+1 >= func.minParameters()
|
||||
and
|
||||
arg_count_objectapi(call) < func.maxParameters()
|
||||
}
|
||||
|
||||
/** Holds if `call` has correct number of arguments for `func`.
|
||||
* Implies nothing about whether `call` could call `func`.
|
||||
*/
|
||||
bindingset[call, func]
|
||||
predicate correct_args_if_called_as_method(Call call, FunctionValue func) {
|
||||
arg_count(call)+1 >= func.minParameters()
|
||||
and
|
||||
arg_count(call) < func.maxParameters()
|
||||
}
|
||||
|
||||
/** Holds if `call` is a call to `overriding`, which overrides `func`. */
|
||||
predicate overridden_call(FunctionObject func, FunctionObject overriding, Call call) {
|
||||
predicate overridden_call_objectapi(FunctionObject func, FunctionObject overriding, Call call) {
|
||||
overriding.overrides(func) and
|
||||
overriding.getACall().getNode() = call
|
||||
}
|
||||
|
||||
/** Holds if `call` is a call to `overriding`, which overrides `func`. */
|
||||
predicate overridden_call(FunctionValue func, FunctionValue overriding, Call call) {
|
||||
overriding.overrides(func) and
|
||||
overriding.getACall().getNode() = call
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ import python
|
||||
from CallNode call_to_super, string name
|
||||
where
|
||||
exists(GlobalVariable gv, ControlFlowNode cn |
|
||||
call_to_super = theSuperType().getACall() and
|
||||
call_to_super = ClassValue::super_().getACall() and
|
||||
gv.getId() = "super" and
|
||||
cn = call_to_super.getArg(0) and
|
||||
name = call_to_super.getScope().getScope().(Class).getName() and
|
||||
exists(ClassObject other |
|
||||
cn.refersTo(other) and
|
||||
not other.getPyClass().getName() = name
|
||||
exists(ClassValue other |
|
||||
cn.pointsTo(other) and
|
||||
not other.getScope().getName() = name
|
||||
)
|
||||
)
|
||||
select call_to_super.getNode(), "First argument to super() should be " + name + "."
|
||||
|
||||
@@ -107,10 +107,10 @@ private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int en
|
||||
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
|
||||
exists(CallNode call |
|
||||
call = format_expr.getAFlowNode() |
|
||||
call.getFunction().refersTo(Object::builtin("format")) and call.getArg(0).refersTo(_, fmt.getAFlowNode()) and
|
||||
call.getFunction().pointsTo(Value::named("format")) and call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and
|
||||
args = count(format_expr.getAnArg()) - 1
|
||||
or
|
||||
call.getFunction().(AttrNode).getObject("format").refersTo(_, fmt.getAFlowNode()) and
|
||||
call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and
|
||||
args = count(format_expr.getAnArg())
|
||||
)
|
||||
}
|
||||
@@ -139,4 +139,3 @@ class AdvancedFormattingCall extends Call {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origi
|
||||
predicate typeerror_is_caught(ControlFlowNode f) {
|
||||
exists (Try try |
|
||||
try.getBody().contains(f.getNode()) and
|
||||
try.getAHandler().getType().refersTo(theTypeErrorType()))
|
||||
try.getAHandler().getType().pointsTo(ClassValue::typeError()))
|
||||
}
|
||||
|
||||
from ControlFlowNode f, ClassValue c, ControlFlowNode origin
|
||||
|
||||
@@ -28,14 +28,14 @@ predicate probablySingleton(ClassValue cls) {
|
||||
|
||||
predicate invalid_to_use_is_portably(ClassValue c) {
|
||||
overrides_eq_or_cmp(c) and
|
||||
/* Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ */
|
||||
// Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__
|
||||
not c = Value::named("type") and not c = ClassValue::builtinFunction() and not c = Value::named("bool") and
|
||||
/* OK to compare with 'is' if a singleton */
|
||||
// OK to compare with 'is' if a singleton
|
||||
not probablySingleton(c)
|
||||
}
|
||||
|
||||
predicate simple_constant(ControlFlowNode f) {
|
||||
exists(Object obj | f.refersTo(obj) | obj = theTrueObject() or obj = theFalseObject() or obj = theNoneObject())
|
||||
exists(Value val | f.pointsTo(val) | val = Value::named("True") or val = Value::named("False") or val = Value::named("None"))
|
||||
}
|
||||
|
||||
private predicate cpython_interned_value(Expr e) {
|
||||
@@ -66,14 +66,14 @@ private predicate universally_interned_value(Expr e) {
|
||||
|
||||
predicate cpython_interned_constant(Expr e) {
|
||||
exists(Expr const |
|
||||
e.refersTo(_, const) |
|
||||
e.pointsTo(_, const) |
|
||||
cpython_interned_value(const)
|
||||
)
|
||||
}
|
||||
|
||||
predicate universally_interned_constant(Expr e) {
|
||||
exists(Expr const |
|
||||
e.refersTo(_, const) |
|
||||
e.pointsTo(_, const) |
|
||||
universally_interned_value(const)
|
||||
)
|
||||
}
|
||||
@@ -95,7 +95,7 @@ private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) {
|
||||
}
|
||||
|
||||
predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) {
|
||||
/* OK to use 'is' when defining '__eq__' */
|
||||
// OK to use 'is' when defining '__eq__'
|
||||
not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | eq = comp.getScope().getScope*())
|
||||
and
|
||||
(
|
||||
@@ -107,24 +107,24 @@ predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls)
|
||||
)
|
||||
)
|
||||
and
|
||||
/* OK to use 'is' when comparing items from a known set of objects */
|
||||
not exists(Expr left, Expr right, Object obj |
|
||||
// OK to use 'is' when comparing items from a known set of objects
|
||||
not exists(Expr left, Expr right, Value val |
|
||||
comp.compares(left, op, right) and
|
||||
exists(ImmutableLiteral il | il.getLiteralObject() = obj) |
|
||||
left.refersTo(obj) and right.refersTo(obj)
|
||||
exists(ImmutableLiteral il | il.getLiteralValue() = val) |
|
||||
left.pointsTo(val) and right.pointsTo(val)
|
||||
or
|
||||
/* Simple constant in module, probably some sort of sentinel */
|
||||
// Simple constant in module, probably some sort of sentinel
|
||||
exists(AstNode origin |
|
||||
not left.refersTo(_) and right.refersTo(obj, origin) and
|
||||
not left.pointsTo(_) and right.pointsTo(val, origin) and
|
||||
origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule()
|
||||
)
|
||||
)
|
||||
and
|
||||
/* OK to use 'is' when comparing with a member of an enum */
|
||||
// OK to use 'is' when comparing with a member of an enum
|
||||
not exists(Expr left, Expr right, AstNode origin |
|
||||
comp.compares(left, op, right) and
|
||||
enum_member(origin) |
|
||||
left.refersTo(_, origin) or right.refersTo(_, origin)
|
||||
left.pointsTo(_, origin) or right.pointsTo(_, origin)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,4 +135,3 @@ private predicate enum_member(AstNode obj) {
|
||||
asgn.getValue() = obj
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,19 +18,20 @@ where
|
||||
// Only relevant for Python 2, as all later versions implement true division
|
||||
major_version() = 2
|
||||
and
|
||||
exists(BinaryExprNode bin, Object lobj, Object robj |
|
||||
exists(BinaryExprNode bin, Value lval, Value rval |
|
||||
bin = div.getAFlowNode()
|
||||
and bin.getNode().getOp() instanceof Div
|
||||
and bin.getLeft().refersTo(lobj, theIntType(), left)
|
||||
and bin.getRight().refersTo(robj, theIntType(), right)
|
||||
and bin.getLeft().pointsTo(lval, left)
|
||||
and lval.getClass() = ClassValue::int_()
|
||||
and bin.getRight().pointsTo(rval, right)
|
||||
and rval.getClass() = ClassValue::int_()
|
||||
// Ignore instances where integer division leaves no remainder
|
||||
and not lobj.(NumericObject).intValue() % robj.(NumericObject).intValue() = 0
|
||||
and not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0
|
||||
and not bin.getNode().getEnclosingModule().hasFromFuture("division")
|
||||
// Filter out results wrapped in `int(...)`
|
||||
and not exists(CallNode c, ClassObject cls |
|
||||
c.getAnArg() = bin
|
||||
and c.getFunction().refersTo(cls)
|
||||
and cls.getName() = "int"
|
||||
and not exists(CallNode c |
|
||||
c = ClassValue::int_().getACall()
|
||||
and c.getAnArg() = bin
|
||||
)
|
||||
)
|
||||
select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.",
|
||||
|
||||
@@ -38,14 +38,14 @@ predicate unnecessary_lambda(Lambda l, Expr e) {
|
||||
simple_wrapper(l, e) and
|
||||
(
|
||||
/* plain class */
|
||||
exists(ClassObject c | e.refersTo(c))
|
||||
exists(ClassValue c | e.pointsTo(c))
|
||||
or
|
||||
/* plain function */
|
||||
exists(FunctionObject f | e.refersTo(f))
|
||||
exists(FunctionValue f | e.pointsTo(f))
|
||||
or
|
||||
/* bound-method of enclosing instance */
|
||||
exists(ClassObject cls, Attribute a |
|
||||
cls.getPyClass() = l.getScope().getScope() and a = e |
|
||||
exists(ClassValue cls, Attribute a |
|
||||
cls.getScope() = l.getScope().getScope() and a = e |
|
||||
((Name)a.getObject()).getId() = "self" and
|
||||
cls.hasAttribute(a.getName())
|
||||
)
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
from CallNode call, ControlFlowNode func
|
||||
where
|
||||
major_version() = 2 and call.getFunction() = func and func.refersTo(Object::builtin("apply"))
|
||||
where major_version() = 2 and call.getFunction() = func and func.pointsTo(Value::named("apply"))
|
||||
select call, "Call to the obsolete builtin function 'apply'."
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>A call to the input() function, <code>input(prompt)</code> is equivalent to <code>eval(raw_input(prompt))</code>. Evaluating user input without any checking can be a serious security flaw.</p>
|
||||
<p>In Python 2, a call to the <code>input()</code> function, <code>input(prompt)</code> is equivalent to <code>eval(raw_input(prompt))</code>. Evaluating user input without any checking can be a serious security flaw.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p> Get user input with <code>raw_input(prompt)</code> and then validate that input before evaluating. If the expected input is a number or
|
||||
<p>Get user input with <code>raw_input(prompt)</code> and then validate that input before evaluating. If the expected input is a number or
|
||||
string, then <code>ast.literal_eval()</code> can always be used safely.</p>
|
||||
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
<li>Python Standard Library: <a href="http://docs.python.org/library/functions.html#input">input</a>,
|
||||
<a href="http://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a>.</li>
|
||||
<li>Python Standard Library: <a href="http://docs.python.org/2/library/functions.html#input">input</a>,
|
||||
<a href="http://docs.python.org/2/library/ast.html#ast.literal_eval">ast.literal_eval</a>.</li>
|
||||
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Data_validation">Data validation</a>.</li>
|
||||
|
||||
</references>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name 'input' function used
|
||||
* @description The built-in function 'input' is used which can allow arbitrary code to be run.
|
||||
* @name 'input' function used in Python 2
|
||||
* @description The built-in function 'input' is used which, in Python 2, can allow arbitrary code to be run.
|
||||
* @kind problem
|
||||
* @tags security
|
||||
* correctness
|
||||
@@ -18,4 +18,4 @@ where
|
||||
call.getFunction() = func and
|
||||
func.pointsTo(context, Value::named("input"), _) and
|
||||
not func.pointsTo(context, Value::named("raw_input"), _)
|
||||
select call, "The unsafe built-in function 'input' is used."
|
||||
select call, "The unsafe built-in function 'input' is used in Python 2."
|
||||
|
||||
@@ -19,7 +19,7 @@ import Expressions.CallArgs
|
||||
|
||||
from Call call, FunctionObject func, string name
|
||||
where
|
||||
illegally_named_parameter(call, func, name) and
|
||||
illegally_named_parameter_objectapi(call, func, name) and
|
||||
not func.isAbstract() and
|
||||
not exists(FunctionObject overridden | func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name)
|
||||
select
|
||||
|
||||
@@ -17,12 +17,12 @@ import CallArgs
|
||||
from Call call, FunctionObject func, string too, string should, int limit
|
||||
where
|
||||
(
|
||||
too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than "
|
||||
too_many_args_objectapi(call, func, limit) and too = "too many arguments" and should = "no more than "
|
||||
or
|
||||
too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than "
|
||||
too_few_args_objectapi(call, func, limit) and too = "too few arguments" and should = "no fewer than "
|
||||
) and
|
||||
not func.isAbstract() and
|
||||
not exists(FunctionObject overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden))
|
||||
not exists(FunctionObject overridden | func.overrides(overridden) and correct_args_if_called_as_method_objectapi(call, overridden))
|
||||
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
|
||||
and not func.getName() = "__new__"
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ import Expressions.CallArgs
|
||||
from Call call, FunctionObject func, FunctionObject overridden, string problem
|
||||
where
|
||||
func.overrides(overridden) and (
|
||||
wrong_args(call, func, _, problem) and correct_args_if_called_as_method(call, overridden)
|
||||
wrong_args_objectapi(call, func, _, problem) and correct_args_if_called_as_method_objectapi(call, overridden)
|
||||
or
|
||||
exists(string name |
|
||||
illegally_named_parameter(call, func, name) and problem = "an argument named '" + name + "'" and
|
||||
illegally_named_parameter_objectapi(call, func, name) and problem = "an argument named '" + name + "'" and
|
||||
overridden.getFunction().getAnArg().(Name).getId() = name
|
||||
)
|
||||
)
|
||||
|
||||
@@ -18,11 +18,11 @@ where
|
||||
not func.getName() = "__init__" and
|
||||
overriding.overrides(func) and
|
||||
call = overriding.getAMethodCall().getNode() and
|
||||
correct_args_if_called_as_method(call, overriding) and
|
||||
correct_args_if_called_as_method_objectapi(call, overriding) and
|
||||
(
|
||||
arg_count(call)+1 < func.minParameters() and problem = "too few arguments"
|
||||
arg_count_objectapi(call)+1 < func.minParameters() and problem = "too few arguments"
|
||||
or
|
||||
arg_count(call) >= func.maxParameters() and problem = "too many arguments"
|
||||
arg_count_objectapi(call) >= func.maxParameters() and problem = "too many arguments"
|
||||
or
|
||||
exists(string name | call.getAKeyword().getArg() = name and
|
||||
overriding.getFunction().getAnArg().(Name).getId() = name and
|
||||
|
||||
@@ -23,12 +23,12 @@ predicate is_used(Call c) {
|
||||
)
|
||||
}
|
||||
|
||||
from Call c, FunctionObject func
|
||||
from Call c, FunctionValue func
|
||||
where
|
||||
/* Call result is used, but callee is a procedure */
|
||||
is_used(c) and c.getFunc().refersTo(func) and func.getFunction().isProcedure() and
|
||||
is_used(c) and c.getFunc().pointsTo(func) and func.getScope().isProcedure() and
|
||||
/* All callees are procedures */
|
||||
forall(FunctionObject callee | c.getFunc().refersTo(callee) | callee.getFunction().isProcedure()) and
|
||||
forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and
|
||||
/* Mox return objects have an `AndReturn` method */
|
||||
not useOfMoxInModule(c.getEnclosingModule())
|
||||
select c, "The result of '$@' is used even though it is always None.", func, func.getQualifiedName()
|
||||
|
||||
1
python/ql/src/experimental/README.md
Normal file
1
python/ql/src/experimental/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This directory contains [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* This library file is auto-generated by 'semmle/query_gen.py'.
|
||||
* WARNING: Any modifications to this file will be lost.
|
||||
* Relations can be changed by modifying master.py.
|
||||
*/
|
||||
import python
|
||||
|
||||
library class Add_ extends @py_Add, Operator {
|
||||
@@ -1781,6 +1786,37 @@ library class Slice_ extends @py_Slice, Expr {
|
||||
|
||||
}
|
||||
|
||||
library class SpecialOperation_ extends @py_SpecialOperation, Expr {
|
||||
|
||||
|
||||
/** Gets the name of this special operation. */
|
||||
string getName() {
|
||||
py_strs(result, this, 2)
|
||||
}
|
||||
|
||||
|
||||
/** Gets the arguments of this special operation. */
|
||||
ExprList getArguments() {
|
||||
py_expr_lists(result, this, 3)
|
||||
}
|
||||
|
||||
|
||||
/** Gets the nth argument of this special operation. */
|
||||
Expr getArgument(int index) {
|
||||
result = this.getArguments().getItem(index)
|
||||
}
|
||||
|
||||
/** Gets an argument of this special operation. */
|
||||
Expr getAnArgument() {
|
||||
result = this.getArguments().getAnItem()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "SpecialOperation"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
library class Starred_ extends @py_Starred, Expr {
|
||||
|
||||
|
||||
|
||||
@@ -910,6 +910,10 @@ private AstNode assigned_value(Expr lhs) {
|
||||
|
||||
predicate nested_sequence_assign(Expr left_parent, Expr right_parent,
|
||||
Expr left_result, Expr right_result) {
|
||||
exists(Assign a |
|
||||
a.getATarget().getASubExpression*() = left_parent and
|
||||
a.getValue().getASubExpression*() = right_parent
|
||||
) and
|
||||
exists(int i, Expr left_elem, Expr right_elem
|
||||
|
|
||||
(
|
||||
|
||||
@@ -726,6 +726,8 @@ private class EssaTaintTracking extends string {
|
||||
private TaintKind iterable_unpacking_descent(
|
||||
SequenceNode left_parent, ControlFlowNode left_defn, CollectionKind parent_kind
|
||||
) {
|
||||
//TODO: Fix the cartesian product in this predicate
|
||||
none() and
|
||||
left_parent.getAnElement() = left_defn and
|
||||
// Handle `a, *b = some_iterable`
|
||||
if left_defn instanceof StarredNode
|
||||
|
||||
@@ -532,6 +532,15 @@ class ClassValue extends Value {
|
||||
this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name)
|
||||
}
|
||||
|
||||
/** Whether this class is a legal exception class.
|
||||
* What constitutes a legal exception class differs between major versions */
|
||||
predicate isLegalExceptionType() {
|
||||
not this.isNewStyle() or
|
||||
this.getASuperType() = ClassValue::baseException()
|
||||
or
|
||||
major_version() = 2 and this = ClassValue::tuple()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -555,6 +564,28 @@ abstract class FunctionValue extends CallableValue {
|
||||
predicate isOverriddenMethod() {
|
||||
exists(Value f | f.overrides(this))
|
||||
}
|
||||
|
||||
/** Whether `name` is a legal argument name for this function */
|
||||
bindingset[name]
|
||||
predicate isLegalArgumentName(string name) {
|
||||
this.getScope().getAnArg().asName().getId() = name
|
||||
or
|
||||
this.getScope().getAKeywordOnlyArg().getId() = name
|
||||
or
|
||||
this.getScope().hasKwArg()
|
||||
}
|
||||
|
||||
/** Whether this is a "normal" method, that is, it is exists as a class attribute
|
||||
* which is not a lambda and not the __new__ method. */
|
||||
predicate isNormalMethod() {
|
||||
exists(ClassValue cls, string name |
|
||||
cls.declaredAttribute(name) = this and
|
||||
name != "__new__" and
|
||||
exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) |
|
||||
not origin instanceof Lambda
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Class representing Python functions */
|
||||
@@ -917,7 +948,7 @@ module ClassValue {
|
||||
|
||||
/** Get the `ClassValue` for the `StopIteration` class. */
|
||||
ClassValue stopIteration() {
|
||||
result = TBuiltinClassObject(Builtin::special("StopIteration"))
|
||||
result = TBuiltinClassObject(Builtin::builtin("StopIteration"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the class of modules. */
|
||||
@@ -960,6 +991,11 @@ module ClassValue {
|
||||
result = TBuiltinClassObject(Builtin::builtin("KeyError"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `LookupError` class. */
|
||||
ClassValue lookupError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("LookupError"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `IOError` class. */
|
||||
ClassValue ioError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("IOError"))
|
||||
@@ -975,4 +1011,14 @@ module ClassValue {
|
||||
result = TBuiltinClassObject(Builtin::builtin("ImportError"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `UnicodeEncodeError` class. */
|
||||
ClassValue unicodeEncodeError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `UnicodeDecodeError` class. */
|
||||
ClassValue unicodeDecodeError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,25 +12,27 @@ private predicate re_module_function(string name, int flags) {
|
||||
name = "subn" and flags = 4
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `s` is used as a regex with the `re` module, with the regex-mode `mode` (if known).
|
||||
* If regex mode is not known, `mode` will be `"None"`.
|
||||
*/
|
||||
predicate used_as_regex(Expr s, string mode) {
|
||||
(s instanceof Bytes or s instanceof Unicode)
|
||||
and
|
||||
exists(ModuleValue re | re.getName() = "re" |
|
||||
/* Call to re.xxx(regex, ... [mode]) */
|
||||
exists(CallNode call, string name |
|
||||
call.getArg(0).refersTo(_, _, s.getAFlowNode()) and
|
||||
call.getFunction().pointsTo(re.attr(name)) |
|
||||
mode = "None"
|
||||
or
|
||||
exists(Value obj |
|
||||
mode = mode_from_mode_object(obj) |
|
||||
exists(int flags_arg |
|
||||
re_module_function(name, flags_arg) and
|
||||
call.getArg(flags_arg).pointsTo(obj)
|
||||
)
|
||||
or
|
||||
call.getArgByName("flags").pointsTo(obj)
|
||||
/* Call to re.xxx(regex, ... [mode]) */
|
||||
exists(CallNode call, string name |
|
||||
call.getArg(0).refersTo(_, _, s.getAFlowNode()) and
|
||||
call.getFunction().pointsTo(Module::named("re").attr(name)) |
|
||||
mode = "None"
|
||||
or
|
||||
exists(Value obj |
|
||||
mode = mode_from_mode_object(obj) |
|
||||
exists(int flags_arg |
|
||||
re_module_function(name, flags_arg) and
|
||||
call.getArg(flags_arg).pointsTo(obj)
|
||||
)
|
||||
or
|
||||
call.getArgByName("flags").pointsTo(obj)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,45 +2,40 @@ import python
|
||||
import Basic
|
||||
private import Common
|
||||
|
||||
/** An extensible kind of taint representing an externally controlled string.
|
||||
/**
|
||||
* An extensible kind of taint representing an externally controlled string.
|
||||
*/
|
||||
abstract class ExternalStringKind extends StringKind {
|
||||
|
||||
bindingset[this]
|
||||
ExternalStringKind() {
|
||||
this = this
|
||||
}
|
||||
ExternalStringKind() { this = this }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = StringKind.super.getTaintForFlowStep(fromnode, tonode)
|
||||
or
|
||||
tonode.(SequenceNode).getElement(_) = fromnode and result.(ExternalStringSequenceKind).getItem() = this
|
||||
tonode.(SequenceNode).getElement(_) = fromnode and
|
||||
result.(ExternalStringSequenceKind).getItem() = this
|
||||
or
|
||||
json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this
|
||||
or
|
||||
tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this
|
||||
or
|
||||
urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this
|
||||
or
|
||||
urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing a sequence, with a "taint" member */
|
||||
class ExternalStringSequenceKind extends SequenceKind {
|
||||
|
||||
ExternalStringSequenceKind() {
|
||||
this.getItem() instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
ExternalStringSequenceKind() { this.getItem() instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
/** An hierachical dictionary or list where the entire structure is externally controlled
|
||||
/**
|
||||
* An hierachical dictionary or list where the entire structure is externally controlled
|
||||
* This is typically a parsed JSON object.
|
||||
*/
|
||||
class ExternalJsonKind extends TaintKind {
|
||||
|
||||
ExternalJsonKind() {
|
||||
this = "json[" + any(ExternalStringKind key) + "]"
|
||||
}
|
||||
|
||||
ExternalJsonKind() { this = "json[" + any(ExternalStringKind key) + "]" }
|
||||
|
||||
/** Gets the taint kind for item in this sequence */
|
||||
TaintKind getValue() {
|
||||
@@ -54,65 +49,225 @@ class ExternalJsonKind extends TaintKind {
|
||||
json_subscript_taint(tonode, fromnode, this, result)
|
||||
or
|
||||
result = this and copy_call(fromnode, tonode)
|
||||
}
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "get" and result = this.getValue()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing a dictionary mapping str->"taint" */
|
||||
class ExternalStringDictKind extends DictKind {
|
||||
|
||||
ExternalStringDictKind() {
|
||||
this.getValue() instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing a dictionary mapping strings to sequences of
|
||||
* tainted strings */
|
||||
|
||||
/**
|
||||
* A kind of "taint", representing a dictionary mapping strings to sequences of
|
||||
* tainted strings
|
||||
*/
|
||||
class ExternalStringSequenceDictKind extends DictKind {
|
||||
ExternalStringSequenceDictKind() {
|
||||
this.getValue() instanceof ExternalStringSequenceKind
|
||||
ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind }
|
||||
}
|
||||
|
||||
/** TaintKind for the result of `urlsplit(tainted_string)` */
|
||||
class ExternalUrlSplitResult extends ExternalStringSequenceKind {
|
||||
// https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result = super.getTaintOfAttribute(name)
|
||||
or
|
||||
(
|
||||
// namedtuple field names
|
||||
name = "scheme" or
|
||||
name = "netloc" or
|
||||
name = "path" or
|
||||
name = "query" or
|
||||
name = "fragment" or
|
||||
// class methods
|
||||
name = "username" or
|
||||
name = "password" or
|
||||
name = "hostname"
|
||||
) and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
result = super.getTaintOfMethodResult(name)
|
||||
or
|
||||
name = "geturl" and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
/** TaintKind for the result of `urlparse(tainted_string)` */
|
||||
class ExternalUrlParseResult extends ExternalStringSequenceKind {
|
||||
// https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result = super.getTaintOfAttribute(name)
|
||||
or
|
||||
(
|
||||
// namedtuple field names
|
||||
name = "scheme" or
|
||||
name = "netloc" or
|
||||
name = "path" or
|
||||
name = "params" or
|
||||
name = "query" or
|
||||
name = "fragment" or
|
||||
// class methods
|
||||
name = "username" or
|
||||
name = "password" or
|
||||
name = "hostname"
|
||||
) and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
result = super.getTaintOfMethodResult(name)
|
||||
or
|
||||
name = "geturl" and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper for getTaintForStep() */
|
||||
pragma [noinline]
|
||||
private predicate json_subscript_taint(SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key) {
|
||||
pragma[noinline]
|
||||
private predicate json_subscript_taint(
|
||||
SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key
|
||||
) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
key = seq.getValue()
|
||||
}
|
||||
|
||||
|
||||
private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
|
||||
exists(FunctionObject json_loads |
|
||||
ModuleObject::named("json").attr("loads") = json_loads and
|
||||
json_loads.getACall() = tonode and tonode.getArg(0) = fromnode
|
||||
json_loads.getACall() = tonode and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
private predicate urlsplit(ControlFlowNode fromnode, CallNode tonode) {
|
||||
// This could be implemented as `exists(FunctionValue` without the explicit six part,
|
||||
// but then our tests will need to import +100 modules, so for now this slightly
|
||||
// altered version gets to live on.
|
||||
exists(Value urlsplit |
|
||||
(
|
||||
urlsplit = Value::named("six.moves.urllib.parse.urlsplit")
|
||||
or
|
||||
// Python 2
|
||||
urlsplit = Value::named("urlparse.urlsplit")
|
||||
or
|
||||
// Python 3
|
||||
urlsplit = Value::named("urllib.parse.urlsplit")
|
||||
) and
|
||||
tonode = urlsplit.getACall() and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
private predicate urlparse(ControlFlowNode fromnode, CallNode tonode) {
|
||||
// This could be implemented as `exists(FunctionValue` without the explicit six part,
|
||||
// but then our tests will need to import +100 modules, so for now this slightly
|
||||
// altered version gets to live on.
|
||||
exists(Value urlparse |
|
||||
(
|
||||
urlparse = Value::named("six.moves.urllib.parse.urlparse")
|
||||
or
|
||||
// Python 2
|
||||
urlparse = Value::named("urlparse.urlparse")
|
||||
or
|
||||
// Python 3
|
||||
urlparse = Value::named("urllib.parse.urlparse")
|
||||
) and
|
||||
tonode = urlparse.getACall() and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing an open file-like object from an external source. */
|
||||
class ExternalFileObject extends TaintKind {
|
||||
|
||||
ExternalFileObject() {
|
||||
this = "file[" + any(ExternalStringKind key) + "]"
|
||||
}
|
||||
|
||||
ExternalFileObject() { this = "file[" + any(ExternalStringKind key) + "]" }
|
||||
|
||||
/** Gets the taint kind for the contents of this file */
|
||||
TaintKind getValue() {
|
||||
this = "file[" + result + "]"
|
||||
}
|
||||
TaintKind getValue() { this = "file[" + result + "]" }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "read" and result = this.getValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary sanitizer for the tainted result from `urlsplit` and `urlparse`. Can be used to reduce FPs until
|
||||
* we have better support for namedtuples.
|
||||
*
|
||||
* Will clear **all** taint on a test of the kind. That is, on the true edge of any matching test,
|
||||
* all fields/indexes will be cleared of taint.
|
||||
*
|
||||
* Handles:
|
||||
* - `if splitres.netloc == "KNOWN_VALUE"`
|
||||
* - `if splitres[0] == "KNOWN_VALUE"`
|
||||
*/
|
||||
class UrlsplitUrlparseTempSanitizer extends Sanitizer {
|
||||
// TODO: remove this once we have better support for named tuples
|
||||
|
||||
UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" }
|
||||
|
||||
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||
(
|
||||
taint instanceof ExternalUrlSplitResult
|
||||
or
|
||||
taint instanceof ExternalUrlParseResult
|
||||
) and
|
||||
exists(ControlFlowNode full_use |
|
||||
full_use.(SubscriptNode).getObject() = test.getInput().getAUse()
|
||||
or
|
||||
full_use.(AttrNode).getObject() = test.getInput().getAUse()
|
||||
|
|
||||
clears_taint(full_use, test.getTest(), test.getSense())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) {
|
||||
test_equality_with_const(test, tainted, sense)
|
||||
or
|
||||
test_in_const_seq(test, tainted, sense)
|
||||
or
|
||||
test.(UnaryExprNode).getNode().getOp() instanceof Not and
|
||||
exists(ControlFlowNode nested_test |
|
||||
nested_test = test.(UnaryExprNode).getOperand() and
|
||||
clears_taint(tainted, nested_test, sense.booleanNot())
|
||||
)
|
||||
}
|
||||
|
||||
/** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */
|
||||
private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) {
|
||||
exists(ControlFlowNode const, Cmpop op |
|
||||
const.getNode() instanceof StrConst
|
||||
|
|
||||
(
|
||||
cmp.operands(const, op, tainted)
|
||||
or
|
||||
cmp.operands(tainted, op, const)
|
||||
) and
|
||||
(
|
||||
op instanceof Eq and sense = true
|
||||
or
|
||||
op instanceof NotEq and sense = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */
|
||||
private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) {
|
||||
exists(SequenceNode const_seq, Cmpop op |
|
||||
forall(ControlFlowNode elem | elem = const_seq.getAnElement() | elem.getNode() instanceof StrConst)
|
||||
|
|
||||
cmp.operands(tainted, op, const_seq) and
|
||||
(
|
||||
op instanceof In and sense = true
|
||||
or
|
||||
op instanceof NotIn and sense = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
2
python/ql/src/semmle/python/web/ClientHttpRequest.qll
Normal file
2
python/ql/src/semmle/python/web/ClientHttpRequest.qll
Normal file
@@ -0,0 +1,2 @@
|
||||
import semmle.python.web.client.StdLib
|
||||
import semmle.python.web.client.Requests
|
||||
@@ -89,7 +89,7 @@ abstract class CookieSet extends CookieOperation {}
|
||||
/** Generic taint sink in a http response */
|
||||
abstract class HttpResponseTaintSink extends TaintSink {
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
@@ -97,9 +97,51 @@ abstract class HttpResponseTaintSink extends TaintSink {
|
||||
|
||||
abstract class HttpRedirectTaintSink extends TaintSink {
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module Client {
|
||||
|
||||
// TODO: user-input in other than URL:
|
||||
// - `data`, `json` for `requests.post`
|
||||
// - `body` for `HTTPConnection.request`
|
||||
// - headers?
|
||||
|
||||
// TODO: Add more library support
|
||||
// - urllib3 https://github.com/urllib3/urllib3
|
||||
// - httpx https://github.com/encode/httpx
|
||||
|
||||
/**
|
||||
* An outgoing http request
|
||||
*
|
||||
* For example:
|
||||
* conn = HTTPConnection('example.com')
|
||||
conn.request('GET', '/path')
|
||||
*/
|
||||
abstract class HttpRequest extends ControlFlowNode {
|
||||
|
||||
/** Get any ControlFlowNode that is used to construct the final URL.
|
||||
*
|
||||
* In the HTTPConnection example, there is a result for both `'example.com'` and for `'/path'`.
|
||||
*/
|
||||
abstract ControlFlowNode getAUrlPart();
|
||||
|
||||
abstract string getMethodUpper();
|
||||
}
|
||||
|
||||
/** Taint sink for the URL-part of an outgoing http request */
|
||||
class HttpRequestUrlTaintSink extends TaintSink {
|
||||
|
||||
HttpRequestUrlTaintSink() {
|
||||
this = any(HttpRequest r).getAUrlPart()
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
22
python/ql/src/semmle/python/web/client/Requests.qll
Normal file
22
python/ql/src/semmle/python/web/client/Requests.qll
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Modeling outgoing HTTP requests using the `requests` package
|
||||
* https://pypi.org/project/requests/
|
||||
*/
|
||||
|
||||
import python
|
||||
private import semmle.python.web.Http
|
||||
|
||||
class RequestsHttpRequest extends Client::HttpRequest, CallNode {
|
||||
CallableValue func;
|
||||
string method;
|
||||
|
||||
RequestsHttpRequest() {
|
||||
method = httpVerbLower() and
|
||||
func = Module::named("requests").attr(method) and
|
||||
this = func.getACall()
|
||||
}
|
||||
|
||||
override ControlFlowNode getAUrlPart() { result = func.getNamedArgumentForCall(this, "url") }
|
||||
|
||||
override string getMethodUpper() { result = method.toUpperCase() }
|
||||
}
|
||||
55
python/ql/src/semmle/python/web/client/StdLib.qll
Normal file
55
python/ql/src/semmle/python/web/client/StdLib.qll
Normal file
@@ -0,0 +1,55 @@
|
||||
import python
|
||||
private import semmle.python.web.Http
|
||||
|
||||
ClassValue httpConnectionClass() {
|
||||
// Python 2
|
||||
result = Value::named("httplib.HTTPConnection")
|
||||
or
|
||||
result = Value::named("httplib.HTTPSConnection")
|
||||
or
|
||||
// Python 3
|
||||
result = Value::named("http.client.HTTPConnection")
|
||||
or
|
||||
result = Value::named("http.client.HTTPSConnection")
|
||||
or
|
||||
// six
|
||||
result = Value::named("six.moves.http_client.HTTPConnection")
|
||||
or
|
||||
result = Value::named("six.moves.http_client.HTTPSConnection")
|
||||
}
|
||||
|
||||
class HttpConnectionHttpRequest extends Client::HttpRequest, CallNode {
|
||||
CallNode constructor_call;
|
||||
CallableValue func;
|
||||
|
||||
HttpConnectionHttpRequest() {
|
||||
exists(ClassValue cls, AttrNode call_origin, Value constructor_call_value |
|
||||
cls = httpConnectionClass() and
|
||||
func = cls.lookup("request") and
|
||||
this = func.getACall() and
|
||||
// since you can do `r = conn.request; r('GET', path)`, we need to find the origin
|
||||
this.getFunction().pointsTo(_, _, call_origin) and
|
||||
// Since HTTPSConnection is a subtype of HTTPConnection, up until this point, `cls` could be either class,
|
||||
// because `HTTPSConnection.request == HTTPConnection.request`. To avoid generating 2 results, we filter
|
||||
// on the actual class used as the constructor
|
||||
call_origin.getObject().pointsTo(_, constructor_call_value, constructor_call) and
|
||||
cls = constructor_call_value.getClass() and
|
||||
constructor_call = cls.getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override ControlFlowNode getAUrlPart() {
|
||||
result = func.getNamedArgumentForCall(this, "url")
|
||||
or
|
||||
result = constructor_call.getArg(0)
|
||||
or
|
||||
result = constructor_call.getArgByName("host")
|
||||
}
|
||||
|
||||
override string getMethodUpper() {
|
||||
exists(string method |
|
||||
result = method.toUpperCase() and
|
||||
func.getNamedArgumentForCall(this, "method").pointsTo(Value::forString(method))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --lang=2 --max-import-depth=4
|
||||
optimize: true
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| TruncatedDivision_test.py:8:12:8:16 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:8:12:8:12 | TruncatedDivision_test.py:8 | left | TruncatedDivision_test.py:8:16:8:16 | TruncatedDivision_test.py:8 | right |
|
||||
| TruncatedDivision_test.py:11:12:11:40 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:2:12:2:12 | TruncatedDivision_test.py:2 | left | TruncatedDivision_test.py:5:12:5:12 | TruncatedDivision_test.py:5 | right |
|
||||
| TruncatedDivision_test.py:65:7:65:11 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:65:7:65:7 | TruncatedDivision_test.py:65 | left | TruncatedDivision_test.py:65:11:65:11 | TruncatedDivision_test.py:65 | right |
|
||||
| TruncatedDivision_test.py:72:7:72:35 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:25:12:25:12 | TruncatedDivision_test.py:25 | left | TruncatedDivision_test.py:28:12:28:12 | TruncatedDivision_test.py:28 | right |
|
||||
|
||||
@@ -1,24 +1,88 @@
|
||||
#### TruncatedDivision.ql
|
||||
|
||||
# NOTE: The following test case will only work under Python 2.
|
||||
|
||||
# Truncated division occurs when two integers are divided. This causes the
|
||||
# fractional part, if there is one, to be discared. So for example, `2 / 3` will
|
||||
# evaluate to `0` instead of `0.666...`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Negative Cases
|
||||
|
||||
|
||||
|
||||
# This case is good, and is a minimal obvious case that should be good. It
|
||||
# SHOULD NOT be found by the query.
|
||||
print(3.0 / 2.0)
|
||||
|
||||
# This case is good, because it explicitly converts the possibly-truncated
|
||||
# value to an integer. It SHOULD NOT be found by the query.
|
||||
|
||||
def return_three():
|
||||
return 3
|
||||
|
||||
def return_two():
|
||||
return 2
|
||||
|
||||
def f1():
|
||||
return 3 / 2
|
||||
print(int(return_three() / return_two()))
|
||||
|
||||
def f2():
|
||||
return return_three() / return_two()
|
||||
|
||||
def f3(x):
|
||||
|
||||
# These cases are good, because `halve` checks the type, and if the type would
|
||||
# truncate, it explicitly converts to a float first before doing the division.
|
||||
# These SHOULD NOT be found by the query.
|
||||
|
||||
def halve(x):
|
||||
if isinstance(x, float):
|
||||
return x / 2
|
||||
else:
|
||||
return (1.0 * x) / 2
|
||||
|
||||
def f4():
|
||||
do_stuff(f3(1))
|
||||
do_stuff(f3(1.0))
|
||||
print(halve(1))
|
||||
print(halve(1.0))
|
||||
|
||||
def f5():
|
||||
return int(return_three() / return_two())
|
||||
|
||||
|
||||
# This case is good, because the sum is `3.0`, which is a float, and will not
|
||||
# truncate. This case SHOULD NOT be found by the query.
|
||||
|
||||
print(average([1.0, 2.0]))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Positive Cases
|
||||
|
||||
|
||||
|
||||
# This case is bad, and is a minimal obvious case that should be bad. It
|
||||
# SHOULD be found by the query.
|
||||
|
||||
print(3 / 2)
|
||||
|
||||
|
||||
|
||||
# This case is bad. It uses indirect returns of integers through function calls
|
||||
# to produce the problem. I
|
||||
|
||||
print(return_three() / return_two())
|
||||
|
||||
|
||||
|
||||
# This case is bad, because the sum is `3`, which is an integer, and will
|
||||
# truncate when divided by the length `2`. This case SHOULD be found by the
|
||||
# query.
|
||||
|
||||
# NOTE (2020-02-20):
|
||||
# The current version of the Value/pointsTo API doesn't permit this detection,
|
||||
# unfortunately, but we preserve this example in the hopes that future
|
||||
# versions will catch it. That will necessitate changing the expected results.
|
||||
|
||||
def average(l):
|
||||
return sum(l) / len(l)
|
||||
|
||||
print(average([1,2]))
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
| UseofApply.py:19:3:19:17 | ControlFlowNode for apply() | Call to the obsolete builtin function 'apply'. |
|
||||
| expressions_test.py:3:5:3:21 | ControlFlowNode for apply() | Call to the obsolete builtin function 'apply'. |
|
||||
|
||||
30
python/ql/test/2/query-tests/Expressions/UseofApply.py
Normal file
30
python/ql/test/2/query-tests/Expressions/UseofApply.py
Normal file
@@ -0,0 +1,30 @@
|
||||
#### UseofApply.ql
|
||||
|
||||
# Use of the builtin function `apply` is generally considered bad now that the
|
||||
# ability to destructure lists of arguments is possible, but we should not flag
|
||||
# cases where the function is merely named `apply` rather than being the actual
|
||||
# builtin `apply` function.
|
||||
|
||||
def useofapply():
|
||||
|
||||
def foo():
|
||||
pass
|
||||
|
||||
|
||||
|
||||
# Positive Cases
|
||||
|
||||
# This use of `apply` is a reference to the builtin function and so SHOULD be
|
||||
# caught by the query.
|
||||
apply(foo, [1])
|
||||
|
||||
|
||||
|
||||
# Negative Cases
|
||||
|
||||
# This use of `apply` is a reference to the locally defined function inside of
|
||||
# `local`, and so SHOULD NOT be caught by the query.
|
||||
def local():
|
||||
def apply(f):
|
||||
pass
|
||||
apply(foo)([1])
|
||||
@@ -1 +1 @@
|
||||
| expressions_test.py:6:12:6:18 | ControlFlowNode for input() | The unsafe built-in function 'input' is used. |
|
||||
| expressions_test.py:6:12:6:18 | ControlFlowNode for input() | The unsafe built-in function 'input' is used in Python 2. |
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --lang=3 -p ../../lib/ --max-import-depth=3
|
||||
optimize: true
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=4
|
||||
optimize: true
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
| test.py:11 | extended_unpacking | first | externally controlled string |
|
||||
| test.py:11 | extended_unpacking | last | externally controlled string |
|
||||
| test.py:11 | extended_unpacking | rest | [externally controlled string] |
|
||||
| test.py:16 | also_allowed | a | [externally controlled string] |
|
||||
| test.py:11 | extended_unpacking | first | NO TAINT |
|
||||
| test.py:11 | extended_unpacking | last | NO TAINT |
|
||||
| test.py:11 | extended_unpacking | rest | NO TAINT |
|
||||
| test.py:16 | also_allowed | a | NO TAINT |
|
||||
| test.py:24 | also_allowed | b | NO TAINT |
|
||||
| test.py:24 | also_allowed | c | NO TAINT |
|
||||
| test.py:31 | nested | x | externally controlled string |
|
||||
| test.py:31 | nested | xs | [externally controlled string] |
|
||||
| test.py:31 | nested | ys | [externally controlled string] |
|
||||
| test.py:31 | nested | x | NO TAINT |
|
||||
| test.py:31 | nested | xs | NO TAINT |
|
||||
| test.py:31 | nested | ys | NO TAINT |
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#### UseofApply.ql
|
||||
|
||||
# Use of the builtin function `apply` is generally considered bad now that the
|
||||
# ability to destructure lists of arguments is possible, but we should not flag
|
||||
# cases where the function is merely named `apply` rather than being the actual
|
||||
# builtin `apply` function.
|
||||
|
||||
def useofapply():
|
||||
|
||||
def foo():
|
||||
pass
|
||||
|
||||
|
||||
|
||||
# Positive Cases
|
||||
|
||||
# This use of `apply` is a reference to the builtin function and so SHOULD be
|
||||
# caught by the query.
|
||||
apply(foo, [1])
|
||||
|
||||
|
||||
|
||||
# Negative Cases
|
||||
|
||||
# This use of `apply` is a reference to the locally defined function inside of
|
||||
# `local`, and so SHOULD NOT be caught by the query.
|
||||
def local():
|
||||
def apply(f):
|
||||
pass
|
||||
apply(foo)([1])
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/UseofApply.ql
|
||||
@@ -0,0 +1,7 @@
|
||||
123456789
|
||||
223456789
|
||||
323456789
|
||||
423456789
|
||||
5234567ø9
|
||||
623456789
|
||||
723456789
|
||||
@@ -0,0 +1 @@
|
||||
Imports/EncodingError.ql
|
||||
@@ -0,0 +1 @@
|
||||
| bad_encoding.py:11:19:11:19 | Encoding Error | 'utf-8' codec can't decode byte 0x82 in position 82: invalid start byte |
|
||||
@@ -0,0 +1 @@
|
||||
Imports/EncodingError.ql
|
||||
@@ -0,0 +1 @@
|
||||
| nonsense.py:1:2:1:2 | Syntax Error | Syntax Error (in Python 3.5). |
|
||||
@@ -0,0 +1 @@
|
||||
Imports/SyntaxError.ql
|
||||
@@ -0,0 +1,12 @@
|
||||
"""Multi-line docstring
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# encoding:shift-jis
|
||||
|
||||
def f():
|
||||
print "Python <20>̊J<CC8A><4A><EFBFBD>́A1990 <20>N<EFBFBD><4E><EFBFBD>납<EFBFBD><EB82A9><EFBFBD>J<EFBFBD>n<EFBFBD><6E><EFBFBD><EFBFBD><EFBFBD>Ă<EFBFBD><C482>܂<EFBFBD>"
|
||||
"""
|
||||
@@ -0,0 +1,37 @@
|
||||
`Twas brillig, and the slithy toves
|
||||
Did gyre and gimble in the wabe:
|
||||
All mimsy were the borogoves,
|
||||
And the mome raths outgrabe.
|
||||
|
||||
|
||||
"Beware the Jabberwock, my son!
|
||||
The jaws that bite, the claws that catch!
|
||||
Beware the Jubjub bird, and shun
|
||||
The frumious Bandersnatch!"
|
||||
|
||||
He took his vorpal sword in hand:
|
||||
Long time the manxome foe he sought --
|
||||
So rested he by the Tumtum tree,
|
||||
And stood awhile in thought.
|
||||
|
||||
And, as in uffish thought he stood,
|
||||
The Jabberwock, with eyes of flame,
|
||||
Came whiffling through the tulgey wood,
|
||||
And burbled as it came!
|
||||
|
||||
One, two! One, two! And through and through
|
||||
The vorpal blade went snicker-snack!
|
||||
He left it dead, and with its head
|
||||
He went galumphing back.
|
||||
|
||||
"And, has thou slain the Jabberwock?
|
||||
Come to my arms, my beamish boy!
|
||||
O frabjous day! Callooh! Callay!'
|
||||
He chortled in his joy.
|
||||
|
||||
|
||||
`Twas brillig, and the slithy toves
|
||||
Did gyre and gimble in the wabe;
|
||||
All mimsy were the borogoves,
|
||||
And the mome raths outgrabe.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
print "Hello World"
|
||||
1
python/ql/test/experimental/README.md
Normal file
1
python/ql/test/experimental/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This directory contains tests for [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=3
|
||||
optimize: true
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=4
|
||||
optimize: true
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.py:5:7:5:9 | ControlFlowNode for foo | int 42 |
|
||||
| test.py:11:11:11:13 | ControlFlowNode for foo | int 1 |
|
||||
| test.py:17:11:17:13 | ControlFlowNode for foo | <MISSING pointsTo()> |
|
||||
@@ -0,0 +1,10 @@
|
||||
import python
|
||||
|
||||
from NameNode name, CallNode call, string debug
|
||||
where
|
||||
call.getAnArg() = name and
|
||||
call.getFunction().(NameNode).getId() = "check" and
|
||||
if exists(name.pointsTo())
|
||||
then debug = name.pointsTo().toString()
|
||||
else debug = "<MISSING pointsTo()>"
|
||||
select name, debug
|
||||
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=1 --lang=3
|
||||
@@ -0,0 +1,17 @@
|
||||
# Only a problem in Python 3
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
foo = 42
|
||||
check(foo)
|
||||
|
||||
def func(url):
|
||||
parts = urlsplit(url)
|
||||
|
||||
foo = 1
|
||||
check(foo)
|
||||
|
||||
if parts.path: # using `urlsplit(url).path` here is equivalent
|
||||
return # using `pass` here instead makes points-to work
|
||||
|
||||
foo = 2
|
||||
check(foo) # no points-to information
|
||||
@@ -0,0 +1 @@
|
||||
| test.py:5:7:5:13 | ControlFlowNode for PATTERN | <MISSING pointsTo()> |
|
||||
@@ -0,0 +1,10 @@
|
||||
import python
|
||||
|
||||
from NameNode name, CallNode call, string debug
|
||||
where
|
||||
call.getAnArg() = name and
|
||||
call.getFunction().(NameNode).getId() = "check" and
|
||||
if exists(name.pointsTo())
|
||||
then debug = name.pointsTo().toString()
|
||||
else debug = "<MISSING pointsTo()>"
|
||||
select name, debug
|
||||
@@ -0,0 +1,5 @@
|
||||
import re
|
||||
|
||||
PATTERN = re.compile("a|b")
|
||||
|
||||
check(PATTERN)
|
||||
@@ -1 +0,0 @@
|
||||
optimize: true
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: -R .
|
||||
optimize: true
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=3
|
||||
optimize: true
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
| Taint [externally controlled string] | test.py:29 | test.py:29:9:29:25 | Subscript | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:16:32:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:20 | tainted_list | | --> | Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | |
|
||||
| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:19:32:19 | d | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:22:32:22 | e | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:25:32:25 | f | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:28:32:28 | g | |
|
||||
| Taint [externally controlled string] | test.py:33 | test.py:33:14:33:25 | tainted_list | | --> | Taint externally controlled string | test.py:33 | test.py:33:5:33:26 | For | |
|
||||
| Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | | --> | Taint externally controlled string | test.py:35 | test.py:35:5:35:36 | For | |
|
||||
| Taint [externally controlled string] | test.py:35 | test.py:35:23:35:34 | tainted_list | | --> | Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | |
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
| test.py:32 | test_access | b | externally controlled string |
|
||||
| test.py:32 | test_access | c | [externally controlled string] |
|
||||
| test.py:32 | test_access | d | [externally controlled string] |
|
||||
| test.py:32 | test_access | e | externally controlled string |
|
||||
| test.py:32 | test_access | f | externally controlled string |
|
||||
| test.py:32 | test_access | g | externally controlled string |
|
||||
| test.py:32 | test_access | e | NO TAINT |
|
||||
| test.py:32 | test_access | f | NO TAINT |
|
||||
| test.py:32 | test_access | g | NO TAINT |
|
||||
| test.py:34 | test_access | h | externally controlled string |
|
||||
| test.py:36 | test_access | i | externally controlled string |
|
||||
| test.py:43 | test_dict_access | a | externally controlled string |
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
| UrlsplitUrlparseTempSanitizer | [externally controlled string] | test.py:21 | Pi(urlsplit_res_0) [true] |
|
||||
| UrlsplitUrlparseTempSanitizer | [externally controlled string] | test.py:24 | Pi(urlsplit_res_3) [true] |
|
||||
| UrlsplitUrlparseTempSanitizer | [externally controlled string] | test.py:27 | Pi(urlsplit_res_6) [true] |
|
||||
| UrlsplitUrlparseTempSanitizer | [externally controlled string] | test.py:30 | Pi(urlsplit_res_9) [true] |
|
||||
| string equality sanitizer | externally controlled string | test.py:21 | Pi(urlsplit_res_0) [true] |
|
||||
| string equality sanitizer | externally controlled string | test.py:24 | Pi(urlsplit_res_3) [true] |
|
||||
| string equality sanitizer | externally controlled string | test.py:27 | Pi(urlsplit_res_6) [true] |
|
||||
@@ -0,0 +1,6 @@
|
||||
import python
|
||||
import Taint
|
||||
|
||||
from Sanitizer s, TaintKind taint, PyEdgeRefinement test
|
||||
where s.sanitizingEdge(taint, test)
|
||||
select s, taint, test.getTest().getLocation().toString(), test.getRepresentation()
|
||||
47
python/ql/test/library-tests/taint/namedtuple/Taint.qll
Normal file
47
python/ql/test/library-tests/taint/namedtuple/Taint.qll
Normal file
@@ -0,0 +1,47 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
class SimpleSource extends TaintSource {
|
||||
SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
|
||||
override string toString() { result = "taint source" }
|
||||
}
|
||||
|
||||
class ListSource extends TaintSource {
|
||||
ListSource() { this.(NameNode).getId() = "TAINTED_LIST" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind }
|
||||
|
||||
override string toString() { result = "list taint source" }
|
||||
}
|
||||
|
||||
class DictSource extends TaintSource {
|
||||
DictSource() { this.(NameNode).getId() = "TAINTED_DICT" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind }
|
||||
|
||||
override string toString() { result = "dict taint source" }
|
||||
}
|
||||
|
||||
class TestConfig extends TaintTracking::Configuration {
|
||||
TestConfig() { this = "TestConfig" }
|
||||
|
||||
override predicate isSanitizer(Sanitizer sanitizer) {
|
||||
sanitizer instanceof UrlsplitUrlparseTempSanitizer
|
||||
}
|
||||
|
||||
override predicate isSource(TaintTracking::Source source) {
|
||||
source instanceof SimpleSource
|
||||
or
|
||||
source instanceof ListSource
|
||||
or
|
||||
source instanceof DictSource
|
||||
}
|
||||
|
||||
override predicate isSink(TaintTracking::Sink sink) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
| test.py:13 | test_basic | a | externally controlled string |
|
||||
| test.py:13 | test_basic | b | externally controlled string |
|
||||
| test.py:13 | test_basic | c | externally controlled string |
|
||||
| test.py:13 | test_basic | d | NO TAINT |
|
||||
| test.py:13 | test_basic | urlsplit_res | [externally controlled string] |
|
||||
| test.py:19 | test_sanitizer | Attribute | externally controlled string |
|
||||
| test.py:22 | test_sanitizer | Attribute | NO TAINT |
|
||||
| test.py:25 | test_sanitizer | Subscript | NO TAINT |
|
||||
| test.py:28 | test_sanitizer | Attribute | NO TAINT |
|
||||
| test.py:31 | test_sanitizer | Attribute | NO TAINT |
|
||||
| test.py:34 | test_sanitizer | Attribute | externally controlled string |
|
||||
| test.py:44 | test_namedtuple | a | NO TAINT |
|
||||
| test.py:44 | test_namedtuple | b | NO TAINT |
|
||||
| test.py:44 | test_namedtuple | c | NO TAINT |
|
||||
| test.py:44 | test_namedtuple | d | NO TAINT |
|
||||
18
python/ql/test/library-tests/taint/namedtuple/TestTaint.ql
Normal file
18
python/ql/test/library-tests/taint/namedtuple/TestTaint.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import Taint
|
||||
|
||||
from Call call, Expr arg, string taint_string
|
||||
where
|
||||
call.getLocation().getFile().getShortName() = "test.py" and
|
||||
call.getFunc().(Name).getId() = "test" and
|
||||
arg = call.getAnArg() and
|
||||
(
|
||||
not exists(TaintedNode tainted | tainted.getAstNode() = arg) and
|
||||
taint_string = "NO TAINT"
|
||||
or
|
||||
exists(TaintedNode tainted | tainted.getAstNode() = arg |
|
||||
taint_string = tainted.getTaintKind().toString()
|
||||
)
|
||||
)
|
||||
select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), taint_string
|
||||
44
python/ql/test/library-tests/taint/namedtuple/test.py
Normal file
44
python/ql/test/library-tests/taint/namedtuple/test.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from six.moves.urllib.parse import urlsplit
|
||||
|
||||
# Currently we don't have support for namedtuples in general, but do have special support
|
||||
# for `urlsplit` (and `urlparse`)
|
||||
|
||||
def test_basic():
|
||||
tainted_string = TAINTED_STRING
|
||||
urlsplit_res = urlsplit(tainted_string)
|
||||
a = urlsplit_res.netloc # field access
|
||||
b = urlsplit_res.hostname # property
|
||||
c = urlsplit_res[3] # indexing
|
||||
_, _, d, _, _ = urlsplit(tainted_string) # unpacking
|
||||
test(a, b, c, d, urlsplit_res)
|
||||
|
||||
def test_sanitizer():
|
||||
tainted_string = TAINTED_STRING
|
||||
urlsplit_res = urlsplit(tainted_string)
|
||||
|
||||
test(urlsplit_res.netloc) # should be tainted
|
||||
|
||||
if urlsplit_res.netloc == "OK":
|
||||
test(urlsplit_res.netloc)
|
||||
|
||||
if urlsplit_res[2] == "OK":
|
||||
test(urlsplit_res[0])
|
||||
|
||||
if urlsplit_res.netloc == "OK":
|
||||
test(urlsplit_res.path) # FN
|
||||
|
||||
if urlsplit_res.netloc in ["OK"]:
|
||||
test(urlsplit_res.netloc)
|
||||
|
||||
if urlsplit_res.netloc in ["OK", non_constant()]:
|
||||
test(urlsplit_res.netloc) # should be tainted
|
||||
|
||||
def test_namedtuple():
|
||||
tainted_string = TAINTED_STRING
|
||||
Point = namedtuple('Point', ['x', 'y'])
|
||||
p = Point('safe', tainted_string)
|
||||
a = p.x
|
||||
b = p.y
|
||||
c = p[0]
|
||||
d = p[1]
|
||||
test(a, b, c, d) # TODO: FN, at least p.y and p[1] should be tainted
|
||||
@@ -1,16 +0,0 @@
|
||||
| Taint exception.info | test.py:41 | test.py:41:22:41:26 | taint | p1 = exception.info |
|
||||
| Taint exception.info | test.py:42 | test.py:42:12:42:22 | func() | p1 = exception.info |
|
||||
| Taint exception.info | test.py:42 | test.py:42:17:42:21 | taint | p1 = exception.info |
|
||||
| Taint exception.info | test.py:45 | test.py:45:12:45:33 | TAINTED_EXCEPTION_INFO | |
|
||||
| Taint exception.info | test.py:46 | test.py:46:11:46:41 | cross_over() | |
|
||||
| Taint exception.info | test.py:46 | test.py:46:37:46:40 | info | |
|
||||
| Taint exception.info | test.py:48 | test.py:48:19:48:21 | arg | p0 = exception.info |
|
||||
| Taint exception.info | test.py:49 | test.py:49:12:49:14 | arg | p0 = exception.info |
|
||||
| Taint externally controlled string | test.py:41 | test.py:41:22:41:26 | taint | p1 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:42 | test.py:42:12:42:22 | func() | p1 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:42 | test.py:42:17:42:21 | taint | p1 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:48 | test.py:48:19:48:21 | arg | p0 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:49 | test.py:49:12:49:14 | arg | p0 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:52 | test.py:52:11:52:33 | TAINTED_EXTERNAL_STRING | |
|
||||
| Taint externally controlled string | test.py:53 | test.py:53:11:53:41 | cross_over() | |
|
||||
| Taint externally controlled string | test.py:53 | test.py:53:38:53:40 | ext | |
|
||||
@@ -1,37 +0,0 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
import semmle.python.security.Exceptions
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
|
||||
class ExceptionInfoSource extends TaintSource {
|
||||
|
||||
ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof ExceptionInfo
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Exception info source"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ExternalStringSource extends TaintSource {
|
||||
|
||||
ExternalStringSource() { this.(NameNode).getId() = "TAINTED_EXTERNAL_STRING" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Untrusted string source"
|
||||
}
|
||||
|
||||
}
|
||||
from TaintedNode n
|
||||
where n.getLocation().getFile().getShortName() = "test.py"
|
||||
select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext()
|
||||
@@ -1,11 +1,12 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.security.Exceptions
|
||||
|
||||
|
||||
class SimpleSource extends TaintSource {
|
||||
|
||||
SimpleSource() { this.(NameNode).getId() = "TAINTED" }
|
||||
SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof ExternalStringKind
|
||||
@@ -46,3 +47,16 @@ class DictSource extends TaintSource {
|
||||
}
|
||||
|
||||
|
||||
class ExceptionInfoSource extends TaintSource {
|
||||
|
||||
ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof ExceptionInfo
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Exception info source"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
| Taint externally controlled string | test.py:5 | test.py:5:22:5:28 | TAINTED | |
|
||||
| Taint externally controlled string | test.py:6 | test.py:6:31:6:44 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:7 | test.py:7:9:7:25 | Subscript | |
|
||||
| Taint externally controlled string | test.py:8 | test.py:8:9:8:9 | a | |
|
||||
| Taint externally controlled string | test.py:8 | test.py:8:9:8:18 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:9 | test.py:9:9:9:9 | b | |
|
||||
| Taint externally controlled string | test.py:9 | test.py:9:9:9:14 | Subscript | |
|
||||
| Taint externally controlled string | test.py:12 | test.py:12:22:12:28 | TAINTED | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:9:13:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:9:13:31 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:14 | test.py:14:9:14:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:14 | test.py:14:9:14:29 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:15 | test.py:15:9:15:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:15 | test.py:15:9:15:25 | Subscript | |
|
||||
| Taint externally controlled string | test.py:16 | test.py:16:9:16:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:16 | test.py:16:9:16:27 | Subscript | |
|
||||
| Taint externally controlled string | test.py:17 | test.py:17:9:17:32 | reversed() | |
|
||||
| Taint externally controlled string | test.py:17 | test.py:17:18:17:31 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:18 | test.py:18:9:18:28 | copy() | |
|
||||
| Taint externally controlled string | test.py:18 | test.py:18:14:18:27 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:19 | test.py:19:9:19:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:19 | test.py:19:9:19:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:22 | test.py:22:22:22:28 | TAINTED | |
|
||||
| Taint externally controlled string | test.py:23 | test.py:23:8:23:21 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:26 | test.py:26:23:26:36 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:29 | test.py:29:22:29:28 | TAINTED | |
|
||||
| Taint externally controlled string | test.py:30 | test.py:30:8:30:21 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:30 | test.py:30:34:30:47 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:33 | test.py:33:23:33:36 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:36 | test.py:36:22:36:28 | TAINTED | |
|
||||
| Taint externally controlled string | test.py:37 | test.py:37:9:37:27 | str() | |
|
||||
| Taint externally controlled string | test.py:37 | test.py:37:13:37:26 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:38 | test.py:38:9:38:29 | bytes() | |
|
||||
| Taint externally controlled string | test.py:38 | test.py:38:15:38:28 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:39 | test.py:39:9:39:46 | bytes() | |
|
||||
| Taint externally controlled string | test.py:39 | test.py:39:15:39:28 | tainted_string | |
|
||||
| Taint json[externally controlled string] | test.py:6 | test.py:6:20:6:45 | Attribute() | |
|
||||
| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | |
|
||||
| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:25 | Subscript | |
|
||||
| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:9 | a | |
|
||||
| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:18 | Attribute() | |
|
||||
| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | b | |
|
||||
| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:14 | Subscript | |
|
||||
@@ -1,8 +0,0 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import Taint
|
||||
|
||||
|
||||
from TaintedNode n
|
||||
where n.getLocation().getFile().getShortName() = "test.py"
|
||||
select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getCfgNode().getNode(), n.getContext()
|
||||
@@ -1,38 +1,76 @@
|
||||
| Taint externally controlled string | test.py:5 | test.py:5:22:5:28 | TAINTED | | --> | Taint externally controlled string | test.py:6 | test.py:6:31:6:44 | tainted_string | |
|
||||
| Taint [externally controlled string] | test.py:67 | test.py:67:20:67:43 | urlsplit() | | --> | Taint [externally controlled string] | test.py:69 | test.py:69:10:69:21 | urlsplit_res | |
|
||||
| Taint [externally controlled string] | test.py:68 | test.py:68:20:68:43 | urlparse() | | --> | Taint [externally controlled string] | test.py:69 | test.py:69:24:69:35 | urlparse_res | |
|
||||
| Taint exception.info | test.py:44 | test.py:44:22:44:26 | taint | p1 = exception.info | --> | Taint exception.info | test.py:45 | test.py:45:17:45:21 | taint | p1 = exception.info |
|
||||
| Taint exception.info | test.py:45 | test.py:45:17:45:21 | taint | p1 = exception.info | --> | Taint exception.info | test.py:45 | test.py:45:12:45:22 | func() | p1 = exception.info |
|
||||
| Taint exception.info | test.py:45 | test.py:45:17:45:21 | taint | p1 = exception.info | --> | Taint exception.info | test.py:52 | test.py:52:19:52:21 | arg | p0 = exception.info |
|
||||
| Taint exception.info | test.py:48 | test.py:48:12:48:33 | TAINTED_EXCEPTION_INFO | | --> | Taint exception.info | test.py:49 | test.py:49:37:49:40 | info | |
|
||||
| Taint exception.info | test.py:49 | test.py:49:11:49:41 | cross_over() | | --> | Taint exception.info | test.py:50 | test.py:50:10:50:12 | res | |
|
||||
| Taint exception.info | test.py:49 | test.py:49:37:49:40 | info | | --> | Taint exception.info | test.py:44 | test.py:44:22:44:26 | taint | p1 = exception.info |
|
||||
| Taint exception.info | test.py:49 | test.py:49:37:49:40 | info | | --> | Taint exception.info | test.py:49 | test.py:49:11:49:41 | cross_over() | |
|
||||
| Taint exception.info | test.py:52 | test.py:52:19:52:21 | arg | p0 = exception.info | --> | Taint exception.info | test.py:53 | test.py:53:12:53:14 | arg | p0 = exception.info |
|
||||
| Taint externally controlled string | test.py:5 | test.py:5:22:5:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:6 | test.py:6:31:6:44 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:6 | test.py:6:31:6:44 | tainted_string | | --> | Taint json[externally controlled string] | test.py:6 | test.py:6:20:6:45 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:7 | test.py:7:9:7:25 | Subscript | | --> | Taint externally controlled string | test.py:8 | test.py:8:9:8:9 | a | |
|
||||
| Taint externally controlled string | test.py:7 | test.py:7:9:7:25 | Subscript | | --> | Taint externally controlled string | test.py:10 | test.py:10:10:10:10 | a | |
|
||||
| Taint externally controlled string | test.py:8 | test.py:8:9:8:18 | Attribute() | | --> | Taint externally controlled string | test.py:9 | test.py:9:9:9:9 | b | |
|
||||
| Taint externally controlled string | test.py:12 | test.py:12:22:12:28 | TAINTED | | --> | Taint externally controlled string | test.py:13 | test.py:13:9:13:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:12 | test.py:12:22:12:28 | TAINTED | | --> | Taint externally controlled string | test.py:14 | test.py:14:9:14:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:12 | test.py:12:22:12:28 | TAINTED | | --> | Taint externally controlled string | test.py:15 | test.py:15:9:15:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:12 | test.py:12:22:12:28 | TAINTED | | --> | Taint externally controlled string | test.py:16 | test.py:16:9:16:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:12 | test.py:12:22:12:28 | TAINTED | | --> | Taint externally controlled string | test.py:17 | test.py:17:18:17:31 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:12 | test.py:12:22:12:28 | TAINTED | | --> | Taint externally controlled string | test.py:18 | test.py:18:14:18:27 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:12 | test.py:12:22:12:28 | TAINTED | | --> | Taint externally controlled string | test.py:19 | test.py:19:9:19:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:9:13:22 | tainted_string | | --> | Taint externally controlled string | test.py:13 | test.py:13:9:13:31 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:14 | test.py:14:9:14:22 | tainted_string | | --> | Taint externally controlled string | test.py:14 | test.py:14:9:14:29 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:15 | test.py:15:9:15:22 | tainted_string | | --> | Taint externally controlled string | test.py:15 | test.py:15:9:15:25 | Subscript | |
|
||||
| Taint externally controlled string | test.py:16 | test.py:16:9:16:22 | tainted_string | | --> | Taint externally controlled string | test.py:16 | test.py:16:9:16:27 | Subscript | |
|
||||
| Taint externally controlled string | test.py:17 | test.py:17:18:17:31 | tainted_string | | --> | Taint externally controlled string | test.py:17 | test.py:17:9:17:32 | reversed() | |
|
||||
| Taint externally controlled string | test.py:18 | test.py:18:14:18:27 | tainted_string | | --> | Taint externally controlled string | test.py:18 | test.py:18:9:18:28 | copy() | |
|
||||
| Taint externally controlled string | test.py:19 | test.py:19:9:19:22 | tainted_string | | --> | Taint externally controlled string | test.py:19 | test.py:19:9:19:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:22 | test.py:22:22:22:28 | TAINTED | | --> | Taint externally controlled string | test.py:23 | test.py:23:8:23:21 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:22 | test.py:22:22:22:28 | TAINTED | | --> | Taint externally controlled string | test.py:26 | test.py:26:23:26:36 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:29 | test.py:29:22:29:28 | TAINTED | | --> | Taint externally controlled string | test.py:30 | test.py:30:8:30:21 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:29 | test.py:29:22:29:28 | TAINTED | | --> | Taint externally controlled string | test.py:30 | test.py:30:34:30:47 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:29 | test.py:29:22:29:28 | TAINTED | | --> | Taint externally controlled string | test.py:33 | test.py:33:23:33:36 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:36 | test.py:36:22:36:28 | TAINTED | | --> | Taint externally controlled string | test.py:37 | test.py:37:13:37:26 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:36 | test.py:36:22:36:28 | TAINTED | | --> | Taint externally controlled string | test.py:38 | test.py:38:15:38:28 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:36 | test.py:36:22:36:28 | TAINTED | | --> | Taint externally controlled string | test.py:39 | test.py:39:15:39:28 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:37 | test.py:37:13:37:26 | tainted_string | | --> | Taint externally controlled string | test.py:37 | test.py:37:9:37:27 | str() | |
|
||||
| Taint externally controlled string | test.py:38 | test.py:38:15:38:28 | tainted_string | | --> | Taint externally controlled string | test.py:38 | test.py:38:9:38:29 | bytes() | |
|
||||
| Taint externally controlled string | test.py:39 | test.py:39:15:39:28 | tainted_string | | --> | Taint externally controlled string | test.py:39 | test.py:39:9:39:46 | bytes() | |
|
||||
| Taint externally controlled string | test.py:8 | test.py:8:9:8:18 | Attribute() | | --> | Taint externally controlled string | test.py:10 | test.py:10:13:10:13 | b | |
|
||||
| Taint externally controlled string | test.py:9 | test.py:9:9:9:14 | Subscript | | --> | Taint externally controlled string | test.py:10 | test.py:10:16:10:16 | c | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:22:13:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:14 | test.py:14:9:14:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:22:13:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:15 | test.py:15:9:15:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:22:13:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:16 | test.py:16:9:16:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:22:13:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:17 | test.py:17:9:17:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:22:13:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:18 | test.py:18:18:18:31 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:22:13:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:19 | test.py:19:14:19:27 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:13 | test.py:13:22:13:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:20 | test.py:20:9:20:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:14 | test.py:14:9:14:22 | tainted_string | | --> | Taint externally controlled string | test.py:14 | test.py:14:9:14:31 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:14 | test.py:14:9:14:31 | Attribute() | | --> | Taint externally controlled string | test.py:21 | test.py:21:10:21:10 | a | |
|
||||
| Taint externally controlled string | test.py:15 | test.py:15:9:15:22 | tainted_string | | --> | Taint externally controlled string | test.py:15 | test.py:15:9:15:29 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:15 | test.py:15:9:15:29 | Attribute() | | --> | Taint externally controlled string | test.py:21 | test.py:21:13:21:13 | b | |
|
||||
| Taint externally controlled string | test.py:16 | test.py:16:9:16:22 | tainted_string | | --> | Taint externally controlled string | test.py:16 | test.py:16:9:16:25 | Subscript | |
|
||||
| Taint externally controlled string | test.py:16 | test.py:16:9:16:25 | Subscript | | --> | Taint externally controlled string | test.py:21 | test.py:21:16:21:16 | c | |
|
||||
| Taint externally controlled string | test.py:17 | test.py:17:9:17:22 | tainted_string | | --> | Taint externally controlled string | test.py:17 | test.py:17:9:17:27 | Subscript | |
|
||||
| Taint externally controlled string | test.py:17 | test.py:17:9:17:27 | Subscript | | --> | Taint externally controlled string | test.py:21 | test.py:21:19:21:19 | d | |
|
||||
| Taint externally controlled string | test.py:18 | test.py:18:9:18:32 | reversed() | | --> | Taint externally controlled string | test.py:21 | test.py:21:22:21:22 | e | |
|
||||
| Taint externally controlled string | test.py:18 | test.py:18:18:18:31 | tainted_string | | --> | Taint externally controlled string | test.py:18 | test.py:18:9:18:32 | reversed() | |
|
||||
| Taint externally controlled string | test.py:19 | test.py:19:9:19:28 | copy() | | --> | Taint externally controlled string | test.py:21 | test.py:21:25:21:25 | f | |
|
||||
| Taint externally controlled string | test.py:19 | test.py:19:14:19:27 | tainted_string | | --> | Taint externally controlled string | test.py:19 | test.py:19:9:19:28 | copy() | |
|
||||
| Taint externally controlled string | test.py:20 | test.py:20:9:20:22 | tainted_string | | --> | Taint externally controlled string | test.py:20 | test.py:20:9:20:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:20 | test.py:20:9:20:30 | Attribute() | | --> | Taint externally controlled string | test.py:21 | test.py:21:28:21:28 | g | |
|
||||
| Taint externally controlled string | test.py:24 | test.py:24:22:24:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:25 | test.py:25:8:25:21 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:24 | test.py:24:22:24:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:28 | test.py:28:14:28:27 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:31 | test.py:31:22:31:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:32 | test.py:32:8:32:21 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:31 | test.py:31:22:31:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:32 | test.py:32:34:32:47 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:31 | test.py:31:22:31:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:35 | test.py:35:14:35:27 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:38 | test.py:38:22:38:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:39 | test.py:39:13:39:26 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:38 | test.py:38:22:38:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:40 | test.py:40:15:40:28 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:38 | test.py:38:22:38:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:41 | test.py:41:15:41:28 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:39 | test.py:39:9:39:27 | str() | | --> | Taint externally controlled string | test.py:42 | test.py:42:10:42:10 | a | |
|
||||
| Taint externally controlled string | test.py:39 | test.py:39:13:39:26 | tainted_string | | --> | Taint externally controlled string | test.py:39 | test.py:39:9:39:27 | str() | |
|
||||
| Taint externally controlled string | test.py:40 | test.py:40:9:40:29 | bytes() | | --> | Taint externally controlled string | test.py:42 | test.py:42:13:42:13 | b | |
|
||||
| Taint externally controlled string | test.py:40 | test.py:40:15:40:28 | tainted_string | | --> | Taint externally controlled string | test.py:40 | test.py:40:9:40:29 | bytes() | |
|
||||
| Taint externally controlled string | test.py:41 | test.py:41:9:41:46 | bytes() | | --> | Taint externally controlled string | test.py:42 | test.py:42:16:42:16 | c | |
|
||||
| Taint externally controlled string | test.py:41 | test.py:41:15:41:28 | tainted_string | | --> | Taint externally controlled string | test.py:41 | test.py:41:9:41:46 | bytes() | |
|
||||
| Taint externally controlled string | test.py:44 | test.py:44:22:44:26 | taint | p1 = externally controlled string | --> | Taint externally controlled string | test.py:45 | test.py:45:17:45:21 | taint | p1 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:45 | test.py:45:17:45:21 | taint | p1 = externally controlled string | --> | Taint externally controlled string | test.py:45 | test.py:45:12:45:22 | func() | p1 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:45 | test.py:45:17:45:21 | taint | p1 = externally controlled string | --> | Taint externally controlled string | test.py:52 | test.py:52:19:52:21 | arg | p0 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:52 | test.py:52:19:52:21 | arg | p0 = externally controlled string | --> | Taint externally controlled string | test.py:53 | test.py:53:12:53:14 | arg | p0 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:56 | test.py:56:11:56:24 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:57 | test.py:57:38:57:40 | ext | |
|
||||
| Taint externally controlled string | test.py:57 | test.py:57:11:57:41 | cross_over() | | --> | Taint externally controlled string | test.py:58 | test.py:58:10:58:12 | res | |
|
||||
| Taint externally controlled string | test.py:57 | test.py:57:38:57:40 | ext | | --> | Taint externally controlled string | test.py:44 | test.py:44:22:44:26 | taint | p1 = externally controlled string |
|
||||
| Taint externally controlled string | test.py:57 | test.py:57:38:57:40 | ext | | --> | Taint externally controlled string | test.py:57 | test.py:57:11:57:41 | cross_over() | |
|
||||
| Taint externally controlled string | test.py:66 | test.py:66:22:66:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:67 | test.py:67:29:67:42 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:66 | test.py:66:22:66:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:68 | test.py:68:29:68:42 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:67 | test.py:67:29:67:42 | tainted_string | | --> | Taint [externally controlled string] | test.py:67 | test.py:67:20:67:43 | urlsplit() | |
|
||||
| Taint externally controlled string | test.py:68 | test.py:68:29:68:42 | tainted_string | | --> | Taint [externally controlled string] | test.py:68 | test.py:68:20:68:43 | urlparse() | |
|
||||
| Taint json[externally controlled string] | test.py:6 | test.py:6:20:6:45 | Attribute() | | --> | Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | |
|
||||
| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | | --> | Taint externally controlled string | test.py:7 | test.py:7:9:7:25 | Subscript | |
|
||||
| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | | --> | Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:25 | Subscript | |
|
||||
| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:25 | Subscript | | --> | Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:9 | a | |
|
||||
| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:25 | Subscript | | --> | Taint json[externally controlled string] | test.py:10 | test.py:10:10:10:10 | a | |
|
||||
| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:9 | a | | --> | Taint externally controlled string | test.py:8 | test.py:8:9:8:18 | Attribute() | |
|
||||
| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:9 | a | | --> | Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:18 | Attribute() | |
|
||||
| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:18 | Attribute() | | --> | Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | b | |
|
||||
| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:18 | Attribute() | | --> | Taint json[externally controlled string] | test.py:10 | test.py:10:13:10:13 | b | |
|
||||
| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | b | | --> | Taint externally controlled string | test.py:9 | test.py:9:9:9:14 | Subscript | |
|
||||
| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | b | | --> | Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:14 | Subscript | |
|
||||
| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:14 | Subscript | | --> | Taint json[externally controlled string] | test.py:10 | test.py:10:16:10:16 | c | |
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
| test.py:10 | test_json | a | externally controlled string |
|
||||
| test.py:10 | test_json | a | json[externally controlled string] |
|
||||
| test.py:10 | test_json | b | externally controlled string |
|
||||
| test.py:10 | test_json | b | json[externally controlled string] |
|
||||
| test.py:10 | test_json | c | externally controlled string |
|
||||
| test.py:10 | test_json | c | json[externally controlled string] |
|
||||
| test.py:21 | test_str | a | externally controlled string |
|
||||
| test.py:21 | test_str | b | externally controlled string |
|
||||
| test.py:21 | test_str | c | externally controlled string |
|
||||
| test.py:21 | test_str | d | externally controlled string |
|
||||
| test.py:21 | test_str | e | externally controlled string |
|
||||
| test.py:21 | test_str | f | externally controlled string |
|
||||
| test.py:21 | test_str | g | externally controlled string |
|
||||
| test.py:26 | test_const_sanitizer1 | tainted_string | NO TAINT |
|
||||
| test.py:28 | test_const_sanitizer1 | tainted_string | externally controlled string |
|
||||
| test.py:33 | test_const_sanitizer2 | tainted_string | NO TAINT |
|
||||
| test.py:35 | test_const_sanitizer2 | tainted_string | externally controlled string |
|
||||
| test.py:42 | test_str2 | a | externally controlled string |
|
||||
| test.py:42 | test_str2 | b | externally controlled string |
|
||||
| test.py:42 | test_str2 | c | externally controlled string |
|
||||
| test.py:50 | test_exc_info | res | exception.info |
|
||||
| test.py:58 | test_untrusted | res | externally controlled string |
|
||||
| test.py:69 | test_urlsplit_urlparse | urlparse_res | [externally controlled string] |
|
||||
| test.py:69 | test_urlsplit_urlparse | urlsplit_res | [externally controlled string] |
|
||||
18
python/ql/test/library-tests/taint/strings/TestTaint.ql
Normal file
18
python/ql/test/library-tests/taint/strings/TestTaint.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import Taint
|
||||
|
||||
from Call call, Expr arg, string taint_string
|
||||
where
|
||||
call.getLocation().getFile().getShortName() = "test.py" and
|
||||
call.getFunc().(Name).getId() = "test" and
|
||||
arg = call.getAnArg() and
|
||||
(
|
||||
not exists(TaintedNode tainted | tainted.getAstNode() = arg) and
|
||||
taint_string = "NO TAINT"
|
||||
or
|
||||
exists(TaintedNode tainted | tainted.getAstNode() = arg |
|
||||
taint_string = tainted.getTaintKind().toString()
|
||||
)
|
||||
)
|
||||
select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), taint_string
|
||||
@@ -2,41 +2,44 @@ import json
|
||||
from copy import copy
|
||||
|
||||
def test_json():
|
||||
tainted_string = TAINTED
|
||||
tainted_string = TAINTED_STRING
|
||||
tainted_json = json.loads(tainted_string)
|
||||
a = tainted_json["x"]
|
||||
b = a.get("y")
|
||||
c = b["z"]
|
||||
test(a, b, c)
|
||||
|
||||
def test_str():
|
||||
tainted_string = TAINTED
|
||||
tainted_string = TAINTED_STRING
|
||||
a = tainted_string.ljust(8)
|
||||
b = tainted_string.copy()
|
||||
c = tainted_string[:]
|
||||
d = tainted_string[::2]
|
||||
e = reversed(tainted_string)
|
||||
f = copy(tainted_string)
|
||||
h = tainted_string.strip()
|
||||
g = tainted_string.strip()
|
||||
test(a, b, c, d, e, f, g)
|
||||
|
||||
def test_const_sanitizer1():
|
||||
tainted_string = TAINTED
|
||||
tainted_string = TAINTED_STRING
|
||||
if tainted_string == "OK":
|
||||
not_tainted(tainted_string)
|
||||
test(tainted_string) # not tainted
|
||||
else:
|
||||
still_tainted(tainted_string)
|
||||
test(tainted_string) # still tainted
|
||||
|
||||
def test_const_sanitizer2():
|
||||
tainted_string = TAINTED
|
||||
tainted_string = TAINTED_STRING
|
||||
if tainted_string == "OK" or tainted_string == "ALSO_OK":
|
||||
not_tainted(tainted_string)
|
||||
test(tainted_string) # not tainted
|
||||
else:
|
||||
still_tainted(tainted_string)
|
||||
test(tainted_string) # still tainted
|
||||
|
||||
def test_str2():
|
||||
tainted_string = TAINTED
|
||||
tainted_string = TAINTED_STRING
|
||||
a = str(tainted_string)
|
||||
b = bytes(tainted_string) # This is an error in Python 3
|
||||
c = bytes(tainted_string, encoding="utf8") # This is an error in Python 2
|
||||
test(a, b, c)
|
||||
|
||||
def cross_over(func, taint):
|
||||
return func(taint)
|
||||
@@ -44,13 +47,23 @@ def cross_over(func, taint):
|
||||
def test_exc_info():
|
||||
info = TAINTED_EXCEPTION_INFO
|
||||
res = cross_over(exc_info_call, info)
|
||||
test(res)
|
||||
|
||||
def exc_info_call(arg):
|
||||
return arg
|
||||
|
||||
def test_untrusted():
|
||||
ext = TAINTED_EXTERNAL_STRING
|
||||
ext = TAINTED_STRING
|
||||
res = cross_over(untrusted_call, ext)
|
||||
test(res)
|
||||
|
||||
def exc_untrusted_call(arg):
|
||||
return arg
|
||||
|
||||
from six.moves.urllib.parse import urlsplit, urlparse
|
||||
|
||||
def test_urlsplit_urlparse():
|
||||
tainted_string = TAINTED_STRING
|
||||
urlsplit_res = urlsplit(tainted_string)
|
||||
urlparse_res = urlparse(tainted_string)
|
||||
test(urlsplit_res, urlparse_res)
|
||||
|
||||
@@ -1,35 +1,8 @@
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:22:23:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:25:23:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:10:23:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:14:23:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:18:23:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:22:27:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:25:27:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:10:27:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:14:27:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:18:27:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:22:31:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:25:31:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:10:31:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:14:31:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:18:31:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:10:48:10 | a | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:13:48:13 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:16:48:16 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:19:48:19 | d | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:22:48:22 | e | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:25:48:25 | f | |
|
||||
| Taint [externally controlled string] | test.py:6 | test.py:6:9:6:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:10:8:10 | a | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:13:8:13 | b | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:16:8:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:12 | test.py:12:9:12:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:10:14:10 | a | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:13:14:13 | b | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:16:14:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:11:19:11 | l | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:14:19:14 | l | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:17:19:17 | l | |
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
| test.py:8 | unpacking | a | externally controlled string |
|
||||
| test.py:8 | unpacking | b | externally controlled string |
|
||||
| test.py:8 | unpacking | c | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | a | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | b | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | c | externally controlled string |
|
||||
| test.py:23 | nested | a1 | externally controlled string |
|
||||
| test.py:23 | nested | a2 | externally controlled string |
|
||||
| test.py:23 | nested | a3 | externally controlled string |
|
||||
| test.py:23 | nested | b | [externally controlled string] |
|
||||
| test.py:23 | nested | c | [externally controlled string] |
|
||||
| test.py:27 | nested | a1 | externally controlled string |
|
||||
| test.py:27 | nested | a2 | externally controlled string |
|
||||
| test.py:27 | nested | a3 | externally controlled string |
|
||||
| test.py:27 | nested | b | [externally controlled string] |
|
||||
| test.py:27 | nested | c | [externally controlled string] |
|
||||
| test.py:31 | nested | a1 | externally controlled string |
|
||||
| test.py:31 | nested | a2 | externally controlled string |
|
||||
| test.py:31 | nested | a3 | externally controlled string |
|
||||
| test.py:31 | nested | b | [externally controlled string] |
|
||||
| test.py:31 | nested | c | [externally controlled string] |
|
||||
| test.py:8 | unpacking | a | NO TAINT |
|
||||
| test.py:8 | unpacking | b | NO TAINT |
|
||||
| test.py:8 | unpacking | c | NO TAINT |
|
||||
| test.py:14 | unpacking_to_list | a | NO TAINT |
|
||||
| test.py:14 | unpacking_to_list | b | NO TAINT |
|
||||
| test.py:14 | unpacking_to_list | c | NO TAINT |
|
||||
| test.py:23 | nested | a1 | NO TAINT |
|
||||
| test.py:23 | nested | a2 | NO TAINT |
|
||||
| test.py:23 | nested | a3 | NO TAINT |
|
||||
| test.py:23 | nested | b | NO TAINT |
|
||||
| test.py:23 | nested | c | NO TAINT |
|
||||
| test.py:27 | nested | a1 | NO TAINT |
|
||||
| test.py:27 | nested | a2 | NO TAINT |
|
||||
| test.py:27 | nested | a3 | NO TAINT |
|
||||
| test.py:27 | nested | b | NO TAINT |
|
||||
| test.py:27 | nested | c | NO TAINT |
|
||||
| test.py:31 | nested | a1 | NO TAINT |
|
||||
| test.py:31 | nested | a2 | NO TAINT |
|
||||
| test.py:31 | nested | a3 | NO TAINT |
|
||||
| test.py:31 | nested | b | NO TAINT |
|
||||
| test.py:31 | nested | c | NO TAINT |
|
||||
| test.py:38 | unpack_from_set | a | NO TAINT |
|
||||
| test.py:38 | unpack_from_set | b | NO TAINT |
|
||||
| test.py:38 | unpack_from_set | c | NO TAINT |
|
||||
| test.py:48 | contrived_1 | a | externally controlled string |
|
||||
| test.py:48 | contrived_1 | b | externally controlled string |
|
||||
| test.py:48 | contrived_1 | c | externally controlled string |
|
||||
| test.py:48 | contrived_1 | d | externally controlled string |
|
||||
| test.py:48 | contrived_1 | e | externally controlled string |
|
||||
| test.py:48 | contrived_1 | f | externally controlled string |
|
||||
| test.py:48 | contrived_1 | a | NO TAINT |
|
||||
| test.py:48 | contrived_1 | b | NO TAINT |
|
||||
| test.py:48 | contrived_1 | c | NO TAINT |
|
||||
| test.py:48 | contrived_1 | d | NO TAINT |
|
||||
| test.py:48 | contrived_1 | e | NO TAINT |
|
||||
| test.py:48 | contrived_1 | f | NO TAINT |
|
||||
| test.py:56 | contrived_2 | a | NO TAINT |
|
||||
| test.py:56 | contrived_2 | b | NO TAINT |
|
||||
| test.py:56 | contrived_2 | c | NO TAINT |
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=3 -p ../../../query-tests/Security/lib/
|
||||
optimize: true
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=3 -p ../../../query-tests/Security/lib/
|
||||
optimize: true
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:3:1:3:27 | ControlFlowNode for Attribute() | test.py:3:14:3:26 | ControlFlowNode for Str | GET |
|
||||
| test.py:4:1:4:28 | ControlFlowNode for Attribute() | test.py:4:15:4:27 | ControlFlowNode for Str | POST |
|
||||
@@ -0,0 +1,11 @@
|
||||
import python
|
||||
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.ClientHttpRequest
|
||||
|
||||
from Client::HttpRequest req, string method
|
||||
where
|
||||
if exists(req.getMethodUpper())
|
||||
then method = req.getMethodUpper()
|
||||
else method = "<NO METHOD>"
|
||||
select req, req.getAUrlPart(), method
|
||||
1
python/ql/test/library-tests/web/client/requests/options
Normal file
1
python/ql/test/library-tests/web/client/requests/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: -p ../../../../query-tests/Security/lib/ --max-import-depth=1
|
||||
4
python/ql/test/library-tests/web/client/requests/test.py
Normal file
4
python/ql/test/library-tests/web/client/requests/test.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import requests
|
||||
|
||||
requests.get('example.com')
|
||||
requests.post('example.com')
|
||||
@@ -0,0 +1,10 @@
|
||||
| test.py:6:5:6:32 | ControlFlowNode for Attribute() | test.py:5:27:5:39 | ControlFlowNode for Str | GET |
|
||||
| test.py:6:5:6:32 | ControlFlowNode for Attribute() | test.py:6:25:6:31 | ControlFlowNode for Str | GET |
|
||||
| test.py:15:5:15:33 | ControlFlowNode for Attribute() | test.py:10:28:10:40 | ControlFlowNode for Str | POST |
|
||||
| test.py:15:5:15:33 | ControlFlowNode for Attribute() | test.py:15:26:15:32 | ControlFlowNode for Str | POST |
|
||||
| test.py:20:5:20:33 | ControlFlowNode for Attribute() | test.py:19:27:19:39 | ControlFlowNode for Str | <NO METHOD> |
|
||||
| test.py:20:5:20:33 | ControlFlowNode for Attribute() | test.py:20:26:20:32 | ControlFlowNode for Str | <NO METHOD> |
|
||||
| test.py:30:5:30:32 | ControlFlowNode for Attribute() | test.py:28:27:28:30 | ControlFlowNode for fake | GET |
|
||||
| test.py:30:5:30:32 | ControlFlowNode for Attribute() | test.py:30:25:30:31 | ControlFlowNode for Str | GET |
|
||||
| test.py:37:5:37:29 | ControlFlowNode for req_meth() | test.py:35:27:35:39 | ControlFlowNode for Str | HEAD |
|
||||
| test.py:37:5:37:29 | ControlFlowNode for req_meth() | test.py:37:22:37:28 | ControlFlowNode for Str | HEAD |
|
||||
@@ -0,0 +1,11 @@
|
||||
import python
|
||||
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.ClientHttpRequest
|
||||
|
||||
from Client::HttpRequest req, string method
|
||||
where
|
||||
if exists(req.getMethodUpper())
|
||||
then method = req.getMethodUpper()
|
||||
else method = "<NO METHOD>"
|
||||
select req, req.getAUrlPart(), method
|
||||
2
python/ql/test/library-tests/web/client/six/options
Normal file
2
python/ql/test/library-tests/web/client/six/options
Normal file
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: --max-import-depth=2
|
||||
optimize: true
|
||||
37
python/ql/test/library-tests/web/client/six/test.py
Normal file
37
python/ql/test/library-tests/web/client/six/test.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from six.moves.http_client import HTTPConnection, HTTPSConnection
|
||||
|
||||
|
||||
def basic():
|
||||
conn = HTTPConnection('example.com')
|
||||
conn.request('GET', '/path')
|
||||
|
||||
|
||||
def indirect_caller():
|
||||
conn = HTTPSConnection('example.com')
|
||||
indirect_callee(conn)
|
||||
|
||||
|
||||
def indirect_callee(conn):
|
||||
conn.request('POST', '/path')
|
||||
|
||||
|
||||
def method_not_known(method):
|
||||
conn = HTTPConnection('example.com')
|
||||
conn.request(method, '/path')
|
||||
|
||||
|
||||
def sneaky_setting_host():
|
||||
# We don't handle that the host is overwritten directly.
|
||||
# A contrived example; you're not supposed to do this, but you certainly can.
|
||||
fake = 'fakehost.com'
|
||||
real = 'realhost.com'
|
||||
conn = HTTPConnection(fake)
|
||||
conn.host = real
|
||||
conn.request('GET', '/path')
|
||||
|
||||
|
||||
def tricky_not_attribute_node():
|
||||
# A contrived example; you're not supposed to do this, but you certainly can.
|
||||
conn = HTTPConnection('example.com')
|
||||
req_meth = conn.request
|
||||
req_meth('HEAD', '/path')
|
||||
@@ -0,0 +1,10 @@
|
||||
| test.py:13:5:13:32 | ControlFlowNode for Attribute() | test.py:12:27:12:39 | ControlFlowNode for Str | GET |
|
||||
| test.py:13:5:13:32 | ControlFlowNode for Attribute() | test.py:13:25:13:31 | ControlFlowNode for Str | GET |
|
||||
| test.py:22:5:22:33 | ControlFlowNode for Attribute() | test.py:17:28:17:40 | ControlFlowNode for Str | POST |
|
||||
| test.py:22:5:22:33 | ControlFlowNode for Attribute() | test.py:22:26:22:32 | ControlFlowNode for Str | POST |
|
||||
| test.py:27:5:27:33 | ControlFlowNode for Attribute() | test.py:26:27:26:39 | ControlFlowNode for Str | <NO METHOD> |
|
||||
| test.py:27:5:27:33 | ControlFlowNode for Attribute() | test.py:27:26:27:32 | ControlFlowNode for Str | <NO METHOD> |
|
||||
| test.py:37:5:37:32 | ControlFlowNode for Attribute() | test.py:35:27:35:30 | ControlFlowNode for fake | GET |
|
||||
| test.py:37:5:37:32 | ControlFlowNode for Attribute() | test.py:37:25:37:31 | ControlFlowNode for Str | GET |
|
||||
| test.py:44:5:44:29 | ControlFlowNode for req_meth() | test.py:42:27:42:39 | ControlFlowNode for Str | HEAD |
|
||||
| test.py:44:5:44:29 | ControlFlowNode for req_meth() | test.py:44:22:44:28 | ControlFlowNode for Str | HEAD |
|
||||
@@ -0,0 +1,11 @@
|
||||
import python
|
||||
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.ClientHttpRequest
|
||||
|
||||
from Client::HttpRequest req, string method
|
||||
where
|
||||
if exists(req.getMethodUpper())
|
||||
then method = req.getMethodUpper()
|
||||
else method = "<NO METHOD>"
|
||||
select req, req.getAUrlPart(), method
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user