mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'master' into testmerge
This commit is contained in:
@@ -19,7 +19,7 @@ predicate doesnt_reraise(ExceptStmt ex) {
|
||||
}
|
||||
|
||||
predicate catches_base_exception(ExceptStmt ex) {
|
||||
ex.getType().refersTo(theBaseExceptionType())
|
||||
ex.getType().pointsTo(ClassValue::baseException())
|
||||
or
|
||||
not exists(ex.getType())
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ predicate no_comment(ExceptStmt ex) {
|
||||
}
|
||||
|
||||
predicate non_local_control_flow(ExceptStmt ex) {
|
||||
ex.getType().refersTo(theStopIterationType())
|
||||
ex.getType().pointsTo(ClassValue::stopIteration())
|
||||
}
|
||||
|
||||
predicate try_has_normal_exit(Try try) {
|
||||
@@ -64,32 +64,29 @@ predicate subscript(Stmt s) {
|
||||
s.(Delete).getATarget() instanceof Subscript
|
||||
}
|
||||
|
||||
predicate encode_decode(Expr ex, ClassObject type) {
|
||||
predicate encode_decode(Call ex, ClassValue type) {
|
||||
exists(string name |
|
||||
ex.(Call).getFunc().(Attribute).getName() = name |
|
||||
name = "encode" and type = Object::builtin("UnicodeEncodeError")
|
||||
ex.getFunc().(Attribute).getName() = name |
|
||||
name = "encode" and type = ClassValue::unicodeEncodeError()
|
||||
or
|
||||
name = "decode" and type = Object::builtin("UnicodeDecodeError")
|
||||
name = "decode" and type = ClassValue::unicodeDecodeError()
|
||||
)
|
||||
}
|
||||
|
||||
predicate small_handler(ExceptStmt ex, Stmt s, ClassObject type) {
|
||||
predicate small_handler(ExceptStmt ex, Stmt s, ClassValue type) {
|
||||
not exists(ex.getTry().getStmt(1)) and
|
||||
s = ex.getTry().getStmt(0) and
|
||||
ex.getType().refersTo(type)
|
||||
ex.getType().pointsTo(type)
|
||||
}
|
||||
|
||||
/** Holds if this exception handler is sufficiently small in scope to not need a comment
|
||||
* as to what it is doing.
|
||||
*/
|
||||
predicate focussed_handler(ExceptStmt ex) {
|
||||
exists(Stmt s, ClassObject type |
|
||||
exists(Stmt s, ClassValue type |
|
||||
small_handler(ex, s, type) |
|
||||
subscript(s) and type.getAnImproperSuperType() = theLookupErrorType()
|
||||
subscript(s) and type.getASuperType() = ClassValue::lookupError()
|
||||
or
|
||||
attribute_access(s) and type = theAttributeErrorType()
|
||||
attribute_access(s) and type = ClassValue::attributeError()
|
||||
or
|
||||
s.(ExprStmt).getValue() instanceof Name and type = theNameErrorType()
|
||||
s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError()
|
||||
or
|
||||
encode_decode(s.(ExprStmt).getValue(), type)
|
||||
)
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
import python
|
||||
|
||||
from ExceptFlowNode ex, Value t, ClassValue c, ControlFlowNode origin, string what
|
||||
where ex.handledException(t, c, origin) and
|
||||
(
|
||||
exists(ClassValue x | x = t |
|
||||
not x.isLegalExceptionType() and
|
||||
not x.failedInference(_) and
|
||||
what = "class '" + x.getName() + "'"
|
||||
)
|
||||
or
|
||||
not t instanceof ClassValue and
|
||||
what = "instance of '" + c.getName() + "'"
|
||||
)
|
||||
|
||||
select ex.getNode(), "Non-exception $@ in exception handler which will never match raised exception.", origin, what
|
||||
|
||||
where
|
||||
ex.handledException(t, c, origin) and
|
||||
(
|
||||
exists(ClassValue x | x = t |
|
||||
not x.isLegalExceptionType() and
|
||||
not x.failedInference(_) and
|
||||
what = "class '" + x.getName() + "'"
|
||||
)
|
||||
or
|
||||
not t instanceof ClassValue and
|
||||
what = "instance of '" + c.getName() + "'"
|
||||
)
|
||||
select ex.getNode(),
|
||||
"Non-exception $@ in exception handler which will never match raised exception.", origin, what
|
||||
|
||||
@@ -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"
|
||||
@@ -13,6 +13,17 @@ private int varargs_length_objectapi(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_objectapi(Call call, FunctionObject func) {
|
||||
func.getACall().getNode() = call and
|
||||
@@ -20,13 +31,20 @@ private Keyword not_keyword_only_arg_objectapi(Call call, FunctionObject func) {
|
||||
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_objectapi_for_call_objectapi(Call call, Object callable) {
|
||||
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_objectapi(callable) |
|
||||
@@ -40,10 +58,34 @@ private int positional_arg_count_objectapi_for_call_objectapi(Call call, Object
|
||||
)
|
||||
}
|
||||
|
||||
/** 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_objectapi(Object callable) {
|
||||
result = callable.(ClassObject).getACall()
|
||||
@@ -51,6 +93,13 @@ private ControlFlowNode get_a_call_objectapi(Object callable) {
|
||||
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_objectapi(Object func_or_cls) {
|
||||
result = func_or_cls.(FunctionObject)
|
||||
@@ -58,6 +107,13 @@ FunctionObject get_function_or_initializer_objectapi(Object func_or_cls) {
|
||||
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_objectapi(Call call, Object func, string name) {
|
||||
@@ -67,6 +123,14 @@ predicate illegally_named_parameter_objectapi(Call call, Object func, string nam
|
||||
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_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'
|
||||
@@ -88,6 +152,27 @@ predicate too_few_args_objectapi(Call call, Object callable, int limit) {
|
||||
)
|
||||
}
|
||||
|
||||
/**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_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'
|
||||
@@ -103,7 +188,25 @@ predicate too_many_args_objectapi(Call call, Object callable, int limit) {
|
||||
callable instanceof ClassObject and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_objectapi_for_call_objectapi(call, callable) > limit
|
||||
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` */
|
||||
@@ -113,6 +216,13 @@ predicate wrong_args_objectapi(Call call, FunctionObject func, int limit, string
|
||||
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"
|
||||
}
|
||||
|
||||
/** Holds if `call` has correct number of arguments for `func`.
|
||||
* Implies nothing about whether `call` could call `func`.
|
||||
*/
|
||||
@@ -123,8 +233,25 @@ predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject f
|
||||
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_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
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,14 @@
|
||||
import python
|
||||
import semmle.python.strings
|
||||
|
||||
from Expr e, ClassObject t
|
||||
where exists(BinaryExpr b | b.getOp() instanceof Mod and format_string(b.getLeft()) and e = b.getRight() and
|
||||
mapping_format(b.getLeft()) and e.refersTo(_, t, _) and not t.isMapping())
|
||||
from Expr e, ClassValue t
|
||||
where
|
||||
exists(BinaryExpr b |
|
||||
b.getOp() instanceof Mod and
|
||||
format_string(b.getLeft()) and
|
||||
e = b.getRight() and
|
||||
mapping_format(b.getLeft()) and
|
||||
e.pointsTo().getClass() = t and
|
||||
not t.isMapping()
|
||||
)
|
||||
select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName()
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -15,29 +15,30 @@
|
||||
import python
|
||||
import semmle.python.strings
|
||||
|
||||
predicate string_format(BinaryExpr operation, StrConst str, Object args, AstNode origin) {
|
||||
exists(Object fmt, Context ctx | operation.getOp() instanceof Mod |
|
||||
operation.getLeft().refersTo(ctx, fmt, _, str) and
|
||||
operation.getRight().refersTo(ctx, args, _, origin)
|
||||
predicate string_format(BinaryExpr operation, StrConst str, Value args, AstNode origin) {
|
||||
operation.getOp() instanceof Mod and
|
||||
exists(Value fmt, Context ctx |
|
||||
operation.getLeft().pointsTo(ctx, fmt, str) and
|
||||
operation.getRight().pointsTo(ctx, args, origin)
|
||||
)
|
||||
}
|
||||
|
||||
int sequence_length(Object args) {
|
||||
int sequence_length(Value args) {
|
||||
/* Guess length of sequence */
|
||||
exists(Tuple seq |
|
||||
seq = args.getOrigin() |
|
||||
exists(Tuple seq, AstNode origin |
|
||||
seq.pointsTo(args,origin) |
|
||||
result = strictcount(seq.getAnElt()) and
|
||||
not seq.getAnElt() instanceof Starred
|
||||
)
|
||||
or
|
||||
exists(ImmutableLiteral i |
|
||||
i.getLiteralObject() = args |
|
||||
i.getLiteralValue() = args |
|
||||
result = 1
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
from BinaryExpr operation, StrConst fmt, Object args, int slen, int alen, AstNode origin, string provided
|
||||
from BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, string provided
|
||||
where string_format(operation, fmt, args, origin) and slen = sequence_length(args) and alen = format_items(fmt) and slen != alen and
|
||||
(if slen = 1 then provided = " is provided." else provided = " are provided.")
|
||||
select operation, "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + slen.toString() + provided,
|
||||
|
||||
@@ -15,9 +15,9 @@ predicate slice_method_name(string name) {
|
||||
name = "__getslice__" or name = "__setslice__" or name = "__delslice__"
|
||||
}
|
||||
|
||||
from PyFunctionObject f, string meth
|
||||
from PythonFunctionValue f, string meth
|
||||
|
||||
where f.getFunction().isMethod() and not f.isOverridingMethod() and
|
||||
where f.getScope().isMethod() and not f.isOverridingMethod() and
|
||||
slice_method_name(meth) and f.getName() = meth
|
||||
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ predicate is_quad_op(string name) {
|
||||
name = "__setslice__" or name = "__exit__"
|
||||
}
|
||||
|
||||
int argument_count(PyFunctionObject f, string name, ClassObject cls) {
|
||||
int argument_count(PythonFunctionValue f, string name, ClassValue cls) {
|
||||
cls.declaredAttribute(name) = f and
|
||||
(
|
||||
is_unary_op(name) and result = 1
|
||||
@@ -125,7 +125,7 @@ int argument_count(PyFunctionObject f, string name, ClassObject cls) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate incorrect_special_method_defn(PyFunctionObject func, string message, boolean show_counts, string name, ClassObject owner) {
|
||||
predicate incorrect_special_method_defn(PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner) {
|
||||
exists(int required |
|
||||
required = argument_count(func, name, owner) |
|
||||
/* actual_non_default <= actual */
|
||||
@@ -133,14 +133,14 @@ predicate incorrect_special_method_defn(PyFunctionObject func, string message, b
|
||||
(message = "Too few parameters" and show_counts = true)
|
||||
else if required < func.minParameters() then
|
||||
(message = "Too many parameters" and show_counts = true)
|
||||
else if (func.minParameters() < required and not func.getFunction().hasVarArg()) then
|
||||
else if (func.minParameters() < required and not func.getScope().hasVarArg()) then
|
||||
(message = (required -func.minParameters()) + " default values(s) will never be used" and show_counts = false)
|
||||
else
|
||||
none()
|
||||
)
|
||||
}
|
||||
|
||||
predicate incorrect_pow(FunctionObject func, string message, boolean show_counts, ClassObject owner) {
|
||||
predicate incorrect_pow(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
|
||||
owner.declaredAttribute("__pow__") = func and
|
||||
(
|
||||
func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true
|
||||
@@ -153,19 +153,19 @@ predicate incorrect_pow(FunctionObject func, string message, boolean show_counts
|
||||
)
|
||||
}
|
||||
|
||||
predicate incorrect_get(FunctionObject func, string message, boolean show_counts, ClassObject owner) {
|
||||
predicate incorrect_get(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
|
||||
owner.declaredAttribute("__get__") = func and
|
||||
(
|
||||
func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
|
||||
or
|
||||
func.minParameters() < 2 and not func.getFunction().hasVarArg() and
|
||||
func.minParameters() < 2 and not func.getScope().hasVarArg() and
|
||||
message = (2 - func.minParameters()) + " default value(s) will never be used" and show_counts = false
|
||||
)
|
||||
}
|
||||
|
||||
string should_have_parameters(PyFunctionObject f, string name, ClassObject owner) {
|
||||
string should_have_parameters(PythonFunctionValue f, string name, ClassValue owner) {
|
||||
exists(int i | i = argument_count(f, name, owner) |
|
||||
result = i.toString()
|
||||
)
|
||||
@@ -173,7 +173,7 @@ string should_have_parameters(PyFunctionObject f, string name, ClassObject owner
|
||||
owner.declaredAttribute(name) = f and (name = "__get__" or name = "__pow__") and result = "2 or 3"
|
||||
}
|
||||
|
||||
string has_parameters(PyFunctionObject f) {
|
||||
string has_parameters(PythonFunctionValue f) {
|
||||
exists(int i | i = f.minParameters() |
|
||||
i = 0 and result = "no parameters"
|
||||
or
|
||||
@@ -183,7 +183,7 @@ string has_parameters(PyFunctionObject f) {
|
||||
)
|
||||
}
|
||||
|
||||
from PyFunctionObject f, string message, string sizes, boolean show_counts, string name, ClassObject owner
|
||||
from PythonFunctionValue f, string message, string sizes, boolean show_counts, string name, ClassValue owner
|
||||
where
|
||||
(
|
||||
incorrect_special_method_defn(f, message, show_counts, name, owner)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -14,4 +14,4 @@ import python
|
||||
|
||||
from SyntaxError error
|
||||
where not error instanceof EncodingError
|
||||
select error, error.getMessage() + " (in Python " + major_version() + "." + minor_version() + ")."
|
||||
select error, error.getMessage() + " (in Python " + major_version() + ")."
|
||||
@@ -26,7 +26,5 @@ where
|
||||
value = test.(NameConstant).toString()
|
||||
) and
|
||||
/* Exclude asserts appearing at the end of a chain of `elif`s */
|
||||
not exists(If i |
|
||||
i.getElif().getAnOrelse() = a
|
||||
)
|
||||
not exists(If i | i.getElif().getAnOrelse() = a)
|
||||
select a, "Assert of literal constant " + value + "."
|
||||
|
||||
@@ -14,11 +14,14 @@
|
||||
import python
|
||||
|
||||
from Assert a, string b, string non
|
||||
where a.getTest() instanceof Tuple and
|
||||
(if exists(((Tuple)a.getTest()).getAnElt()) then
|
||||
(b = "True" and non = "non-")
|
||||
else
|
||||
(b = "False" and non = "")
|
||||
)
|
||||
where
|
||||
a.getTest() instanceof Tuple and
|
||||
(
|
||||
if exists(a.getTest().(Tuple).getAnElt())
|
||||
then (
|
||||
b = "True" and non = "non-"
|
||||
) else (
|
||||
b = "False" and non = ""
|
||||
)
|
||||
)
|
||||
select a, "Assertion of " + non + "empty tuple is always " + b + "."
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
import python
|
||||
|
||||
from Stmt s, string kind
|
||||
where
|
||||
s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s))
|
||||
or
|
||||
s instanceof Break and kind = "break" and
|
||||
exists(Try t | t.getFinalbody().contains(s) |
|
||||
not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop))
|
||||
and
|
||||
not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop))
|
||||
)
|
||||
where
|
||||
s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s))
|
||||
or
|
||||
s instanceof Break and
|
||||
kind = "break" and
|
||||
exists(Try t | t.getFinalbody().contains(s) |
|
||||
not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and
|
||||
not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop))
|
||||
)
|
||||
select s, "'" + kind + "' in a finally block will swallow any exceptions raised."
|
||||
|
||||
@@ -14,19 +14,22 @@
|
||||
import python
|
||||
|
||||
from Expr e, Location l, string kind, string what
|
||||
where e.isParenthesized() and
|
||||
not e instanceof Tuple and
|
||||
(
|
||||
exists(If i | i.getTest() = e) and kind = "if" and what = "condition"
|
||||
or
|
||||
exists(While w | w.getTest() = e) and kind = "while" and what = "condition"
|
||||
or
|
||||
exists(Return r | r.getValue() = e) and kind = "return" and what = "value"
|
||||
or
|
||||
exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and kind = "assert" and what = "test"
|
||||
)
|
||||
and
|
||||
// These require parentheses
|
||||
(not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and
|
||||
l = e.getLocation() and l.getStartLine() = l.getEndLine()
|
||||
where
|
||||
e.isParenthesized() and
|
||||
not e instanceof Tuple and
|
||||
(
|
||||
exists(If i | i.getTest() = e) and kind = "if" and what = "condition"
|
||||
or
|
||||
exists(While w | w.getTest() = e) and kind = "while" and what = "condition"
|
||||
or
|
||||
exists(Return r | r.getValue() = e) and kind = "return" and what = "value"
|
||||
or
|
||||
exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and
|
||||
kind = "assert" and
|
||||
what = "test"
|
||||
) and
|
||||
// These require parentheses
|
||||
(not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and
|
||||
l = e.getLocation() and
|
||||
l.getStartLine() = l.getEndLine()
|
||||
select e, "Parenthesized " + what + " in '" + kind + "' statement."
|
||||
|
||||
@@ -15,16 +15,15 @@
|
||||
|
||||
import python
|
||||
|
||||
|
||||
predicate is_condition(Expr cond) {
|
||||
exists(If i | i.getTest() = cond) or
|
||||
exists(IfExp ie | ie.getTest() = cond)
|
||||
exists(If i | i.getTest() = cond) or
|
||||
exists(IfExp ie | ie.getTest() = cond)
|
||||
}
|
||||
|
||||
/* Treat certain unmodified builtins as constants as well. */
|
||||
predicate effective_constant(Name cond) {
|
||||
exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) |
|
||||
var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented"
|
||||
var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -34,9 +33,10 @@ predicate test_makes_code_unreachable(Expr cond) {
|
||||
exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable())
|
||||
}
|
||||
|
||||
|
||||
from Expr cond
|
||||
where is_condition(cond) and (cond.isConstant() or effective_constant(cond)) and
|
||||
/* Ignore cases where test makes code unreachable, as that is handled in different query */
|
||||
not test_makes_code_unreachable(cond)
|
||||
where
|
||||
is_condition(cond) and
|
||||
(cond.isConstant() or effective_constant(cond)) and
|
||||
/* Ignore cases where test makes code unreachable, as that is handled in different query */
|
||||
not test_makes_code_unreachable(cond)
|
||||
select cond, "Testing a constant will always give the same result."
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
* @precision medium
|
||||
* @id py/missing-docstring
|
||||
*/
|
||||
/* NOTE: precision of 'medium' reflects the lack of precision in the underlying rule.
|
||||
|
||||
/*
|
||||
* NOTE: precision of 'medium' reflects the lack of precision in the underlying rule.
|
||||
* Do we care whether a function has a docstring? That often depends on the reader of that docstring.
|
||||
*/
|
||||
|
||||
@@ -18,25 +20,26 @@ import python
|
||||
predicate needs_docstring(Scope s) {
|
||||
s.isPublic() and
|
||||
(
|
||||
not s instanceof Function
|
||||
or
|
||||
function_needs_docstring(s)
|
||||
not s instanceof Function
|
||||
or
|
||||
function_needs_docstring(s)
|
||||
)
|
||||
}
|
||||
|
||||
predicate function_needs_docstring(Function f) {
|
||||
not exists(FunctionObject fo, FunctionObject base | fo.overrides(base) and fo.getFunction() = f |
|
||||
not function_needs_docstring(base.getFunction())) and
|
||||
not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f |
|
||||
not function_needs_docstring(base.getScope())
|
||||
) and
|
||||
f.getName() != "lambda" and
|
||||
(f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2
|
||||
and not exists(PythonPropertyObject p |
|
||||
(f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and
|
||||
not exists(PythonPropertyObject p |
|
||||
p.getGetter().getFunction() = f or
|
||||
p.getSetter().getFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
string scope_type(Scope s) {
|
||||
result = "Module" and s instanceof Module and not ((Module)s).isPackage()
|
||||
result = "Module" and s instanceof Module and not s.(Module).isPackage()
|
||||
or
|
||||
result = "Class" and s instanceof Class
|
||||
or
|
||||
@@ -46,5 +49,3 @@ string scope_type(Scope s) {
|
||||
from Scope s
|
||||
where needs_docstring(s) and not exists(s.getDocString())
|
||||
select s, scope_type(s) + " " + s.getName() + " does not have a docstring"
|
||||
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ string message() {
|
||||
}
|
||||
|
||||
predicate exec_function_call(Call c) {
|
||||
exists(GlobalVariable exec | exec = ((Name)c.getFunc()).getVariable() and exec.getId() = "exec")
|
||||
exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec")
|
||||
}
|
||||
|
||||
from AstNode exec
|
||||
where exec_function_call(exec) or exec instanceof Exec
|
||||
select exec, message()
|
||||
select exec, message()
|
||||
|
||||
@@ -13,18 +13,20 @@
|
||||
|
||||
import python
|
||||
|
||||
predicate is_a_string_type(ClassObject seqtype) {
|
||||
seqtype = theBytesType() and major_version() = 2
|
||||
or
|
||||
seqtype = theUnicodeType()
|
||||
predicate has_string_type(Value v) {
|
||||
v.getClass() = ClassValue::str()
|
||||
or
|
||||
v.getClass() = ClassValue::unicode() and major_version() = 2
|
||||
}
|
||||
|
||||
from For loop, ControlFlowNode iter, Object str, Object seq, ControlFlowNode seq_origin, ClassObject strtype, ClassObject seqtype, ControlFlowNode str_origin
|
||||
where loop.getIter().getAFlowNode() = iter and
|
||||
iter.refersTo(str, strtype, str_origin) and
|
||||
iter.refersTo(seq, seqtype, seq_origin) and
|
||||
is_a_string_type(strtype) and
|
||||
seqtype.isIterable() and
|
||||
not is_a_string_type(seqtype)
|
||||
|
||||
select loop, "Iteration over $@, of class " + seqtype.getName() + ", may also iterate over $@.", seq_origin, "sequence", str_origin, "string"
|
||||
from
|
||||
For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, ControlFlowNode str_origin
|
||||
where
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.pointsTo(str, str_origin) and
|
||||
iter.pointsTo(seq, seq_origin) and
|
||||
has_string_type(str) and
|
||||
seq.getClass().isIterable() and
|
||||
not has_string_type(seq)
|
||||
select loop, "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.",
|
||||
seq_origin, "sequence", str_origin, "string"
|
||||
|
||||
@@ -14,18 +14,19 @@
|
||||
|
||||
import python
|
||||
|
||||
private int len(ExprList el) {
|
||||
result = count(el.getAnItem())
|
||||
}
|
||||
private int len(ExprList el) { result = count(el.getAnItem()) }
|
||||
|
||||
predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequenceType) {
|
||||
exists(ExprList l, ExprList r |
|
||||
(a.getATarget().(Tuple).getElts() = l or
|
||||
a.getATarget().(List).getElts() = l)
|
||||
and
|
||||
((a.getValue().(Tuple).getElts() = r and sequenceType = "tuple") or
|
||||
(a.getValue().(List).getElts() = r and sequenceType = "list"))
|
||||
and
|
||||
(
|
||||
a.getATarget().(Tuple).getElts() = l or
|
||||
a.getATarget().(List).getElts() = l
|
||||
) and
|
||||
(
|
||||
a.getValue().(Tuple).getElts() = r and sequenceType = "tuple"
|
||||
or
|
||||
a.getValue().(List).getElts() = r and sequenceType = "list"
|
||||
) and
|
||||
loc = a.getValue().getLocation() and
|
||||
lcount = len(l) and
|
||||
rcount = len(r) and
|
||||
@@ -35,24 +36,26 @@ predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequ
|
||||
}
|
||||
|
||||
predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) {
|
||||
exists(ExprList l, TupleObject r, AstNode origin |
|
||||
(a.getATarget().(Tuple).getElts() = l or
|
||||
a.getATarget().(List).getElts() = l)
|
||||
and
|
||||
a.getValue().refersTo(r, origin) and
|
||||
exists(ExprList l, TupleValue r, AstNode origin |
|
||||
(
|
||||
a.getATarget().(Tuple).getElts() = l or
|
||||
a.getATarget().(List).getElts() = l
|
||||
) and
|
||||
a.getValue().pointsTo(r, origin) and
|
||||
loc = origin.getLocation() and
|
||||
lcount = len(l) and
|
||||
rcount = r.getLength() and
|
||||
rcount = r.length() and
|
||||
lcount != rcount and
|
||||
not exists(Starred s | l.getAnItem() = s)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
from Assign a, int lcount, int rcount, Location loc, string sequenceType
|
||||
where
|
||||
mismatched(a, lcount, rcount, loc, sequenceType)
|
||||
or
|
||||
mismatched_tuple_rhs(a, lcount, rcount, loc) and
|
||||
sequenceType = "tuple"
|
||||
select a, "Left hand side of assignment contains " + lcount + " variables, but right hand side is a $@ of length " + rcount + "." , loc, sequenceType
|
||||
select a,
|
||||
"Left hand side of assignment contains " + lcount +
|
||||
" variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType
|
||||
|
||||
@@ -12,24 +12,18 @@
|
||||
|
||||
import python
|
||||
|
||||
Object aFunctionLocalsObject() {
|
||||
exists(Call c, Name n, GlobalVariable v |
|
||||
c = result.getOrigin() and
|
||||
n = c.getFunc() and
|
||||
n.getVariable() = v and
|
||||
v.getId() = "locals" and
|
||||
c.getScope() instanceof FastLocalsFunction
|
||||
)
|
||||
predicate originIsLocals(ControlFlowNode n) {
|
||||
n.pointsTo(_, _, Value::named("locals").getACall())
|
||||
}
|
||||
|
||||
|
||||
|
||||
predicate modification_of_locals(ControlFlowNode f) {
|
||||
f.(SubscriptNode).getObject().refersTo(aFunctionLocalsObject()) and (f.isStore() or f.isDelete())
|
||||
originIsLocals(f.(SubscriptNode).getObject()) and
|
||||
(f.isStore() or f.isDelete())
|
||||
or
|
||||
exists(string mname, AttrNode attr |
|
||||
attr = f.(CallNode).getFunction() and
|
||||
attr.getObject(mname).refersTo(aFunctionLocalsObject(), _) |
|
||||
originIsLocals(attr.getObject(mname))
|
||||
|
|
||||
mname = "pop" or
|
||||
mname = "popitem" or
|
||||
mname = "update" or
|
||||
@@ -39,5 +33,4 @@ predicate modification_of_locals(ControlFlowNode f) {
|
||||
|
||||
from AstNode a, ControlFlowNode f
|
||||
where modification_of_locals(f) and a = f.getNode()
|
||||
|
||||
select a, "Modification of the locals() dictionary will have no effect on the local variables."
|
||||
|
||||
@@ -10,20 +10,21 @@
|
||||
* @precision very-high
|
||||
* @id py/nested-loops-with-same-variable
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
predicate loop_variable(For f, Variable v) {
|
||||
f.getTarget().defines(v)
|
||||
}
|
||||
predicate loop_variable(For f, Variable v) { f.getTarget().defines(v) }
|
||||
|
||||
predicate variableUsedInNestedLoops(For inner, For outer, Variable v) {
|
||||
/* Only treat loops in body as inner loops. Loops in the else clause are ignored. */
|
||||
outer.getBody().contains(inner) and loop_variable(inner, v) and loop_variable(outer, v)
|
||||
outer.getBody().contains(inner) and
|
||||
loop_variable(inner, v) and
|
||||
loop_variable(outer, v) and
|
||||
/* Ignore cases where there is no use of the variable or the only use is in the inner loop */
|
||||
and exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n))
|
||||
exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n))
|
||||
}
|
||||
|
||||
from For inner, For outer, Variable v
|
||||
where variableUsedInNestedLoops(inner, outer, v)
|
||||
select inner, "Nested for statement uses loop variable '" + v.getId() + "' of enclosing $@.",
|
||||
outer, "for statement"
|
||||
select inner, "Nested for statement uses loop variable '" + v.getId() + "' of enclosing $@.", outer,
|
||||
"for statement"
|
||||
|
||||
@@ -19,18 +19,18 @@ predicate loop_variable_ssa(For f, Variable v, SsaVariable s) {
|
||||
|
||||
predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) {
|
||||
/* Ignore cases where there is no use of the variable or the only use is in the inner loop. */
|
||||
outer.contains(n)
|
||||
and not inner.contains(n)
|
||||
outer.contains(n) and
|
||||
not inner.contains(n) and
|
||||
/* Only treat loops in body as inner loops. Loops in the else clause are ignored. */
|
||||
and outer.getBody().contains(inner)
|
||||
and exists(SsaVariable s |
|
||||
loop_variable_ssa(inner, v, s.getAnUltimateDefinition())
|
||||
and loop_variable_ssa(outer, v, _)
|
||||
and s.getAUse().getNode() = n
|
||||
outer.getBody().contains(inner) and
|
||||
exists(SsaVariable s |
|
||||
loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) and
|
||||
loop_variable_ssa(outer, v, _) and
|
||||
s.getAUse().getNode() = n
|
||||
)
|
||||
}
|
||||
|
||||
from For inner, For outer, Variable v, Name n
|
||||
where variableUsedInNestedLoops(inner, outer, v, n)
|
||||
select inner, "Nested for statement $@ loop variable '" + v.getId() + "' of enclosing $@.", n, "uses",
|
||||
outer, "for statement"
|
||||
select inner, "Nested for statement $@ loop variable '" + v.getId() + "' of enclosing $@.", n,
|
||||
"uses", outer, "for statement"
|
||||
|
||||
@@ -17,7 +17,7 @@ for addressing the defect. </p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
In this example, the loop may attempt to iterate over <code>None</code>, which is not an iterator.
|
||||
In this example, the loop may attempt to iterate over <code>None</code>, which is not an iterable.
|
||||
It is likely that the programmer forgot to test for <code>None</code> before the loop.
|
||||
</p>
|
||||
<sample src="NonIteratorInForLoop.py" />
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
import python
|
||||
|
||||
from For loop, ControlFlowNode iter, Value v, ClassValue t, ControlFlowNode origin
|
||||
where loop.getIter().getAFlowNode() = iter and
|
||||
iter.pointsTo(_, v, origin) and v.getClass() = t and
|
||||
not t.isIterable() and not t.failedInference(_) and
|
||||
not v = Value::named("None") and
|
||||
not t.isDescriptorType()
|
||||
|
||||
select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterator", t, t.getName()
|
||||
where
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.pointsTo(_, v, origin) and
|
||||
v.getClass() = t and
|
||||
not t.isIterable() and
|
||||
not t.failedInference(_) and
|
||||
not v = Value::named("None") and
|
||||
not t.isDescriptorType()
|
||||
select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterable", t, t.getName()
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
predicate assignment(AssignStmt a, Expr left, Expr right)
|
||||
{
|
||||
|
||||
predicate assignment(AssignStmt a, Expr left, Expr right) {
|
||||
a.getATarget() = left and a.getValue() = right
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ predicate corresponding(Expr left, Expr right) {
|
||||
exists(Attribute la, Attribute ra |
|
||||
corresponding(la, ra) and
|
||||
left = la.getObject() and
|
||||
right = ra.getObject())
|
||||
right = ra.getObject()
|
||||
)
|
||||
}
|
||||
|
||||
predicate same_value(Expr left, Expr right) {
|
||||
@@ -33,34 +34,41 @@ predicate same_value(Expr left, Expr right) {
|
||||
}
|
||||
|
||||
predicate maybe_defined_in_outer_scope(Name n) {
|
||||
exists(SsaVariable v | v.getAUse().getNode() = n |
|
||||
v.maybeUndefined()
|
||||
)
|
||||
exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined())
|
||||
}
|
||||
|
||||
Variable relevant_var(Name n) {
|
||||
n.getVariable() = result and
|
||||
(corresponding(n, _) or corresponding(_, n))
|
||||
/* Protection against FPs in projects that offer compatibility between Python 2 and 3,
|
||||
* since many of them make assignments such as
|
||||
*
|
||||
* if PY2:
|
||||
* bytes = str
|
||||
* else:
|
||||
* bytes = bytes
|
||||
*
|
||||
*/
|
||||
predicate isBuiltin(string name) {
|
||||
exists(Value v | v = Value::named(name) and v.isBuiltin())
|
||||
}
|
||||
|
||||
predicate same_name(Name n1, Name n2) {
|
||||
corresponding(n1, n2) and
|
||||
relevant_var(n1) = relevant_var(n2) and
|
||||
not exists(Object::builtin(n1.getId())) and
|
||||
n1.getVariable() = n2.getVariable() and
|
||||
not isBuiltin(n1.getId()) and
|
||||
not maybe_defined_in_outer_scope(n2)
|
||||
}
|
||||
|
||||
ClassObject value_type(Attribute a) {
|
||||
a.getObject().refersTo(_, result, _)
|
||||
}
|
||||
ClassValue value_type(Attribute a) { a.getObject().pointsTo().getClass() = result }
|
||||
|
||||
predicate is_property_access(Attribute a) {
|
||||
value_type(a).lookupAttribute(a.getName()) instanceof PropertyObject
|
||||
value_type(a).lookup(a.getName()) instanceof PropertyValue
|
||||
}
|
||||
|
||||
predicate same_attribute(Attribute a1, Attribute a2) {
|
||||
corresponding(a1, a2) and a1.getName() = a2.getName() and same_value(a1.getObject(), a2.getObject()) and
|
||||
exists(value_type(a1)) and not is_property_access(a1)
|
||||
corresponding(a1, a2) and
|
||||
a1.getName() = a2.getName() and
|
||||
same_value(a1.getObject(), a2.getObject()) and
|
||||
exists(value_type(a1)) and
|
||||
not is_property_access(a1)
|
||||
}
|
||||
|
||||
int pyflakes_commented_line(File file) {
|
||||
@@ -72,21 +80,25 @@ int pyflakes_commented_line(File file) {
|
||||
predicate pyflakes_commented(AssignStmt assignment) {
|
||||
exists(Location loc |
|
||||
assignment.getLocation() = loc and
|
||||
loc.getStartLine() = pyflakes_commented_line(loc.getFile()))
|
||||
loc.getStartLine() = pyflakes_commented_line(loc.getFile())
|
||||
)
|
||||
}
|
||||
|
||||
predicate side_effecting_lhs(Attribute lhs) {
|
||||
exists(ClassObject cls, ClassObject decl |
|
||||
lhs.getObject().refersTo(_, cls, _) and
|
||||
decl = cls.getAnImproperSuperType() and
|
||||
not decl.isBuiltin() |
|
||||
exists(ClassValue cls, ClassValue decl |
|
||||
lhs.getObject().pointsTo().getClass() = cls and
|
||||
decl = cls.getASuperType() and
|
||||
not decl.isBuiltin()
|
||||
|
|
||||
decl.declaresAttribute("__setattr__")
|
||||
)
|
||||
}
|
||||
|
||||
from AssignStmt a, Expr left, Expr right
|
||||
where assignment(a, left, right)
|
||||
and same_value(left, right)
|
||||
and not pyflakes_commented(a) and
|
||||
not side_effecting_lhs(left)
|
||||
where
|
||||
assignment(a, left, right) and
|
||||
same_value(left, right) and
|
||||
// some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes`
|
||||
not pyflakes_commented(a) and
|
||||
not side_effecting_lhs(left)
|
||||
select a, "This assignment assigns a variable to itself."
|
||||
|
||||
@@ -14,12 +14,12 @@ import python
|
||||
|
||||
from AstNode node, string kind
|
||||
where
|
||||
not node.getScope() instanceof Function and
|
||||
(
|
||||
node instanceof Return and kind = "return"
|
||||
or
|
||||
node instanceof Yield and kind = "yield"
|
||||
or
|
||||
node instanceof YieldFrom and kind = "yield from"
|
||||
)
|
||||
not node.getScope() instanceof Function and
|
||||
(
|
||||
node instanceof Return and kind = "return"
|
||||
or
|
||||
node instanceof Yield and kind = "yield"
|
||||
or
|
||||
node instanceof YieldFrom and kind = "yield from"
|
||||
)
|
||||
select node, "'" + kind + "' is used outside a function."
|
||||
|
||||
@@ -14,24 +14,26 @@
|
||||
|
||||
import python
|
||||
|
||||
predicate calls_close(Call c) { exists(Attribute a | c.getFunc() = a and a.getName() = "close") }
|
||||
|
||||
predicate calls_close(Call c) {
|
||||
exists (Attribute a | c.getFunc() = a and a.getName() = "close")
|
||||
predicate only_stmt_in_finally(Try t, Call c) {
|
||||
exists(ExprStmt s |
|
||||
t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1
|
||||
)
|
||||
}
|
||||
|
||||
predicate
|
||||
only_stmt_in_finally(Try t, Call c) {
|
||||
exists(ExprStmt s | t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1)
|
||||
predicate points_to_context_manager(ControlFlowNode f, ClassValue cls) {
|
||||
forex(Value v | f.pointsTo(v) | v.getClass() = cls) and
|
||||
cls.isContextManager()
|
||||
}
|
||||
|
||||
predicate points_to_context_manager(ControlFlowNode f, ClassObject cls) {
|
||||
cls.isContextManager() and
|
||||
forex(Object obj | f.refersTo(obj) | f.refersTo(obj, cls, _))
|
||||
}
|
||||
|
||||
from Call close, Try t, ClassObject cls
|
||||
where only_stmt_in_finally(t, close) and calls_close(close) and
|
||||
exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() |
|
||||
points_to_context_manager(f, cls))
|
||||
select close, "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", cls, cls.getName()
|
||||
|
||||
from Call close, Try t, ClassValue cls
|
||||
where
|
||||
only_stmt_in_finally(t, close) and
|
||||
calls_close(close) and
|
||||
exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() |
|
||||
points_to_context_manager(f, cls)
|
||||
)
|
||||
select close,
|
||||
"Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.",
|
||||
cls, cls.getName()
|
||||
|
||||
@@ -1 +1 @@
|
||||
assert(subprocess.call(['run-backup']) == 0)
|
||||
assert subprocess.call(['run-backup']) == 0
|
||||
|
||||
@@ -14,22 +14,37 @@
|
||||
import python
|
||||
|
||||
predicate func_with_side_effects(Expr e) {
|
||||
exists(string name |
|
||||
name = ((Attribute)e).getName() or name = ((Name)e).getId() |
|
||||
name = "print" or name = "write" or name = "append" or
|
||||
name = "pop" or name = "remove" or name = "discard" or
|
||||
name = "delete" or name = "close" or name = "open" or
|
||||
exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() |
|
||||
name = "print" or
|
||||
name = "write" or
|
||||
name = "append" or
|
||||
name = "pop" or
|
||||
name = "remove" or
|
||||
name = "discard" or
|
||||
name = "delete" or
|
||||
name = "close" or
|
||||
name = "open" or
|
||||
name = "exit"
|
||||
)
|
||||
}
|
||||
|
||||
predicate call_with_side_effect(Call e) {
|
||||
e.getAFlowNode() = Value::named("subprocess.call").getACall()
|
||||
or
|
||||
e.getAFlowNode() = Value::named("subprocess.check_call").getACall()
|
||||
or
|
||||
e.getAFlowNode() = Value::named("subprocess.check_output").getACall()
|
||||
}
|
||||
|
||||
predicate probable_side_effect(Expr e) {
|
||||
// Only consider explicit yields, not artificial ones in comprehensions
|
||||
e instanceof Yield and not exists(Comp c | c.contains(e))
|
||||
or
|
||||
e instanceof YieldFrom
|
||||
or
|
||||
e instanceof Call and func_with_side_effects(((Call)e).getFunc())
|
||||
e instanceof Call and func_with_side_effects(e.(Call).getFunc())
|
||||
or
|
||||
e instanceof Call and call_with_side_effect(e)
|
||||
}
|
||||
|
||||
from Assert a, Expr e
|
||||
|
||||
@@ -13,73 +13,75 @@
|
||||
|
||||
import python
|
||||
|
||||
predicate understood_attribute(Attribute attr, ClassObject cls, ClassObject attr_cls) {
|
||||
exists(string name |
|
||||
attr.getName() = name |
|
||||
attr.getObject().refersTo(_, cls, _) and
|
||||
cls.attributeRefersTo(name, _, attr_cls, _)
|
||||
predicate understood_attribute(Attribute attr, ClassValue cls, ClassValue attr_cls) {
|
||||
exists(string name | attr.getName() = name |
|
||||
attr.getObject().pointsTo().getClass() = cls and
|
||||
cls.attr(name).getClass() = attr_cls
|
||||
)
|
||||
}
|
||||
|
||||
/* Conservative estimate of whether attribute lookup has a side effect */
|
||||
predicate side_effecting_attribute(Attribute attr) {
|
||||
exists(ClassObject cls, ClassObject attr_cls |
|
||||
exists(ClassValue cls, ClassValue attr_cls |
|
||||
understood_attribute(attr, cls, attr_cls) and
|
||||
side_effecting_descriptor_type(attr_cls)
|
||||
)
|
||||
}
|
||||
|
||||
predicate maybe_side_effecting_attribute(Attribute attr) {
|
||||
not understood_attribute(attr, _, _) and not attr.refersTo(_)
|
||||
not understood_attribute(attr, _, _) and not attr.pointsTo(_)
|
||||
or
|
||||
side_effecting_attribute(attr)
|
||||
}
|
||||
|
||||
predicate side_effecting_descriptor_type(ClassObject descriptor) {
|
||||
predicate side_effecting_descriptor_type(ClassValue descriptor) {
|
||||
descriptor.isDescriptorType() and
|
||||
/* Technically all descriptor gets have side effects,
|
||||
* but some are indicative of a missing call and
|
||||
* we want to treat them as having no effect. */
|
||||
not descriptor = thePyFunctionType() and
|
||||
not descriptor = theStaticMethodType() and
|
||||
not descriptor = theClassMethodType()
|
||||
// Technically all descriptor gets have side effects,
|
||||
// but some are indicative of a missing call and
|
||||
// we want to treat them as having no effect.
|
||||
not descriptor = ClassValue::functionType() and
|
||||
not descriptor = ClassValue::staticmethod() and
|
||||
not descriptor = ClassValue::classmethod()
|
||||
}
|
||||
|
||||
/** Side effecting binary operators are rare, so we assume they are not
|
||||
/**
|
||||
* Side effecting binary operators are rare, so we assume they are not
|
||||
* side-effecting unless we know otherwise.
|
||||
*/
|
||||
predicate side_effecting_binary(Expr b) {
|
||||
exists(Expr sub, ClassObject cls, string method_name |
|
||||
exists(Expr sub, ClassValue cls, string method_name |
|
||||
binary_operator_special_method(b, sub, cls, method_name)
|
||||
or
|
||||
comparison_special_method(b, sub, cls, method_name)
|
||||
|
|
||||
|
|
||||
method_name = special_method() and
|
||||
cls.hasAttribute(method_name)
|
||||
and
|
||||
not exists(ClassObject declaring |
|
||||
declaring.declaresAttribute(method_name)
|
||||
and declaring = cls.getAnImproperSuperType() and
|
||||
declaring.isBuiltin() and not declaring = theObjectType()
|
||||
cls.hasAttribute(method_name) and
|
||||
not exists(ClassValue declaring |
|
||||
declaring.declaresAttribute(method_name) and
|
||||
declaring = cls.getASuperType() and
|
||||
declaring.isBuiltin() and
|
||||
not declaring = ClassValue::object()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binary_operator_special_method(BinaryExpr b, Expr sub, ClassObject cls, string method_name) {
|
||||
private predicate binary_operator_special_method(
|
||||
BinaryExpr b, Expr sub, ClassValue cls, string method_name
|
||||
) {
|
||||
method_name = special_method() and
|
||||
sub = b.getLeft() and
|
||||
method_name = b.getOp().getSpecialMethodName() and
|
||||
sub.refersTo(_, cls, _)
|
||||
sub.pointsTo().getClass() = cls
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate comparison_special_method(Compare b, Expr sub, ClassObject cls, string method_name) {
|
||||
private predicate comparison_special_method(Compare b, Expr sub, ClassValue cls, string method_name) {
|
||||
exists(Cmpop op |
|
||||
b.compares(sub, op, _) and
|
||||
method_name = op.getSpecialMethodName()
|
||||
) and
|
||||
sub.refersTo(_, cls, _)
|
||||
sub.pointsTo().getClass() = cls
|
||||
}
|
||||
|
||||
private string special_method() {
|
||||
@@ -89,19 +91,16 @@ private string special_method() {
|
||||
}
|
||||
|
||||
predicate is_notebook(File f) {
|
||||
exists(Comment c |
|
||||
c.getLocation().getFile() = f |
|
||||
exists(Comment c | c.getLocation().getFile() = f |
|
||||
c.getText().regexpMatch("#\\s*<nbformat>.+</nbformat>\\s*")
|
||||
)
|
||||
}
|
||||
|
||||
/** Expression (statement) in a jupyter/ipython notebook */
|
||||
predicate in_notebook(Expr e) {
|
||||
is_notebook(e.getScope().(Module).getFile())
|
||||
}
|
||||
predicate in_notebook(Expr e) { is_notebook(e.getScope().(Module).getFile()) }
|
||||
|
||||
FunctionObject assertRaises() {
|
||||
result = ModuleObject::named("unittest").attr("TestCase").(ClassObject).lookupAttribute("assertRaises")
|
||||
FunctionValue assertRaises() {
|
||||
result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises")
|
||||
}
|
||||
|
||||
/** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */
|
||||
@@ -121,14 +120,11 @@ predicate python2_print(Expr e) {
|
||||
}
|
||||
|
||||
predicate no_effect(Expr e) {
|
||||
// strings can be used as comments
|
||||
not e instanceof StrConst and
|
||||
not ((StrConst)e).isDocString() and
|
||||
not e.hasSideEffects() and
|
||||
forall(Expr sub |
|
||||
sub = e.getASubExpression*()
|
||||
|
|
||||
not side_effecting_binary(sub)
|
||||
and
|
||||
forall(Expr sub | sub = e.getASubExpression*() |
|
||||
not side_effecting_binary(sub) and
|
||||
not maybe_side_effecting_attribute(sub)
|
||||
) and
|
||||
not in_notebook(e) and
|
||||
@@ -139,4 +135,3 @@ predicate no_effect(Expr e) {
|
||||
from ExprStmt stmt
|
||||
where no_effect(stmt.getValue())
|
||||
select stmt, "This statement has no effect."
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>If you concatenate strings in a loop then the time taken by the loop is quadratic in the number
|
||||
<p>If you concatenate strings in a loop then the time taken by the loop is quadratic in the number
|
||||
of iterations.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Initialize an empty list before the start of the list.
|
||||
<p>Initialize an empty list before the start of the loop.
|
||||
During the loop append the substrings to the list.
|
||||
At the end of the loop, convert the list to a string by using <code>''.join(list)</code>.</p>
|
||||
|
||||
|
||||
@@ -13,17 +13,16 @@
|
||||
import python
|
||||
|
||||
predicate string_concat_in_loop(BinaryExpr b) {
|
||||
b.getOp() instanceof Add
|
||||
and
|
||||
exists(SsaVariable d, SsaVariable u, BinaryExprNode add, ClassObject str_type |
|
||||
add.getNode() = b and d = u.getAnUltimateDefinition() |
|
||||
d.getDefinition().(DefinitionNode).getValue() = add and u.getAUse() = add.getAnOperand() and
|
||||
add.getAnOperand().refersTo(_, str_type, _) and
|
||||
(str_type = theBytesType() or str_type = theUnicodeType())
|
||||
b.getOp() instanceof Add and
|
||||
exists(SsaVariable d, SsaVariable u, BinaryExprNode add |
|
||||
add.getNode() = b and d = u.getAnUltimateDefinition()
|
||||
|
|
||||
d.getDefinition().(DefinitionNode).getValue() = add and
|
||||
u.getAUse() = add.getAnOperand() and
|
||||
add.getAnOperand().pointsTo().getClass() = ClassValue::str()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
from BinaryExpr b, Stmt s
|
||||
where string_concat_in_loop(b) and s.getASubExpression() = b
|
||||
select s, "String concatenation in a loop is quadratic in the number of iterations."
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
|
||||
import python
|
||||
|
||||
|
||||
predicate main_eq_name(If i) {
|
||||
exists(Name n, StrConst m, Compare c |
|
||||
i.getTest() = c and c.getLeft() = n and
|
||||
i.getTest() = c and
|
||||
c.getLeft() = n and
|
||||
c.getAComparator() = m and
|
||||
n.getId() = "__name__" and
|
||||
m.getText() = "__main__"
|
||||
@@ -24,12 +24,17 @@ predicate main_eq_name(If i) {
|
||||
}
|
||||
|
||||
predicate is_print_stmt(Stmt s) {
|
||||
s instanceof Print or
|
||||
exists(ExprStmt e, Call c, Name n | e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print")
|
||||
s instanceof Print
|
||||
or
|
||||
exists(ExprStmt e, Call c, Name n |
|
||||
e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print"
|
||||
)
|
||||
}
|
||||
|
||||
from Stmt p
|
||||
where is_print_stmt(p) and
|
||||
exists(ModuleObject m | m.getModule() = p.getScope() and m.getKind() = "module") and
|
||||
not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p)
|
||||
where
|
||||
is_print_stmt(p) and
|
||||
// TODO: Need to discuss how we would like to handle ModuleObject.getKind in the glorious future
|
||||
exists(ModuleValue m | m.getScope() = p.getScope() and m.isUsedAsModule()) and
|
||||
not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p)
|
||||
select p, "Print statement may execute during import."
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
* @id py/unnecessary-delete
|
||||
*/
|
||||
|
||||
|
||||
import python
|
||||
|
||||
from Delete del, Expr e, Function f
|
||||
@@ -23,11 +22,11 @@ where
|
||||
not e instanceof Subscript and
|
||||
not e instanceof Attribute and
|
||||
not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and
|
||||
/* False positive: calling `sys.exc_info` within a function results in a
|
||||
reference cycle,and an explicit call to `del` helps break this cycle. */
|
||||
not exists(FunctionObject ex |
|
||||
ex.hasLongName("sys.exc_info") and
|
||||
// False positive: calling `sys.exc_info` within a function results in a
|
||||
// reference cycle, and an explicit call to `del` helps break this cycle.
|
||||
not exists(FunctionValue ex |
|
||||
ex = Value::named("sys.exc_info") and
|
||||
ex.getACall().getScope() = f
|
||||
)
|
||||
select del, "Unnecessary deletion of local variable $@ in function $@.",
|
||||
e.getLocation(), e.toString(), f.getLocation(), f.getName()
|
||||
select del, "Unnecessary deletion of local variable $@ in function $@.", e.getLocation(),
|
||||
e.toString(), f.getLocation(), f.getName()
|
||||
|
||||
@@ -14,9 +14,11 @@ import python
|
||||
|
||||
from Stmt loop, StmtList body, StmtList clause, string kind
|
||||
where
|
||||
(exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for")
|
||||
or
|
||||
exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while")
|
||||
)
|
||||
and not exists(Break b | body.contains(b))
|
||||
select loop, "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body."
|
||||
(
|
||||
exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for")
|
||||
or
|
||||
exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while")
|
||||
) and
|
||||
not exists(Break b | body.contains(b))
|
||||
select loop,
|
||||
"This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body."
|
||||
|
||||
@@ -13,21 +13,20 @@
|
||||
import python
|
||||
|
||||
predicate is_doc_string(ExprStmt s) {
|
||||
s.getValue() instanceof Unicode or s.getValue() instanceof Bytes
|
||||
s.getValue() instanceof Unicode or s.getValue() instanceof Bytes
|
||||
}
|
||||
|
||||
predicate has_doc_string(StmtList stmts) {
|
||||
stmts.getParent() instanceof Scope
|
||||
and
|
||||
stmts.getParent() instanceof Scope and
|
||||
is_doc_string(stmts.getItem(0))
|
||||
}
|
||||
|
||||
from Pass p, StmtList list
|
||||
where list.getAnItem() = p and
|
||||
(
|
||||
strictcount(list.getAnItem()) = 2 and not has_doc_string(list)
|
||||
or
|
||||
strictcount(list.getAnItem()) > 2
|
||||
)
|
||||
where
|
||||
list.getAnItem() = p and
|
||||
(
|
||||
strictcount(list.getAnItem()) = 2 and not has_doc_string(list)
|
||||
or
|
||||
strictcount(list.getAnItem()) > 2
|
||||
)
|
||||
select p, "Unnecessary 'pass' statement."
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
|
||||
import python
|
||||
|
||||
from Call call, ClassObject ex
|
||||
where call.getFunc().refersTo(ex) and ex.getAnImproperSuperType() = theExceptionType()
|
||||
and exists(ExprStmt s | s.getValue() = call)
|
||||
|
||||
from Call call, ClassValue ex
|
||||
where
|
||||
call.getFunc().pointsTo(ex) and
|
||||
ex.getASuperType() = ClassValue::exception() and
|
||||
exists(ExprStmt s | s.getValue() = call)
|
||||
select call, "Instantiating an exception, but not raising it, has no effect"
|
||||
|
||||
@@ -12,5 +12,7 @@
|
||||
import python
|
||||
|
||||
from CallNode call, string name
|
||||
where call.getFunction().refersTo(Object::quitter(name))
|
||||
select call, "The '" + name + "' site.Quitter object may not exist if the 'site' module is not loaded or is modified."
|
||||
where call.getFunction().pointsTo(Value::siteQuitter(name))
|
||||
select call,
|
||||
"The '" + name +
|
||||
"' site.Quitter object may not exist if the 'site' module is not loaded or is modified."
|
||||
|
||||
@@ -5,7 +5,7 @@ predicate monkey_patched_builtin(string name) {
|
||||
subscr.isStore() and
|
||||
subscr.getIndex().getNode() = s and
|
||||
s.getText() = name and
|
||||
subscr.getValue() = attr and
|
||||
subscr.getObject() = attr and
|
||||
attr.getObject("__dict__").pointsTo(Module::builtinModule())
|
||||
)
|
||||
or
|
||||
|
||||
@@ -910,6 +910,10 @@ private AstNode assigned_value(Expr lhs) {
|
||||
|
||||
predicate nested_sequence_assign(Expr left_parent, Expr right_parent,
|
||||
Expr left_result, Expr right_result) {
|
||||
exists(Assign a |
|
||||
a.getATarget().getASubExpression*() = left_parent and
|
||||
a.getValue().getASubExpression*() = right_parent
|
||||
) and
|
||||
exists(int i, Expr left_elem, Expr right_elem
|
||||
|
|
||||
(
|
||||
|
||||
@@ -726,6 +726,8 @@ private class EssaTaintTracking extends string {
|
||||
private TaintKind iterable_unpacking_descent(
|
||||
SequenceNode left_parent, ControlFlowNode left_defn, CollectionKind parent_kind
|
||||
) {
|
||||
//TODO: Fix the cartesian product in this predicate
|
||||
none() and
|
||||
left_parent.getAnElement() = left_defn and
|
||||
// Handle `a, *b = some_iterable`
|
||||
if left_defn instanceof StarredNode
|
||||
|
||||
@@ -286,14 +286,14 @@ module DictKind {
|
||||
pragma[noinline]
|
||||
private predicate subscript_index(ControlFlowNode obj, SubscriptNode sub) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
sub.getObject() = obj and
|
||||
not sub.getNode().getIndex() instanceof Slice
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate subscript_slice(ControlFlowNode obj, SubscriptNode sub) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
sub.getObject() = obj and
|
||||
sub.getNode().getIndex() instanceof Slice
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,39 @@ class PropertyInternal extends ObjectInternal, TProperty {
|
||||
this = TProperty(_, _, result)
|
||||
}
|
||||
|
||||
private CallNode getCallNode() { this = TProperty(result, _, _) }
|
||||
|
||||
/** Gets the setter function of this property */
|
||||
CallableObjectInternal getSetter() {
|
||||
// @x.setter
|
||||
exists(CallNode call, AttrNode setter |
|
||||
call.getFunction() = setter and
|
||||
call.getFunction() = setter and
|
||||
PointsToInternal::pointsTo(setter.getObject("setter"), this.getContext(), this, _) and
|
||||
PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _)
|
||||
)
|
||||
or
|
||||
// x = property(getter, setter, deleter)
|
||||
exists(ControlFlowNode setter_arg |
|
||||
setter_arg = getCallNode().getArg(1) or setter_arg = getCallNode().getArgByName("fset")
|
||||
|
|
||||
PointsToInternal::pointsTo(setter_arg, this.getContext(), result, _)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the setter function of this property */
|
||||
CallableObjectInternal getDeleter() {
|
||||
exists(CallNode call, AttrNode setter |
|
||||
call.getFunction() = setter and
|
||||
PointsToInternal::pointsTo(setter.getObject("deleter"), this.getContext(), this, _) and
|
||||
PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _)
|
||||
)
|
||||
or
|
||||
// x = property(getter, setter, deleter)
|
||||
exists(ControlFlowNode deleter_arg |
|
||||
deleter_arg = getCallNode().getArg(2) or deleter_arg = getCallNode().getArgByName("fdel")
|
||||
|
|
||||
PointsToInternal::pointsTo(deleter_arg, this.getContext(), result, _)
|
||||
)
|
||||
}
|
||||
|
||||
private Context getContext() { this = TProperty(_,result, _) }
|
||||
|
||||
@@ -44,6 +44,12 @@ class Value extends TObject {
|
||||
PointsToInternal::pointsTo(result, _, this, _)
|
||||
}
|
||||
|
||||
/** Gets the origin CFG node for this value. */
|
||||
ControlFlowNode getOrigin() {
|
||||
result = this.(ObjectInternal).getOrigin()
|
||||
}
|
||||
|
||||
|
||||
/** Gets the class of this object.
|
||||
* Strictly, the `Value` representing the class of the objects
|
||||
* represented by this Value.
|
||||
@@ -78,7 +84,7 @@ class Value extends TObject {
|
||||
predicate isBuiltin() {
|
||||
this.(ObjectInternal).isBuiltin()
|
||||
}
|
||||
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
this.(ObjectInternal).getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec)
|
||||
or
|
||||
@@ -187,6 +193,37 @@ class ModuleValue extends Value {
|
||||
result.importedAs(this.getScope().getAnImportedModuleName())
|
||||
}
|
||||
|
||||
/** When used as a normal module (for example, imported and used by other modules) */
|
||||
predicate isUsedAsModule() {
|
||||
this.isBuiltin()
|
||||
or
|
||||
this.isPackage()
|
||||
or
|
||||
exists(ImportingStmt i | this.importedAs(i.getAnImportedModuleName()))
|
||||
or
|
||||
this.getPath().getBaseName() = "__init__.py"
|
||||
}
|
||||
|
||||
/** When used (exclusively) as a script (will not include normal modules that can also be run as a script) */
|
||||
predicate isUsedAsScript() {
|
||||
not isUsedAsModule() and
|
||||
(
|
||||
not this.getPath().getExtension() = "py"
|
||||
or
|
||||
exists(If i, Name name, StrConst main, Cmpop op |
|
||||
i.getScope() = this.getScope() and
|
||||
op instanceof Eq and
|
||||
i.getTest().(Compare).compares(name, op, main) and
|
||||
name.getId() = "__name__" and main.getText() = "__main__"
|
||||
)
|
||||
or
|
||||
exists(Comment c |
|
||||
c.getLocation().getFile() = this.getPath() and
|
||||
c.getLocation().getStartLine() = 1 and
|
||||
c.getText().regexpMatch("^#!/.*python(2|3)?[ \\\\t]*$")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Module {
|
||||
@@ -301,6 +338,19 @@ module Value {
|
||||
result = ObjectInternal::none_()
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorcuts added by the `site` module to exit your interactive session.
|
||||
*
|
||||
* see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module
|
||||
*/
|
||||
Value siteQuitter(string name) {
|
||||
(
|
||||
name = "exit"
|
||||
or
|
||||
name = "quit"
|
||||
) and
|
||||
result = Value::named(name)
|
||||
}
|
||||
}
|
||||
|
||||
/** Class representing callables in the Python program
|
||||
@@ -412,12 +462,57 @@ class ClassValue extends Value {
|
||||
or
|
||||
this.hasAttribute("__getitem__")
|
||||
}
|
||||
|
||||
/** Holds if this class is a container(). That is, does it have a __getitem__ method.*/
|
||||
predicate isContainer() {
|
||||
exists(this.lookup("__getitem__"))
|
||||
}
|
||||
|
||||
/** Holds if this class is probably a sequence. */
|
||||
predicate isSequence() {
|
||||
/* To determine whether something is a sequence or a mapping is not entirely clear,
|
||||
* so we need to guess a bit.
|
||||
*/
|
||||
this.getASuperType() = ClassValue::tuple()
|
||||
or
|
||||
this.getASuperType() = ClassValue::list()
|
||||
or
|
||||
this.getASuperType() = ClassValue::range()
|
||||
or
|
||||
this.getASuperType() = ClassValue::bytes()
|
||||
or
|
||||
this.getASuperType() = ClassValue::unicode()
|
||||
or
|
||||
major_version() = 2 and this.getASuperType() = Value::named("collections.Sequence")
|
||||
or
|
||||
major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Sequence")
|
||||
or
|
||||
/* Does it have an index or __reversed__ method? */
|
||||
this.isContainer() and
|
||||
(
|
||||
this.hasAttribute("index") or
|
||||
this.hasAttribute("__reversed__")
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this class is a mapping. */
|
||||
predicate isMapping() {
|
||||
this.hasAttribute("__getitem__")
|
||||
and
|
||||
not this.isSequence()
|
||||
}
|
||||
|
||||
/** Holds if this class is a descriptor. */
|
||||
predicate isDescriptorType() {
|
||||
this.hasAttribute("__get__")
|
||||
}
|
||||
|
||||
/** Holds if this class is a context manager. */
|
||||
predicate isContextManager() {
|
||||
this.hasAttribute("__enter__") and
|
||||
this.hasAttribute("__exit__")
|
||||
}
|
||||
|
||||
/** Gets the qualified name for this class.
|
||||
* Should return the same name as the `__qualname__` attribute on classes in Python 3.
|
||||
*/
|
||||
@@ -481,11 +576,12 @@ class ClassValue extends Value {
|
||||
predicate declaresAttribute(string name) {
|
||||
this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name)
|
||||
}
|
||||
|
||||
/** Whether this class is a legal exception class.
|
||||
|
||||
/** Whether this class is a legal exception class.
|
||||
* What constitutes a legal exception class differs between major versions */
|
||||
predicate isLegalExceptionType() {
|
||||
not this.isNewStyle() or
|
||||
not this.isNewStyle()
|
||||
or
|
||||
this.getASuperType() = ClassValue::baseException()
|
||||
or
|
||||
major_version() = 2 and this = ClassValue::tuple()
|
||||
@@ -514,6 +610,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 */
|
||||
@@ -659,6 +777,34 @@ class NumericValue extends Value {
|
||||
}
|
||||
}
|
||||
|
||||
/** A Python property:
|
||||
* @property
|
||||
* def f():
|
||||
* ....
|
||||
*
|
||||
* https://docs.python.org/3/howto/descriptor.html#properties
|
||||
* https://docs.python.org/3/library/functions.html#property
|
||||
*/
|
||||
class PropertyValue extends Value {
|
||||
|
||||
PropertyValue() {
|
||||
this instanceof PropertyInternal
|
||||
}
|
||||
|
||||
CallableValue getGetter(){
|
||||
result = this.(PropertyInternal).getGetter()
|
||||
}
|
||||
|
||||
CallableValue getSetter(){
|
||||
result = this.(PropertyInternal).getSetter()
|
||||
}
|
||||
|
||||
CallableValue getDeleter(){
|
||||
result = this.(PropertyInternal).getDeleter()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A method-resolution-order sequence of classes */
|
||||
class MRO extends TClassList {
|
||||
|
||||
@@ -707,34 +853,34 @@ module ClassValue {
|
||||
ClassValue bool() {
|
||||
result = TBuiltinClassObject(Builtin::special("bool"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `tuple` class. */
|
||||
ClassValue tuple() {
|
||||
result = TBuiltinClassObject(Builtin::special("tuple"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `list` class. */
|
||||
ClassValue list() {
|
||||
result = TBuiltinClassObject(Builtin::special("list"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */
|
||||
ClassValue range() {
|
||||
major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange"))
|
||||
or
|
||||
major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `dict` class. */
|
||||
ClassValue dict() {
|
||||
result = TBuiltinClassObject(Builtin::special("dict"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `set` class. */
|
||||
ClassValue set() {
|
||||
result = TBuiltinClassObject(Builtin::special("set"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `object` class. */
|
||||
ClassValue object() {
|
||||
result = TBuiltinClassObject(Builtin::special("object"))
|
||||
@@ -744,7 +890,7 @@ module ClassValue {
|
||||
ClassValue int_() {
|
||||
result = TBuiltinClassObject(Builtin::special("int"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `long` class. */
|
||||
ClassValue long() {
|
||||
result = TBuiltinClassObject(Builtin::special("long"))
|
||||
@@ -754,12 +900,12 @@ module ClassValue {
|
||||
ClassValue float_() {
|
||||
result = TBuiltinClassObject(Builtin::special("float"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `complex` class. */
|
||||
ClassValue complex() {
|
||||
result = TBuiltinClassObject(Builtin::special("complex"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */
|
||||
ClassValue bytes() {
|
||||
result = TBuiltinClassObject(Builtin::special("bytes"))
|
||||
@@ -779,12 +925,12 @@ module ClassValue {
|
||||
else
|
||||
result = unicode()
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `property` class. */
|
||||
ClassValue property() {
|
||||
result = TBuiltinClassObject(Builtin::special("property"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the class of Python functions. */
|
||||
ClassValue functionType() {
|
||||
result = TBuiltinClassObject(Builtin::special("FunctionType"))
|
||||
@@ -794,32 +940,32 @@ module ClassValue {
|
||||
ClassValue builtinFunction() {
|
||||
result = Value::named("len").getClass()
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `generatorType` class. */
|
||||
ClassValue generator() {
|
||||
result = TBuiltinClassObject(Builtin::special("generator"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `type` class. */
|
||||
ClassValue type() {
|
||||
result = TType()
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for `ClassType`. */
|
||||
ClassValue classType() {
|
||||
result = TBuiltinClassObject(Builtin::special("ClassType"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for `InstanceType`. */
|
||||
ClassValue instanceType() {
|
||||
result = TBuiltinClassObject(Builtin::special("InstanceType"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for `super`. */
|
||||
ClassValue super_() {
|
||||
result = TBuiltinClassObject(Builtin::special("super"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `classmethod` class. */
|
||||
ClassValue classmethod() {
|
||||
result = TBuiltinClassObject(Builtin::special("ClassMethod"))
|
||||
@@ -829,26 +975,26 @@ module ClassValue {
|
||||
ClassValue staticmethod() {
|
||||
result = TBuiltinClassObject(Builtin::special("StaticMethod"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `MethodType` class. */
|
||||
pragma [noinline]
|
||||
ClassValue methodType() {
|
||||
result = TBuiltinClassObject(Builtin::special("MethodType"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `MethodDescriptorType` class. */
|
||||
ClassValue methodDescriptorType() {
|
||||
result = TBuiltinClassObject(Builtin::special("MethodDescriptorType"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `GetSetDescriptorType` class. */
|
||||
ClassValue getSetDescriptorType() {
|
||||
result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType"))
|
||||
}
|
||||
|
||||
|
||||
/** 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. */
|
||||
@@ -860,17 +1006,17 @@ module ClassValue {
|
||||
ClassValue exception() {
|
||||
result = TBuiltinClassObject(Builtin::special("Exception"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `BaseException` class. */
|
||||
ClassValue baseException() {
|
||||
result = TBuiltinClassObject(Builtin::special("BaseException"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `NoneType` class. */
|
||||
ClassValue nonetype() {
|
||||
result = TBuiltinClassObject(Builtin::special("NoneType"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `TypeError` class */
|
||||
ClassValue typeError() {
|
||||
result = TBuiltinClassObject(Builtin::special("TypeError"))
|
||||
@@ -880,22 +1026,27 @@ module ClassValue {
|
||||
ClassValue nameError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("NameError"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `AttributeError` class. */
|
||||
ClassValue attributeError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("AttributeError"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `KeyError` class. */
|
||||
ClassValue keyError() {
|
||||
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"))
|
||||
}
|
||||
|
||||
|
||||
/** Get the `ClassValue` for the `NotImplementedError` class. */
|
||||
ClassValue notImplementedError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("NotImplementedError"))
|
||||
@@ -906,4 +1057,14 @@ module ClassValue {
|
||||
result = TBuiltinClassObject(Builtin::builtin("ImportError"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `UnicodeEncodeError` class. */
|
||||
ClassValue unicodeEncodeError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `UnicodeDecodeError` class. */
|
||||
ClassValue unicodeDecodeError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,25 +12,27 @@ private predicate re_module_function(string name, int flags) {
|
||||
name = "subn" and flags = 4
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `s` is used as a regex with the `re` module, with the regex-mode `mode` (if known).
|
||||
* If regex mode is not known, `mode` will be `"None"`.
|
||||
*/
|
||||
predicate used_as_regex(Expr s, string mode) {
|
||||
(s instanceof Bytes or s instanceof Unicode)
|
||||
and
|
||||
exists(ModuleValue re | re.getName() = "re" |
|
||||
/* Call to re.xxx(regex, ... [mode]) */
|
||||
exists(CallNode call, string name |
|
||||
call.getArg(0).refersTo(_, _, s.getAFlowNode()) and
|
||||
call.getFunction().pointsTo(re.attr(name)) |
|
||||
mode = "None"
|
||||
or
|
||||
exists(Value obj |
|
||||
mode = mode_from_mode_object(obj) |
|
||||
exists(int flags_arg |
|
||||
re_module_function(name, flags_arg) and
|
||||
call.getArg(flags_arg).pointsTo(obj)
|
||||
)
|
||||
or
|
||||
call.getArgByName("flags").pointsTo(obj)
|
||||
/* Call to re.xxx(regex, ... [mode]) */
|
||||
exists(CallNode call, string name |
|
||||
call.getArg(0).refersTo(_, _, s.getAFlowNode()) and
|
||||
call.getFunction().pointsTo(Module::named("re").attr(name)) |
|
||||
mode = "None"
|
||||
or
|
||||
exists(Value obj |
|
||||
mode = mode_from_mode_object(obj) |
|
||||
exists(int flags_arg |
|
||||
re_module_function(name, flags_arg) and
|
||||
call.getArg(flags_arg).pointsTo(obj)
|
||||
)
|
||||
or
|
||||
call.getArgByName("flags").pointsTo(obj)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ module ClearTextLogging {
|
||||
PrintSink() {
|
||||
exists(CallNode call |
|
||||
call.getAnArg() = this and
|
||||
thePrintFunction().(FunctionObject).getACall() = call
|
||||
call = Value::named("print").getACall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,11 @@ abstract class WeakCryptoSink extends TaintSink {
|
||||
}
|
||||
}
|
||||
|
||||
/** Modeling the 'pycrypto' package https://github.com/dlitz/pycrypto (latest release 2013) */
|
||||
module Pycrypto {
|
||||
|
||||
ModuleObject cipher(string name) {
|
||||
exists(PackageObject crypto |
|
||||
crypto.getName() = "Crypto.Cipher" |
|
||||
crypto.submodule(name) = result
|
||||
)
|
||||
ModuleValue cipher(string name) {
|
||||
result = Module::named("Crypto.Cipher").attr(name)
|
||||
}
|
||||
|
||||
class CipherInstance extends TaintKind {
|
||||
@@ -51,7 +49,7 @@ module Pycrypto {
|
||||
CipherInstanceSource() {
|
||||
exists(AttrNode attr |
|
||||
this.(CallNode).getFunction() = attr and
|
||||
attr.getObject("new").refersTo(cipher(instance.getName()))
|
||||
attr.getObject("new").pointsTo(cipher(instance.getName()))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -59,7 +57,7 @@ module Pycrypto {
|
||||
result = "Source of " + instance
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind = instance
|
||||
}
|
||||
|
||||
@@ -70,12 +68,12 @@ module Pycrypto {
|
||||
string name;
|
||||
|
||||
PycryptoWeakCryptoSink() {
|
||||
exists(CallNode call, AttrNode method, CipherInstance Cipher |
|
||||
exists(CallNode call, AttrNode method, CipherInstance cipher |
|
||||
call.getAnArg() = this and
|
||||
call.getFunction() = method and
|
||||
Cipher.taints(method.getObject("encrypt")) and
|
||||
Cipher.isWeak() and
|
||||
Cipher.getName() = name
|
||||
cipher.taints(method.getObject("encrypt")) and
|
||||
cipher.isWeak() and
|
||||
cipher.getName() = name
|
||||
)
|
||||
}
|
||||
|
||||
@@ -89,25 +87,25 @@ module Pycrypto {
|
||||
|
||||
module Cryptography {
|
||||
|
||||
PackageObject ciphers() {
|
||||
result.getName() = "cryptography.hazmat.primitives.ciphers"
|
||||
ModuleValue ciphers() {
|
||||
result = Module::named("cryptography.hazmat.primitives.ciphers") and
|
||||
result.isPackage()
|
||||
}
|
||||
|
||||
class CipherClass extends ClassObject {
|
||||
class CipherClass extends ClassValue {
|
||||
CipherClass() {
|
||||
ciphers().attr("Cipher") = this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AlgorithmClass extends ClassObject {
|
||||
class AlgorithmClass extends ClassValue {
|
||||
|
||||
AlgorithmClass() {
|
||||
ciphers().submodule("algorithms").attr(_) = this
|
||||
ciphers().attr("algorithms").attr(_) = this
|
||||
}
|
||||
|
||||
string getAlgorithmName() {
|
||||
result = this.declaredAttribute("name").(StringObject).getText()
|
||||
result = this.declaredAttribute("name").(StringValue).getText()
|
||||
}
|
||||
|
||||
predicate isWeak() {
|
||||
@@ -134,7 +132,7 @@ module Cryptography {
|
||||
cls.isWeak()
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "encryptor" and
|
||||
result.(Encryptor).getAlgorithm() = this.getAlgorithm()
|
||||
}
|
||||
@@ -144,11 +142,11 @@ module Cryptography {
|
||||
class CipherSource extends TaintSource {
|
||||
|
||||
CipherSource() {
|
||||
this.(CallNode).getFunction().refersTo(any(CipherClass cls))
|
||||
this.(CallNode).getFunction().pointsTo(any(CipherClass cls))
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
this.(CallNode).getArg(0).refersTo(_, kind.(CipherInstance).getAlgorithm(), _)
|
||||
this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
@@ -203,5 +201,3 @@ private class CipherConfig extends TaintTracking::Configuration {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class ExceptionInfo extends StringKind {
|
||||
|
||||
}
|
||||
|
||||
/** A class representing sources of information about
|
||||
/** A class representing sources of information about
|
||||
* execution state exposed in tracebacks and the like.
|
||||
*/
|
||||
abstract class ErrorInfoSource extends TaintSource {}
|
||||
@@ -59,9 +59,9 @@ class ExceptionKind extends TaintKind {
|
||||
class ExceptionSource extends ErrorInfoSource {
|
||||
|
||||
ExceptionSource() {
|
||||
exists(ClassObject cls |
|
||||
cls.isSubclassOf(theExceptionType()) and
|
||||
this.(ControlFlowNode).refersTo(_, cls, _)
|
||||
exists(ClassValue cls |
|
||||
cls.getASuperType() = ClassValue::baseException() and
|
||||
this.(ControlFlowNode).pointsTo().getClass() = cls
|
||||
)
|
||||
or
|
||||
this = any(ExceptStmt s).getName().getAFlowNode()
|
||||
@@ -116,7 +116,7 @@ class CallToTracebackFunction extends ErrorInfoSource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents calls to functions in the `traceback` module that return a single
|
||||
* string of information about an exception.
|
||||
*/
|
||||
|
||||
@@ -166,9 +166,9 @@ module SensitiveData {
|
||||
|
||||
SensitiveRequestParameter() {
|
||||
this.(CallNode).getFunction().(AttrNode).getName() = "get" and
|
||||
exists(string sensitive |
|
||||
this.(CallNode).getAnArg().refersTo(any(StringObject s | s.getText() = sensitive)) and
|
||||
data = HeuristicNames::getSensitiveDataForName(sensitive)
|
||||
exists(StringValue sensitive |
|
||||
this.(CallNode).getAnArg().pointsTo(sensitive) and
|
||||
data = HeuristicNames::getSensitiveDataForName(sensitive.getText())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -71,11 +71,7 @@ private predicate str_method_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
/* tonode = ....format(fromnode) */
|
||||
private predicate str_format(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getName() = "format" and
|
||||
(
|
||||
tonode.getAnArg() = fromnode
|
||||
or
|
||||
tonode.getNode().getAKeyword().getValue() = fromnode.getNode()
|
||||
)
|
||||
tonode.getAnArg() = fromnode
|
||||
}
|
||||
|
||||
/* tonode = codec.[en|de]code(fromnode)*/
|
||||
@@ -93,9 +89,10 @@ private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) {
|
||||
/* tonode = str(fromnode)*/
|
||||
private predicate to_str(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getAnArg() = fromnode and
|
||||
exists(ClassObject str |
|
||||
tonode.getFunction().refersTo(str) |
|
||||
str = theUnicodeType() or str = theBytesType()
|
||||
(
|
||||
tonode = ClassValue::bytes().getACall()
|
||||
or
|
||||
tonode = ClassValue::unicode().getACall()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -104,17 +101,14 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
|
||||
exists(Slice all |
|
||||
all = tonode.getIndex().getNode() and
|
||||
not exists(all.getStart()) and not exists(all.getStop()) and
|
||||
tonode.getValue() = fromnode
|
||||
tonode.getObject() = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
/* tonode = os.path.join(..., fromnode, ...) */
|
||||
private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
|
||||
exists(FunctionObject path_join |
|
||||
path_join = ModuleObject::named("os").attr("path").(ModuleObject).attr("join")
|
||||
and
|
||||
tonode = path_join.getACall() and tonode.getAnArg() = fromnode
|
||||
)
|
||||
tonode = Value::named("os.path.join").getACall()
|
||||
and tonode.getAnArg() = fromnode
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing a dictionary mapping str->"taint" */
|
||||
@@ -125,5 +119,3 @@ class StringDictKind extends DictKind {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ import python
|
||||
predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
|
||||
or
|
||||
exists(ModuleObject copy, string name |
|
||||
exists(ModuleValue copy, string name |
|
||||
name = "copy" or name = "deepcopy" |
|
||||
copy.attr(name).(FunctionObject).getACall() = tonode and
|
||||
copy.attr(name).(FunctionValue).getACall() = tonode and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
or
|
||||
tonode.getFunction().refersTo(Object::builtin("reversed")) and
|
||||
tonode.getFunction().pointsTo(Value::named("reversed")) and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
|
||||
@@ -134,16 +134,13 @@ private predicate json_subscript_taint(
|
||||
SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key
|
||||
) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
sub.getObject() = 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
|
||||
)
|
||||
tonode = Value::named("json.loads").getACall() and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
|
||||
private predicate urlsplit(ControlFlowNode fromnode, CallNode tonode) {
|
||||
@@ -223,19 +220,19 @@ class UrlsplitUrlparseTempSanitizer extends Sanitizer {
|
||||
or
|
||||
full_use.(AttrNode).getObject() = test.getInput().getAUse()
|
||||
|
|
||||
clears_taint(_, full_use, test.getTest(), test.getSense())
|
||||
clears_taint(full_use, test.getTest(), test.getSense())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate clears_taint(ControlFlowNode final_test, ControlFlowNode tainted, ControlFlowNode test, boolean sense) {
|
||||
test_equality_with_const(final_test, tainted, sense)
|
||||
private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) {
|
||||
test_equality_with_const(test, tainted, sense)
|
||||
or
|
||||
test_in_const_seq(final_test, tainted, sense)
|
||||
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(final_test, tainted, nested_test, sense.booleanNot())
|
||||
clears_taint(tainted, nested_test, sense.booleanNot())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -265,10 +265,10 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
or
|
||||
exists(Object tup |
|
||||
this.handledObject_objectapi(tup, theTupleType(), _) |
|
||||
element_from_tuple(tup).refersTo(obj, cls, origin)
|
||||
element_from_tuple_objectapi(tup).refersTo(obj, cls, origin)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) {
|
||||
val.getClass() = cls and
|
||||
(
|
||||
@@ -276,7 +276,7 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
or
|
||||
exists(TupleValue tup |
|
||||
this.handledObject(tup, ClassValue::tuple(), _) |
|
||||
val = tup.getItem(_) and origin.pointsTo(val)
|
||||
val = tup.getItem(_) and origin = val.getOrigin()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -289,7 +289,7 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
not exists(this.getNode().(ExceptStmt).getType()) and obj = theBaseExceptionType() and cls = theTypeType() and
|
||||
origin = this
|
||||
}
|
||||
|
||||
|
||||
/** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */
|
||||
pragma [noinline]
|
||||
predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) {
|
||||
@@ -298,6 +298,8 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
not exists(this.getNode().(ExceptStmt).getType()) and val = ClassValue::baseException() and cls = ClassValue::type() and
|
||||
origin = this
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Whether this `except` handles `cls` */
|
||||
predicate handles(ClassObject cls) {
|
||||
@@ -309,7 +311,7 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
|
||||
}
|
||||
|
||||
private ControlFlowNode element_from_tuple(Object tuple) {
|
||||
private ControlFlowNode element_from_tuple_objectapi(Object tuple) {
|
||||
exists(Tuple t |
|
||||
t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()
|
||||
)
|
||||
|
||||
@@ -82,7 +82,7 @@ private predicate tracking_step(ControlFlowNode src, ControlFlowNode dest) {
|
||||
or
|
||||
src = dest.(AttrNode).getObject()
|
||||
or
|
||||
src = dest.(SubscriptNode).getValue()
|
||||
src = dest.(SubscriptNode).getObject()
|
||||
or
|
||||
tracked_call_step(src, dest)
|
||||
or
|
||||
|
||||
@@ -26,7 +26,7 @@ class WsgiEnvironment extends TaintKind {
|
||||
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and
|
||||
tonode.(CallNode).getArg(0).pointsTo(key)
|
||||
or
|
||||
tonode.(SubscriptNode).getValue() = fromnode and tonode.isLoad() and
|
||||
tonode.(SubscriptNode).getObject() = fromnode and tonode.isLoad() and
|
||||
tonode.(SubscriptNode).getIndex().pointsTo(key)
|
||||
|
|
||||
key = Value::forString(text) and result instanceof ExternalStringKind and
|
||||
@@ -66,7 +66,7 @@ class UntrustedCookie extends TaintKind {
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.(SubscriptNode).getValue() = fromnode and
|
||||
tonode.(SubscriptNode).getObject() = fromnode and
|
||||
result instanceof UntrustedMorsel
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class DjangoRequest extends TaintKind {
|
||||
/* Helper for getTaintForStep() */
|
||||
pragma[noinline]
|
||||
private predicate subscript_taint(SubscriptNode sub, ControlFlowNode obj, TaintKind kind) {
|
||||
sub.getValue() = obj and
|
||||
sub.getObject() = obj and
|
||||
kind instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
| nonsense.py:1:14:1:14 | Syntax Error | Syntax Error (in Python 2.7). |
|
||||
| nonsense.py:1:14:1:14 | Syntax Error | Syntax Error (in Python 2). |
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
| mwe_failure.py:7:1:7:23 | class MyTest | <MISSING BASE TYPE> |
|
||||
| mwe_failure_2.py:7:1:7:23 | class MyTest | <MISSING BASE TYPE> |
|
||||
| mwe_success.py:7:1:7:23 | class MyTest | class TestCase |
|
||||
@@ -0,0 +1,10 @@
|
||||
import python
|
||||
|
||||
// as used in semmle.python.filters.Tests
|
||||
|
||||
from ClassValue c, string base
|
||||
where
|
||||
c.getScope().getLocation().getFile().getShortName().matches("mwe%.py") and
|
||||
c.getName() = "MyTest" and
|
||||
if exists(c.getABaseType()) then base = c.getABaseType().toString() else base = "<MISSING BASE TYPE>"
|
||||
select c, base
|
||||
@@ -0,0 +1,10 @@
|
||||
import subprocess
|
||||
assert subprocess.call(['run-backup']) == 0
|
||||
|
||||
class TestCase:
|
||||
pass
|
||||
|
||||
class MyTest(TestCase):
|
||||
pass
|
||||
|
||||
# found by /home/rasmus/code/ql/python/ql/test/query-tests/Statements/asserts/AssertLiteralConstant.qlref
|
||||
@@ -0,0 +1,8 @@
|
||||
import subprocess
|
||||
assert subprocess.call(['run-backup'])
|
||||
|
||||
class TestCase:
|
||||
pass
|
||||
|
||||
class MyTest(TestCase):
|
||||
pass
|
||||
@@ -0,0 +1,8 @@
|
||||
import subprocess
|
||||
subprocess.call(['run-backup'])
|
||||
|
||||
class TestCase:
|
||||
pass
|
||||
|
||||
class MyTest(TestCase):
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=1
|
||||
@@ -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 |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| nonsense.py:1:2:1:2 | Syntax Error | Syntax Error (in Python 3.5). |
|
||||
| nonsense.py:1:2:1:2 | Syntax Error | Syntax Error (in Python 3). |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| async_iterator.py:26:11:26:34 | For | $@ of class '$@' may be used in for-loop. | async_iterator.py:26:20:26:33 | ControlFlowNode for MissingAiter() | Non-iterator | async_iterator.py:13:1:13:19 | class MissingAiter | MissingAiter |
|
||||
| statements_test.py:34:5:34:19 | For | $@ of class '$@' may be used in for-loop. | statements_test.py:34:18:34:18 | ControlFlowNode for IntegerLiteral | Non-iterator | file://:0:0:0:0 | builtin-class int | int |
|
||||
| async_iterator.py:26:11:26:34 | For | $@ of class '$@' may be used in for-loop. | async_iterator.py:26:20:26:33 | ControlFlowNode for MissingAiter() | Non-iterable | async_iterator.py:13:1:13:19 | class MissingAiter | MissingAiter |
|
||||
| statements_test.py:34:5:34:19 | For | $@ of class '$@' may be used in for-loop. | statements_test.py:34:18:34:18 | ControlFlowNode for IntegerLiteral | Non-iterable | file://:0:0:0:0 | builtin-class int | int |
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.py:5:7:5:9 | ControlFlowNode for foo | int 42 |
|
||||
| test.py:11:11:11:13 | ControlFlowNode for foo | int 1 |
|
||||
| test.py:17:11:17:13 | ControlFlowNode for foo | <MISSING pointsTo()> |
|
||||
@@ -0,0 +1,10 @@
|
||||
import python
|
||||
|
||||
from NameNode name, CallNode call, string debug
|
||||
where
|
||||
call.getAnArg() = name and
|
||||
call.getFunction().(NameNode).getId() = "check" and
|
||||
if exists(name.pointsTo())
|
||||
then debug = name.pointsTo().toString()
|
||||
else debug = "<MISSING pointsTo()>"
|
||||
select name, debug
|
||||
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=1 --lang=3
|
||||
@@ -0,0 +1,17 @@
|
||||
# Only a problem in Python 3
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
foo = 42
|
||||
check(foo)
|
||||
|
||||
def func(url):
|
||||
parts = urlsplit(url)
|
||||
|
||||
foo = 1
|
||||
check(foo)
|
||||
|
||||
if parts.path: # using `urlsplit(url).path` here is equivalent
|
||||
return # using `pass` here instead makes points-to work
|
||||
|
||||
foo = 2
|
||||
check(foo) # no points-to information
|
||||
@@ -0,0 +1 @@
|
||||
| test.py:5:7:5:13 | ControlFlowNode for PATTERN | <MISSING pointsTo()> |
|
||||
@@ -0,0 +1,10 @@
|
||||
import python
|
||||
|
||||
from NameNode name, CallNode call, string debug
|
||||
where
|
||||
call.getAnArg() = name and
|
||||
call.getFunction().(NameNode).getId() = "check" and
|
||||
if exists(name.pointsTo())
|
||||
then debug = name.pointsTo().toString()
|
||||
else debug = "<MISSING pointsTo()>"
|
||||
select name, debug
|
||||
@@ -0,0 +1,5 @@
|
||||
import re
|
||||
|
||||
PATTERN = re.compile("a|b")
|
||||
|
||||
check(PATTERN)
|
||||
@@ -1,6 +1,6 @@
|
||||
| 16 | classmethod() | 17 | Function c1 |
|
||||
| 23 | classmethod() | 20 | Function c2 |
|
||||
| 24 | classmethod() | 20 | Function c2 |
|
||||
| 26 | staticmethod() | 27 | Function s1 |
|
||||
| 33 | staticmethod() | 30 | Function s2 |
|
||||
| 34 | staticmethod() | 30 | Function s2 |
|
||||
| 104 | classmethod() | 105 | Function c1 |
|
||||
| 111 | classmethod() | 108 | Function c2 |
|
||||
| 112 | classmethod() | 108 | Function c2 |
|
||||
| 114 | staticmethod() | 115 | Function s1 |
|
||||
| 121 | staticmethod() | 118 | Function s2 |
|
||||
| 122 | staticmethod() | 118 | Function s2 |
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
| 6 | Property f | 7 | Function f | 11 | Function f |
|
||||
| test.py:6:5:6:16 | Function WithDecorator.x | getter | test.py:5:6:5:13 | property x |
|
||||
| test.py:11:5:11:23 | Function WithDecorator.x | setter | test.py:5:6:5:13 | property x |
|
||||
| test.py:15:5:15:16 | Function WithDecorator.x | deleter | test.py:5:6:5:13 | property x |
|
||||
| test.py:21:5:21:16 | Function WithDecoratorOnlyGetter.x | getter | test.py:20:6:20:13 | property x |
|
||||
| test.py:28:5:28:19 | Function WithoutDecorator.getx | getter | test.py:37:9:37:59 | property getx |
|
||||
| test.py:31:5:31:26 | Function WithoutDecorator.setx | setter | test.py:37:9:37:59 | property getx |
|
||||
| test.py:34:5:34:19 | Function WithoutDecorator.delx | deleter | test.py:37:9:37:59 | property getx |
|
||||
| test.py:41:5:41:19 | Function WithoutDecoratorOnlyGetter.getx | getter | test.py:44:9:44:22 | property getx |
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
|
||||
import python
|
||||
import semmle.python.types.Descriptors
|
||||
|
||||
int lineof(Object o) {
|
||||
result = o.getOrigin().getLocation().getStartLine()
|
||||
}
|
||||
|
||||
from PropertyObject p, FunctionObject getter, FunctionObject setter
|
||||
from PropertyValue p, string method_name, FunctionValue method
|
||||
where
|
||||
getter = p.getGetter() and setter = p.getSetter()
|
||||
select lineof(p), p.toString(), lineof(getter), getter.toString(), lineof(setter), setter.toString()
|
||||
|
||||
method_name = "getter" and method = p.getGetter()
|
||||
or
|
||||
method_name = "setter" and method = p.getSetter()
|
||||
or
|
||||
method_name = "deleter" and method = p.getDeleter()
|
||||
select method, method_name, p
|
||||
|
||||
@@ -1,15 +1,103 @@
|
||||
|
||||
|
||||
|
||||
class C(object):
|
||||
class WithDecorator(object):
|
||||
def __init__(self):
|
||||
self._x = None
|
||||
|
||||
@property
|
||||
def f(self):
|
||||
return self._f
|
||||
def x(self):
|
||||
"""I'm the 'x' property."""
|
||||
return self._x
|
||||
|
||||
@f.setter
|
||||
def f(self):
|
||||
return self._f
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._x = value
|
||||
|
||||
@x.deleter
|
||||
def x(self):
|
||||
del self._x
|
||||
|
||||
class WithDecoratorOnlyGetter(object):
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return 42
|
||||
|
||||
class WithoutDecorator(object):
|
||||
def __init__(self):
|
||||
self._x = None
|
||||
|
||||
def getx(self):
|
||||
return self._x
|
||||
|
||||
def setx(self, value):
|
||||
self._x = value
|
||||
|
||||
def delx(self):
|
||||
del self._x
|
||||
|
||||
x = property(getx, setx, delx, "I'm the 'x' property.")
|
||||
|
||||
class WithoutDecoratorOnlyGetter(object):
|
||||
|
||||
def getx(self):
|
||||
return 42
|
||||
|
||||
x = property(getx)
|
||||
|
||||
class WithoutDecoratorOnlyGetterKWArg(object):
|
||||
|
||||
def getx(self):
|
||||
return 42
|
||||
|
||||
x = property(fget=getx)
|
||||
|
||||
class WithoutDecoratorOnlySetter(object):
|
||||
|
||||
def setx(self, value):
|
||||
self._x = value
|
||||
|
||||
x = property(fset=setx) # TODO: Not handled
|
||||
|
||||
class WithDecoratorOnlySetter(object):
|
||||
|
||||
x = property()
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
print('{} setting value to {}'.format(self.__class__, value))
|
||||
|
||||
class FunkyButValid(object):
|
||||
|
||||
def delx(self):
|
||||
print("deleting x")
|
||||
|
||||
x = property(fdel=delx)
|
||||
|
||||
@x.setter
|
||||
def y(self, value):
|
||||
print('setting value to {}'.format(value))
|
||||
|
||||
@y.getter
|
||||
def z(self):
|
||||
return 42
|
||||
|
||||
|
||||
wat = FunkyButValid()
|
||||
try:
|
||||
wat.x
|
||||
except AttributeError as e:
|
||||
print("x can't be read")
|
||||
del wat.x
|
||||
|
||||
try:
|
||||
wat.y
|
||||
except AttributeError as e:
|
||||
print("y can't be read")
|
||||
wat.y = 1234
|
||||
del wat.y
|
||||
|
||||
print(wat.z)
|
||||
wat.z = 10
|
||||
del wat.z
|
||||
|
||||
class D(object):
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
| file://:0:0:0:0 | Module sys | isUsedAsModule |
|
||||
| imported.py:0:0:0:0 | Module imported | isUsedAsModule |
|
||||
| main.py:0:0:0:0 | Module main | isUsedAsScript |
|
||||
| myscript.py:0:0:0:0 | Script myscript | isUsedAsScript |
|
||||
| script:0:0:0:0 | Script script | isUsedAsScript |
|
||||
16
python/ql/test/library-tests/modules/usage/ModuleUsage.ql
Normal file
16
python/ql/test/library-tests/modules/usage/ModuleUsage.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
import python
|
||||
|
||||
from ModuleValue mv, string usage
|
||||
where
|
||||
// builtin module has different name in Python 2 and 3
|
||||
not mv = Module::builtinModule() and
|
||||
(
|
||||
mv.isUsedAsModule() and usage = "isUsedAsModule"
|
||||
or
|
||||
mv.isUsedAsScript() and usage = "isUsedAsScript"
|
||||
or
|
||||
not mv.isUsedAsModule() and
|
||||
not mv.isUsedAsScript() and
|
||||
usage = "<UNKNOWN>"
|
||||
)
|
||||
select mv, usage
|
||||
6
python/ql/test/library-tests/modules/usage/imported.py
Normal file
6
python/ql/test/library-tests/modules/usage/imported.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def func():
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("I could have done something interesting...")
|
||||
print("but I didn't")
|
||||
5
python/ql/test/library-tests/modules/usage/main.py
Normal file
5
python/ql/test/library-tests/modules/usage/main.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import imported
|
||||
|
||||
if __name__ == "__main__":
|
||||
imported.func()
|
||||
print('Done')
|
||||
3
python/ql/test/library-tests/modules/usage/myscript.py
Executable file
3
python/ql/test/library-tests/modules/usage/myscript.py
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
print("I'm actually a script you see ;)")
|
||||
1
python/ql/test/library-tests/modules/usage/options
Normal file
1
python/ql/test/library-tests/modules/usage/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: -F script
|
||||
3
python/ql/test/library-tests/modules/usage/script
Executable file
3
python/ql/test/library-tests/modules/usage/script
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
print('Under construction :)')
|
||||
@@ -26,9 +26,6 @@
|
||||
| Taint [externally controlled string] | test.py:29 | test.py:29:9:29:25 | Subscript | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:16:32:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:20 | tainted_list | | --> | Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | |
|
||||
| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:19:32:19 | d | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:22:32:22 | e | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:25:32:25 | f | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:28:32:28 | g | |
|
||||
| Taint [externally controlled string] | test.py:33 | test.py:33:14:33:25 | tainted_list | | --> | Taint externally controlled string | test.py:33 | test.py:33:5:33:26 | For | |
|
||||
| Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | | --> | Taint externally controlled string | test.py:35 | test.py:35:5:35:36 | For | |
|
||||
| Taint [externally controlled string] | test.py:35 | test.py:35:23:35:34 | tainted_list | | --> | Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | |
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
| test.py:32 | test_access | b | externally controlled string |
|
||||
| test.py:32 | test_access | c | [externally controlled string] |
|
||||
| test.py:32 | test_access | d | [externally controlled string] |
|
||||
| test.py:32 | test_access | e | externally controlled string |
|
||||
| test.py:32 | test_access | f | externally controlled string |
|
||||
| test.py:32 | test_access | g | externally controlled string |
|
||||
| test.py:32 | test_access | e | NO TAINT |
|
||||
| test.py:32 | test_access | f | NO TAINT |
|
||||
| test.py:32 | test_access | g | NO TAINT |
|
||||
| test.py:34 | test_access | h | externally controlled string |
|
||||
| test.py:36 | test_access | i | externally controlled string |
|
||||
| test.py:43 | test_dict_access | a | externally controlled string |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
| 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 | 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 |
|
||||
|
||||
@@ -1,35 +1,8 @@
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:22:23:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:25:23:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:10:23:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:14:23:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:18:23:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:22:27:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:25:27:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:10:27:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:14:27:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:18:27:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:22:31:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:25:31:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:10:31:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:14:31:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:18:31:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:10:48:10 | a | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:13:48:13 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:16:48:16 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:19:48:19 | d | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:22:48:22 | e | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:25:48:25 | f | |
|
||||
| Taint [externally controlled string] | test.py:6 | test.py:6:9:6:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:10:8:10 | a | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:13:8:13 | b | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:16:8:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:12 | test.py:12:9:12:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:10:14:10 | a | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:13:14:13 | b | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:16:14:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:11:19:11 | l | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:14:19:14 | l | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:17:19:17 | l | |
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
| test.py:8 | unpacking | a | externally controlled string |
|
||||
| test.py:8 | unpacking | b | externally controlled string |
|
||||
| test.py:8 | unpacking | c | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | a | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | b | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | c | externally controlled string |
|
||||
| test.py:23 | nested | a1 | externally controlled string |
|
||||
| test.py:23 | nested | a2 | externally controlled string |
|
||||
| test.py:23 | nested | a3 | externally controlled string |
|
||||
| test.py:23 | nested | b | [externally controlled string] |
|
||||
| test.py:23 | nested | c | [externally controlled string] |
|
||||
| test.py:27 | nested | a1 | externally controlled string |
|
||||
| test.py:27 | nested | a2 | externally controlled string |
|
||||
| test.py:27 | nested | a3 | externally controlled string |
|
||||
| test.py:27 | nested | b | [externally controlled string] |
|
||||
| test.py:27 | nested | c | [externally controlled string] |
|
||||
| test.py:31 | nested | a1 | externally controlled string |
|
||||
| test.py:31 | nested | a2 | externally controlled string |
|
||||
| test.py:31 | nested | a3 | externally controlled string |
|
||||
| test.py:31 | nested | b | [externally controlled string] |
|
||||
| test.py:31 | nested | c | [externally controlled string] |
|
||||
| test.py:8 | unpacking | a | NO TAINT |
|
||||
| test.py:8 | unpacking | b | NO TAINT |
|
||||
| test.py:8 | unpacking | c | NO TAINT |
|
||||
| test.py:14 | unpacking_to_list | a | NO TAINT |
|
||||
| test.py:14 | unpacking_to_list | b | NO TAINT |
|
||||
| test.py:14 | unpacking_to_list | c | NO TAINT |
|
||||
| test.py:23 | nested | a1 | NO TAINT |
|
||||
| test.py:23 | nested | a2 | NO TAINT |
|
||||
| test.py:23 | nested | a3 | NO TAINT |
|
||||
| test.py:23 | nested | b | NO TAINT |
|
||||
| test.py:23 | nested | c | NO TAINT |
|
||||
| test.py:27 | nested | a1 | NO TAINT |
|
||||
| test.py:27 | nested | a2 | NO TAINT |
|
||||
| test.py:27 | nested | a3 | NO TAINT |
|
||||
| test.py:27 | nested | b | NO TAINT |
|
||||
| test.py:27 | nested | c | NO TAINT |
|
||||
| test.py:31 | nested | a1 | NO TAINT |
|
||||
| test.py:31 | nested | a2 | NO TAINT |
|
||||
| test.py:31 | nested | a3 | NO TAINT |
|
||||
| test.py:31 | nested | b | NO TAINT |
|
||||
| test.py:31 | nested | c | NO TAINT |
|
||||
| test.py:38 | unpack_from_set | a | NO TAINT |
|
||||
| test.py:38 | unpack_from_set | b | NO TAINT |
|
||||
| test.py:38 | unpack_from_set | c | NO TAINT |
|
||||
| test.py:48 | contrived_1 | a | externally controlled string |
|
||||
| test.py:48 | contrived_1 | b | externally controlled string |
|
||||
| test.py:48 | contrived_1 | c | externally controlled string |
|
||||
| test.py:48 | contrived_1 | d | externally controlled string |
|
||||
| test.py:48 | contrived_1 | e | externally controlled string |
|
||||
| test.py:48 | contrived_1 | f | externally controlled string |
|
||||
| test.py:48 | contrived_1 | a | NO TAINT |
|
||||
| test.py:48 | contrived_1 | b | NO TAINT |
|
||||
| test.py:48 | contrived_1 | c | NO TAINT |
|
||||
| test.py:48 | contrived_1 | d | NO TAINT |
|
||||
| test.py:48 | contrived_1 | e | NO TAINT |
|
||||
| test.py:48 | contrived_1 | f | NO TAINT |
|
||||
| test.py:56 | contrived_2 | a | NO TAINT |
|
||||
| test.py:56 | contrived_2 | b | NO TAINT |
|
||||
| test.py:56 | contrived_2 | c | NO TAINT |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| str_fmt_test.py:5:26:5:26 | x | Right hand side of a % operator must be a mapping, not class $@. | file://:Compiled Code:0:0:0:0 | builtin-class list | list |
|
||||
| str_fmt_test.py:5:26:5:26 | x | Right hand side of a % operator must be a mapping, not class $@. | file://:0:0:0:0 | builtin-class list | list |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| functions_test.py:99:5:99:40 | Function __getslice__ | __getslice__ method has been deprecated since Python 2.0 |
|
||||
| functions_test.py:102:5:102:47 | Function __setslice__ | __setslice__ method has been deprecated since Python 2.0 |
|
||||
| functions_test.py:105:5:105:40 | Function __delslice__ | __delslice__ method has been deprecated since Python 2.0 |
|
||||
| functions_test.py:99:5:99:40 | Function DeprecatedSliceMethods.__getslice__ | __getslice__ method has been deprecated since Python 2.0 |
|
||||
| functions_test.py:102:5:102:47 | Function DeprecatedSliceMethods.__setslice__ | __setslice__ method has been deprecated since Python 2.0 |
|
||||
| functions_test.py:105:5:105:40 | Function DeprecatedSliceMethods.__delslice__ | __delslice__ method has been deprecated since Python 2.0 |
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
| om_test.py:59:5:59:28 | Function __div__ | Too many parameters for special method __div__, which has 3 parameters, but should have 2, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:62:5:62:22 | Function __mul__ | Too few parameters for special method __mul__, which has 1 parameter, but should have 2, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:65:5:65:29 | Function __neg__ | Too many parameters for special method __neg__, which has 2 parameters, but should have 1, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:68:5:68:35 | Function __exit__ | Too few parameters for special method __exit__, which has 3 parameters, but should have 4, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:71:5:71:19 | Function __repr__ | Too few parameters for special method __repr__, which has no parameters, but should have 1, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:74:5:74:46 | Function __add__ | 1 default values(s) will never be used for special method __add__, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:97:15:97:34 | Function lambda | Too few parameters for special method __sub__, which has 1 parameter, but should have 2, in class $@. | om_test.py:95:1:95:28 | class NotOKSpecials | NotOKSpecials |
|
||||
| om_test.py:59:5:59:28 | Function WrongSpecials.__div__ | Too many parameters for special method __div__, which has 3 parameters, but should have 2, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:62:5:62:22 | Function WrongSpecials.__mul__ | Too few parameters for special method __mul__, which has 1 parameter, but should have 2, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:65:5:65:29 | Function WrongSpecials.__neg__ | Too many parameters for special method __neg__, which has 2 parameters, but should have 1, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:68:5:68:35 | Function WrongSpecials.__exit__ | Too few parameters for special method __exit__, which has 3 parameters, but should have 4, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:71:5:71:19 | Function WrongSpecials.__repr__ | Too few parameters for special method __repr__, which has no parameters, but should have 1, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:74:5:74:46 | Function WrongSpecials.__add__ | 1 default values(s) will never be used for special method __add__, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials |
|
||||
| om_test.py:97:15:97:34 | Function NotOKSpecials.lambda | Too few parameters for special method __sub__, which has 1 parameter, but should have 2, in class $@. | om_test.py:95:1:95:28 | class NotOKSpecials | NotOKSpecials |
|
||||
| protocols.py:104:1:104:12 | Function f | Too few parameters for special method __add__, which has 1 parameter, but should have 2, in class $@. | protocols.py:107:1:107:29 | class MissingMethods | MissingMethods |
|
||||
| protocols.py:104:1:104:12 | Function f | Too few parameters for special method __set__, which has 1 parameter, but should have 3, in class $@. | protocols.py:107:1:107:29 | class MissingMethods | MissingMethods |
|
||||
|
||||
1
python/ql/test/query-tests/Imports/unused/options
Normal file
1
python/ql/test/query-tests/Imports/unused/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=0
|
||||
@@ -1,2 +1,3 @@
|
||||
| assert.py:5:5:5:20 | Assert | This 'assert' statement contains $@ which may have side effects. | assert.py:5:13:5:19 | Yield | an expression |
|
||||
| assert.py:8:5:8:22 | Assert | This 'assert' statement contains $@ which may have side effects. | assert.py:8:12:8:22 | Attribute() | an expression |
|
||||
| side_effect.py:5:1:5:43 | Assert | This 'assert' statement contains $@ which may have side effects. | side_effect.py:5:8:5:38 | Attribute() | an expression |
|
||||
|
||||
@@ -103,4 +103,4 @@ def error_assert_in_intermediate_branch(x):
|
||||
elif yks(x):
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# For now, this test lives in its own file, since including them in the top of assert.py
|
||||
# messes up the results of the refers-to/points-to analysis
|
||||
# see /home/rasmus/code/ql/python/ql/test/library-tests/PointsTo/regressions/subprocess-assert/mwe_failure.py
|
||||
import subprocess
|
||||
assert subprocess.call(['run-backup']) == 0
|
||||
@@ -1,4 +1,4 @@
|
||||
| statements_test.py:19:5:19:18 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 2. | statements_test.py:19:15:19:18 | statements_test.py:19 | tuple |
|
||||
| statements_test.py:163:5:163:23 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:163:13:163:23 | statements_test.py:163 | list |
|
||||
| statements_test.py:172:5:172:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:167:16:167:24 | statements_test.py:167 | tuple |
|
||||
| statements_test.py:172:5:172:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 6. | statements_test.py:169:16:169:26 | statements_test.py:169 | tuple |
|
||||
| statements_test.py:169:5:169:23 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:169:13:169:23 | statements_test.py:169 | list |
|
||||
| statements_test.py:178:5:178:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:173:16:173:24 | statements_test.py:173 | tuple |
|
||||
| statements_test.py:178:5:178:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 6. | statements_test.py:175:16:175:26 | statements_test.py:175 | tuple |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| test.py:50:1:50:23 | For | $@ of class '$@' may be used in for-loop. | test.py:50:10:50:22 | ControlFlowNode for NonIterator() | Non-iterator | test.py:45:1:45:26 | class NonIterator | NonIterator |
|
||||
| test.py:50:1:50:23 | For | $@ of class '$@' may be used in for-loop. | test.py:50:10:50:22 | ControlFlowNode for NonIterator() | Non-iterable | test.py:45:1:45:26 | class NonIterator | NonIterator |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user