Merge branch 'master' into python-modernise-statements

This commit is contained in:
Rasmus Wriedt Larsen
2020-03-10 14:53:44 +01:00
575 changed files with 18497 additions and 6785 deletions

View File

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

View File

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

View File

@@ -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())
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 + "."

View File

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

View File

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

View File

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

View File

@@ -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.",

View File

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

View File

@@ -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'."

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
This directory contains [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.

View File

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

View File

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

View File

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

View File

@@ -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"))
}
}

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
import semmle.python.web.client.StdLib
import semmle.python.web.client.Requests

View File

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

View 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() }
}

View 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))
)
}
}

View File

@@ -1,2 +1 @@
semmle-extractor-options: --lang=2 --max-import-depth=4
optimize: true

View File

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

View File

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

View File

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

View 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])

View File

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

View File

@@ -1,2 +1 @@
semmle-extractor-options: --lang=3 -p ../../lib/ --max-import-depth=3
optimize: true

View File

@@ -1,2 +1 @@
semmle-extractor-options: --lang=3 --max-import-depth=4
optimize: true

View File

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

View 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])

View File

@@ -0,0 +1 @@
Expressions/UseofApply.ql

View File

@@ -0,0 +1,7 @@
123456789
223456789
323456789
423456789
5234567ø9
623456789
723456789

View File

@@ -0,0 +1 @@
Imports/EncodingError.ql

View File

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

View File

@@ -0,0 +1 @@
Imports/EncodingError.ql

View File

@@ -0,0 +1 @@
| nonsense.py:1:2:1:2 | Syntax Error | Syntax Error (in Python 3.5). |

View File

@@ -0,0 +1 @@
Imports/SyntaxError.ql

View File

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

View File

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

View File

@@ -0,0 +1 @@
print "Hello World"

View File

@@ -0,0 +1 @@
This directory contains tests for [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.

View File

@@ -1,2 +1 @@
semmle-extractor-options: --max-import-depth=3
optimize: true

View File

@@ -1,2 +1 @@
semmle-extractor-options: --max-import-depth=4
optimize: true

View File

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

View File

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

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1 --lang=3

View File

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

View File

@@ -0,0 +1 @@
| test.py:5:7:5:13 | ControlFlowNode for PATTERN | <MISSING pointsTo()> |

View File

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

View File

@@ -0,0 +1,5 @@
import re
PATTERN = re.compile("a|b")
check(PATTERN)

View File

@@ -1 +0,0 @@
optimize: true

View File

@@ -1,2 +1 @@
semmle-extractor-options: -R .
optimize: true

View File

@@ -1,2 +1 @@
semmle-extractor-options: --max-import-depth=3
optimize: true

View File

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

View File

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

View File

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

View File

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

View 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()
}
}

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

@@ -1,2 +1 @@
semmle-extractor-options: --max-import-depth=3 -p ../../../query-tests/Security/lib/
optimize: true

View File

@@ -1,2 +1 @@
semmle-extractor-options: --max-import-depth=3 -p ../../../query-tests/Security/lib/
optimize: true

View File

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

View File

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

View File

@@ -0,0 +1 @@
semmle-extractor-options: -p ../../../../query-tests/Security/lib/ --max-import-depth=1

View File

@@ -0,0 +1,4 @@
import requests
requests.get('example.com')
requests.post('example.com')

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
semmle-extractor-options: --max-import-depth=2
optimize: true

View 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')

View File

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

View File

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