Merge branch 'master' of github.com:github/codeql into UnmatchableDollar

to make CodeScan happy
This commit is contained in:
Rasmus Lerchedahl Petersen
2020-06-24 11:04:07 +02:00
1314 changed files with 43671 additions and 20522 deletions

View File

@@ -31,8 +31,8 @@ predicate calls_super(FunctionObject f) {
)
}
/** Holds if the given name is white-listed for some reason */
predicate whitelisted(string name) {
/** Holds if the given name is allowed for some reason */
predicate allowed(string name) {
/*
* The standard library specifically recommends this :(
* See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins
@@ -53,7 +53,7 @@ where
not name.matches("\\_\\_%\\_\\_") and
not calls_super(o1) and
not does_nothing(o2) and
not whitelisted(name) and
not allowed(name) and
not o1.overrides(o2) and
not o2.overrides(o1) and
not c.declaresAttribute(name)

View File

@@ -14,21 +14,21 @@ import python
import semmle.python.SelfAttribute
import Equality
predicate class_stores_to_attribute(ClassObject cls, SelfAttributeStore store, string name) {
exists(FunctionObject f |
f = cls.declaredAttribute(_) and store.getScope() = f.getFunction() and store.getName() = name
predicate class_stores_to_attribute(ClassValue cls, SelfAttributeStore store, string name) {
exists(FunctionValue f |
f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name
) and
/* Exclude classes used as metaclasses */
not cls.getASuperType() = theTypeType()
not cls.getASuperType() = ClassValue::type()
}
predicate should_override_eq(ClassObject cls, Object base_eq) {
predicate should_override_eq(ClassValue cls, Value base_eq) {
not cls.declaresAttribute("__eq__") and
exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
not exists(GenericEqMethod eq | eq.getScope() = sup.getPyClass()) and
not exists(IdentityEqMethod eq | eq.getScope() = sup.getPyClass()) and
not base_eq.(FunctionObject).getFunction() instanceof IdentityEqMethod and
not base_eq = theObjectType().declaredAttribute("__eq__")
exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and
not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and
not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and
not base_eq = ClassValue::object().declaredAttribute("__eq__")
)
}
@@ -36,16 +36,16 @@ predicate should_override_eq(ClassObject cls, Object base_eq) {
* Does the non-overridden __eq__ method access the attribute,
* which implies that the __eq__ method does not need to be overridden.
*/
predicate superclassEqExpectsAttribute(ClassObject cls, PyFunctionObject base_eq, string attrname) {
predicate superclassEqExpectsAttribute(ClassValue cls, FunctionValue base_eq, string attrname) {
not cls.declaresAttribute("__eq__") and
exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
exists(SelfAttributeRead store | store.getName() = attrname |
store.getScope() = base_eq.getFunction()
store.getScope() = base_eq.getScope()
)
)
}
from ClassObject cls, SelfAttributeStore store, Object base_eq
from ClassValue cls, SelfAttributeStore store, Value base_eq
where
class_stores_to_attribute(cls, store, _) and
should_override_eq(cls, base_eq) and

View File

@@ -14,8 +14,8 @@
import python
from ClassObject c
where not c.isC() and not c.isContextManager() and exists(c.declaredAttribute("__del__"))
from ClassValue c
where not c.isBuiltin() and not c.isContextManager() and exists(c.declaredAttribute("__del__"))
select c,
"Class " + c.getName() +
" implements __del__ (presumably to release some resource). Consider making it a context manager."

View File

@@ -4,8 +4,8 @@
* the arguments with which it is called, and if it were called, would be likely to cause an error.
* @kind problem
* @tags maintainability
* @problem.severity error
* @sub-severity low
* @problem.severity recommendation
* @sub-severity high
* @precision high
* @id py/inheritance/incorrect-overridden-signature
*/

View File

@@ -20,7 +20,7 @@ where
count(int line |
exists(DuplicateBlock d | d.sourceFile() = f |
line in [d.sourceStartLine() .. d.sourceEndLine()] and
not whitelistedLineForDuplication(f, line)
not allowlistedLineForDuplication(f, line)
)
)
select f, n order by n desc

View File

@@ -20,7 +20,7 @@ where
count(int line |
exists(SimilarBlock d | d.sourceFile() = f |
line in [d.sourceStartLine() .. d.sourceEndLine()] and
not whitelistedLineForDuplication(f, line)
not allowlistedLineForDuplication(f, line)
)
)
select f, n order by n desc

View File

@@ -68,7 +68,7 @@
<p>
The second two examples show safe checks.
In <code>safe1</code>, a white-list is used. Although fairly inflexible,
In <code>safe1</code>, an allowlist is used. Although fairly inflexible,
this is easy to get right and is most likely to be safe.
</p>
<p>

View File

@@ -21,16 +21,16 @@ def unsafe2(request):
#Simplest and safest approach is to use a white-list
#Simplest and safest approach is to use an allowlist
@app.route('/some/path/good1')
def safe1(request):
whitelist = [
allowlist = [
"example.com/home",
"example.com/login",
]
target = request.args.get('target', '')
if target in whitelist:
if target in allowlist:
return redirect(target)
#More complex example allowing sub-domains.

View File

@@ -26,7 +26,7 @@ Ideally, follow these rules:
<li>Do not allow directory separators such as "/" or "\" (depending on the file system).</li>
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after
applying this filter to ".../...//", the resulting string would still be "../".</li>
<li>Use a whitelist of known good patterns.</li>
<li>Use an allowlist of known good patterns.</li>
</ul>
</recommendation>

View File

@@ -13,7 +13,7 @@
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
/** A TaintKind to represent open tarfile objects. That is, the result of calling `tarfile.open(...)` */

View File

@@ -25,7 +25,7 @@ safe before using it.</p>
<p>The following example shows two functions. The first is unsafe as it takes a shell script that can be changed
by a user, and passes it straight to <code>subprocess.call()</code> without examining it first.
The second is safe as it selects the command from a predefined white-list.</p>
The second is safe as it selects the command from a predefined allowlist.</p>
<sample src="examples/command_injection.py" />

View File

@@ -32,6 +32,8 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration {
override predicate isExtension(TaintTracking::Extension extension) {
extension instanceof FirstElementFlow
or
extension instanceof FabricExecuteExtension
}
}

View File

@@ -19,5 +19,5 @@ def command_execution_unsafe(request):
def command_execution_safe(request):
if request.method == 'POST':
action = request.POST.get('action', '')
#GOOD -- Use a whitelist
#GOOD -- Use an allowlist
subprocess.call(["application", COMMANDS[action]])

View File

@@ -14,7 +14,7 @@
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.security.ClearText

View File

@@ -14,7 +14,7 @@
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.security.ClearText

View File

@@ -13,7 +13,7 @@
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.filters.Tests
class HardcodedValue extends TaintKind {

View File

@@ -16,7 +16,7 @@ import python
import Shadowing
import semmle.python.types.Builtins
predicate white_list(string name) {
predicate allow_list(string name) {
/* These are rarely used and thus unlikely to be confusing */
name = "iter" or
name = "next" or
@@ -51,7 +51,7 @@ predicate shadows(Name d, string name, Function scope, int line) {
) and
d.getScope() = scope and
d.getLocation().getStartLine() = line and
not white_list(name) and
not allow_list(name) and
not optimizing_parameter(d)
}

View File

@@ -1,6 +1,6 @@
import python
import Loop
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
/** Marker for "uninitialized". */
class Uninitialized extends TaintKind {

View File

@@ -33,12 +33,27 @@ predicate mutates_globals(ModuleValue m) {
exists(SubscriptNode sub | sub.getObject() = globals and sub.isStore())
)
or
exists(Value enum_convert, ClassValue enum_class |
// Enum (added in 3.4) has method `_convert_` that alters globals
// This was called `_convert` until 3.8, but that name will be removed in 3.9
exists(ClassValue enum_class |
enum_class.getASuperType() = Value::named("enum.Enum") and
enum_convert = enum_class.attr("_convert") and
exists(CallNode call | call.getScope() = m.getScope() |
enum_convert.getACall() = call or
call.getFunction().pointsTo(enum_convert)
(
// In Python < 3.8, Enum._convert can be found with points-to
exists(Value enum_convert |
enum_convert = enum_class.attr("_convert") and
exists(CallNode call | call.getScope() = m.getScope() |
enum_convert.getACall() = call or
call.getFunction().pointsTo(enum_convert)
)
)
or
// In Python 3.8, Enum._convert_ is implemented using a metaclass, and our points-to
// analysis doesn't handle that well enough. So we need a special case for this
not exists(Value enum_convert | enum_convert = enum_class.attr("_convert")) and
exists(CallNode call | call.getScope() = m.getScope() |
call.getFunction().(AttrNode).getObject(["_convert", "_convert_"]).pointsTo() =
enum_class
)
)
)
}
@@ -59,7 +74,7 @@ predicate contains_unknown_import_star(ModuleValue m) {
from ModuleValue m, StrConst name, string exported_name
where
declaredInAll(m.getScope(), name) and
exported_name = name.strValue() and
exported_name = name.getText() and
not m.hasAttribute(exported_name) and
not is_exported_submodule_name(m, exported_name) and
not contains_unknown_import_star(m) and

View File

@@ -1,7 +1,7 @@
/**
* @name Sanity check
* @description General sanity check to be run on any and all code. Should never produce any results.
* @id py/sanity-check
* @name Consistency check
* @description General consistency check to be run on any and all code. Should never produce any results.
* @id py/consistency-check
*/
import python
@@ -24,7 +24,7 @@ predicate uniqueness_error(int number, string what, string problem) {
)
}
predicate ast_sanity(string clsname, string problem, string what) {
predicate ast_consistency(string clsname, string problem, string what) {
exists(AstNode a | clsname = a.getAQlClass() |
uniqueness_error(count(a.toString()), "toString", problem) and
what = "at " + a.getLocation().toString()
@@ -39,7 +39,7 @@ predicate ast_sanity(string clsname, string problem, string what) {
)
}
predicate location_sanity(string clsname, string problem, string what) {
predicate location_consistency(string clsname, string problem, string what) {
exists(Location l | clsname = l.getAQlClass() |
uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString()
or
@@ -65,7 +65,7 @@ predicate location_sanity(string clsname, string problem, string what) {
)
}
predicate cfg_sanity(string clsname, string problem, string what) {
predicate cfg_consistency(string clsname, string problem, string what) {
exists(ControlFlowNode f | clsname = f.getAQlClass() |
uniqueness_error(count(f.getNode()), "getNode", problem) and
what = "at " + f.getLocation().toString()
@@ -80,7 +80,7 @@ predicate cfg_sanity(string clsname, string problem, string what) {
)
}
predicate scope_sanity(string clsname, string problem, string what) {
predicate scope_consistency(string clsname, string problem, string what) {
exists(Scope s | clsname = s.getAQlClass() |
uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and
what = "at " + s.getLocation().toString()
@@ -125,7 +125,7 @@ private predicate introspected_builtin_object(Object o) {
py_cobject_sources(o, 0)
}
predicate builtin_object_sanity(string clsname, string problem, string what) {
predicate builtin_object_consistency(string clsname, string problem, string what) {
exists(Object o |
clsname = o.getAQlClass() and
what = best_description_builtin_object(o) and
@@ -146,7 +146,7 @@ predicate builtin_object_sanity(string clsname, string problem, string what) {
)
}
predicate source_object_sanity(string clsname, string problem, string what) {
predicate source_object_consistency(string clsname, string problem, string what) {
exists(Object o | clsname = o.getAQlClass() and not o.isBuiltin() |
uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and
what = "at " + o.getOrigin().getLocation().toString()
@@ -161,7 +161,7 @@ predicate source_object_sanity(string clsname, string problem, string what) {
)
}
predicate ssa_sanity(string clsname, string problem, string what) {
predicate ssa_consistency(string clsname, string problem, string what) {
/* Zero or one definitions of each SSA variable */
exists(SsaVariable var | clsname = var.getAQlClass() |
uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and
@@ -196,7 +196,7 @@ predicate ssa_sanity(string clsname, string problem, string what) {
)
}
predicate function_object_sanity(string clsname, string problem, string what) {
predicate function_object_consistency(string clsname, string problem, string what) {
exists(FunctionObject func | clsname = func.getAQlClass() |
what = func.getName() and
(
@@ -229,7 +229,7 @@ predicate intermediate_origins(ControlFlowNode use, ControlFlowNode inter, Objec
)
}
predicate points_to_sanity(string clsname, string problem, string what) {
predicate points_to_consistency(string clsname, string problem, string what) {
exists(Object obj |
multiple_origins_per_object(obj) and
clsname = obj.getAQlClass() and
@@ -245,7 +245,7 @@ predicate points_to_sanity(string clsname, string problem, string what) {
)
}
predicate jump_to_definition_sanity(string clsname, string problem, string what) {
predicate jump_to_definition_consistency(string clsname, string problem, string what) {
problem = "multiple (jump-to) definitions" and
exists(Expr use |
strictcount(getUniqueDefinition(use)) > 1 and
@@ -254,7 +254,7 @@ predicate jump_to_definition_sanity(string clsname, string problem, string what)
)
}
predicate file_sanity(string clsname, string problem, string what) {
predicate file_consistency(string clsname, string problem, string what) {
exists(File file, Folder folder |
clsname = file.getAQlClass() and
problem = "has same name as a folder" and
@@ -269,7 +269,7 @@ predicate file_sanity(string clsname, string problem, string what) {
)
}
predicate class_value_sanity(string clsname, string problem, string what) {
predicate class_value_consistency(string clsname, string problem, string what) {
exists(ClassValue value, ClassValue sup, string attr |
what = value.getName() and
sup = value.getASuperType() and
@@ -283,16 +283,16 @@ predicate class_value_sanity(string clsname, string problem, string what) {
from string clsname, string problem, string what
where
ast_sanity(clsname, problem, what) or
location_sanity(clsname, problem, what) or
scope_sanity(clsname, problem, what) or
cfg_sanity(clsname, problem, what) or
ssa_sanity(clsname, problem, what) or
builtin_object_sanity(clsname, problem, what) or
source_object_sanity(clsname, problem, what) or
function_object_sanity(clsname, problem, what) or
points_to_sanity(clsname, problem, what) or
jump_to_definition_sanity(clsname, problem, what) or
file_sanity(clsname, problem, what) or
class_value_sanity(clsname, problem, what)
ast_consistency(clsname, problem, what) or
location_consistency(clsname, problem, what) or
scope_consistency(clsname, problem, what) or
cfg_consistency(clsname, problem, what) or
ssa_consistency(clsname, problem, what) or
builtin_object_consistency(clsname, problem, what) or
source_object_consistency(clsname, problem, what) or
function_object_consistency(clsname, problem, what) or
points_to_consistency(clsname, problem, what) or
jump_to_definition_consistency(clsname, problem, what) or
file_consistency(clsname, problem, what) or
class_value_consistency(clsname, problem, what)
select clsname + " " + what + " has " + problem

View File

@@ -7,7 +7,7 @@ import DefinitionTracking
predicate want_to_have_definition(Expr e) {
/* not builtin object like len, tuple, etc. */
not exists(Object cobj | e.refersTo(cobj) and cobj.isC()) and
not exists(Value builtin | e.pointsTo(builtin) and builtin.isBuiltin()) and
(
e instanceof Name and e.(Name).getCtx() instanceof Load
or

View File

@@ -0,0 +1,4 @@
- description: Security-and-quality queries for Python
- qlpack: codeql-python
- apply: security-and-quality-selectors.yml
from: codeql-suite-helpers

View File

@@ -0,0 +1,4 @@
- description: Security-extended queries for Python
- qlpack: codeql-python
- apply: security-extended-selectors.yml
from: codeql-suite-helpers

View File

@@ -268,6 +268,6 @@ predicate similarScopes(Scope s, Scope other, float percent, string message) {
* Holds if the line is acceptable as a duplicate.
* This is true for blocks of import statements.
*/
predicate whitelistedLineForDuplication(File f, int line) {
predicate allowlistedLineForDuplication(File f, int line) {
exists(ImportingStmt i | i.getLocation().getFile() = f and i.getLocation().getStartLine() = line)
}

View File

@@ -597,7 +597,7 @@ class StrConst extends Str_, ImmutableLiteral {
this.getEnclosingModule().hasFromFuture("unicode_literals")
}
override string strValue() { result = this.getS() }
deprecated override string strValue() { result = this.getS() }
override Expr getASubExpression() { none() }

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
private import semmle.python.objects.ObjectInternal
private import semmle.python.dataflow.Implementation

View File

@@ -1 +1 @@
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
class OpenFile extends TaintKind {
OpenFile() { this = "file.open" }

View File

@@ -1,4 +1,4 @@
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
private import semmle.python.objects.ObjectInternal
import semmle.python.dataflow.Implementation

View File

@@ -27,8 +27,10 @@ abstract class CallableObjectInternal extends ObjectInternal {
none()
}
/** Gets the `n`th parameter node of this callable. */
abstract NameNode getParameter(int n);
/** Gets the `name`d parameter node of this callable. */
abstract NameNode getParameterByName(string name);
abstract predicate neverReturns();
@@ -438,16 +440,30 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
PointsTo::pointsTo(result.getFunction(), ctx, this, _)
}
override NameNode getParameter(int n) { result = this.getFunction().getParameter(n + 1) }
/** Gets the parameter node that will be used for `self`. */
NameNode getSelfParameter() { result = this.getFunction().getParameter(0) }
override NameNode getParameter(int n) {
result = this.getFunction().getParameter(n + 1) and
// don't return the parameter for `self` at `n = -1`
n >= 0
}
/**
* Gets the `name`d parameter node of this callable.
* Will not return the parameter node for `self`, instead use `getSelfParameter`.
*/
override NameNode getParameterByName(string name) {
result = this.getFunction().getParameterByName(name)
result = this.getFunction().getParameterByName(name) and
not result = this.getSelfParameter()
}
override predicate neverReturns() { this.getFunction().neverReturns() }
override predicate functionAndOffset(CallableObjectInternal function, int offset) {
function = this.getFunction() and offset = 1
or
function = this and offset = 0
}
override predicate useOriginAsLegacyObject() { any() }

View File

@@ -352,7 +352,29 @@ class CallableValue extends Value {
result = this.(CallableObjectInternal).getParameterByName(name)
}
/** Gets the argument corresponding to the `n'th parameter node of this callable. */
/**
* Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable.
*
* Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as
* a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable.
*
* This method also gives results when the argument is passed as a keyword argument in `call`, as long
* as `this` is not a builtin function or a builtin method.
*
* Examples:
*
* - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents
* `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`.
*
* - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give
* the `ControlFlowNode` for `10`.
*
* - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call`
* represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the
* `ControlFlowNode` for `10`.
* Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)`
* will give the `ControlFlowNode` for `10` (notice the shift in index used).
*/
cached
ControlFlowNode getArgumentForCall(CallNode call, int n) {
exists(ObjectInternal called, int offset |
@@ -363,7 +385,7 @@ class CallableValue extends Value {
or
exists(string name |
call.getArgByName(name) = result and
this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name
this.getParameter(n).getId() = name
)
or
called instanceof BoundMethodObjectInternal and
@@ -373,21 +395,37 @@ class CallableValue extends Value {
)
}
/** Gets the argument corresponding to the `name`d parameter node of this callable. */
/**
* Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable.
*
* This method also gives results when the argument is passed as a positional argument in `call`, as long
* as `this` is not a builtin function or a builtin method.
*
* Examples:
*
* - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents
* `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`.
*
* - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give
* the `ControlFlowNode` for `10`.
*
* - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call`
* represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the
* `ControlFlowNode` for `10`.
*/
cached
ControlFlowNode getNamedArgumentForCall(CallNode call, string name) {
exists(CallableObjectInternal called, int offset |
PointsToInternal::pointsTo(call.getFunction(), _, called, _) and
called.functionAndOffset(this, offset)
|
call.getArgByName(name) = result
or
exists(int n |
call.getArg(n) = result and
this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name
this.getParameter(n + offset).getId() = name
)
or
call.getArgByName(name) = result and
exists(this.(PythonFunctionObjectInternal).getScope().getArgByName(name))
or
called instanceof BoundMethodObjectInternal and
offset = 1 and
name = "self" and
@@ -396,6 +434,29 @@ class CallableValue extends Value {
}
}
/**
* Class representing bound-methods, such as `o.func`, where `o` is an instance
* of a class that has a callable attribute `func`.
*/
class BoundMethodValue extends CallableValue {
BoundMethodValue() { this instanceof BoundMethodObjectInternal }
/**
* Gets the callable that will be used when `this` is called.
* The actual callable for `func` in `o.func`.
*/
CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() }
/**
* Gets the value that will be used for the `self` parameter when `this` is called.
* The value for `o` in `o.func`.
*/
Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() }
/** Gets the parameter node that will be used for `self`. */
NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() }
}
/**
* Class representing classes in the Python program, both Python and built-in.
*/
@@ -456,7 +517,14 @@ class ClassValue extends Value {
/** 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. */
/**
* Holds if this class is a sequence. Mutually exclusive with `isMapping()`.
*
* Following the definition from
* https://docs.python.org/3/glossary.html#term-sequence.
* We don't look at the keys accepted by `__getitem__, but default to treating a class
* as a sequence (so might treat some mappings as sequences).
*/
predicate isSequence() {
/*
* To determine whether something is a sequence or a mapping is not entirely clear,
@@ -477,16 +545,26 @@ class ClassValue extends Value {
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__")
)
this.hasAttribute("__getitem__") and
this.hasAttribute("__len__") and
not this.getASuperType() = ClassValue::dict() and
not this.getASuperType() = Value::named("collections.Mapping") and
not this.getASuperType() = Value::named("collections.abc.Mapping")
}
/** Holds if this class is a mapping. */
/**
* Holds if this class is a mapping. Mutually exclusive with `isSequence()`.
*
* Although a class will satisfy the requirement by the definition in
* https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys
* accepted by `__getitem__, but default to treating a class as a sequence (so might
* treat some mappings as sequences).
*/
predicate isMapping() {
major_version() = 2 and this.getASuperType() = Value::named("collections.Mapping")
or
major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Mapping")
or
this.hasAttribute("__getitem__") and
not this.isSequence()
}
@@ -571,6 +649,10 @@ class ClassValue extends Value {
* Note that this does not include other callables such as bound-methods.
*/
abstract class FunctionValue extends CallableValue {
/**
* Gets the qualified name for this function.
* Should return the same name as the `__qualname__` attribute on functions in Python 3.
*/
abstract string getQualifiedName();
/** Gets a longer, more descriptive version of toString() */
@@ -663,11 +745,13 @@ class PythonFunctionValue extends FunctionValue {
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
override ClassValue getAnInferredReturnType() {
/* We have to do a special version of this because builtin functions have no
/*
* We have to do a special version of this because builtin functions have no
* explicit return nodes that we can query and get the class of.
*/
result = this.getAReturnedNode().pointsTo().getClass()
}
}
@@ -690,9 +774,11 @@ class BuiltinFunctionValue extends FunctionValue {
}
override ClassValue getAnInferredReturnType() {
/* We have to do a special version of this because builtin functions have no
/*
* We have to do a special version of this because builtin functions have no
* explicit return nodes that we can query and get the class of.
*/
result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType())
}
}
@@ -719,7 +805,7 @@ class BuiltinMethodValue extends FunctionValue {
/* Information is unavailable for C code in general */
none()
}
override ClassValue getAnInferredReturnType() {
result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType())
}

View File

@@ -28,7 +28,8 @@ predicate used_as_regex(Expr s, string mode) {
/* 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))
call.getFunction().pointsTo(Module::named("re").attr(name)) and
not name = "escape"
|
mode = "None"
or
@@ -124,16 +125,40 @@ abstract class RegexString extends Expr {
)
}
/** Named unicode characters, eg \N{degree sign} */
private predicate escapedName(int start, int end) {
this.escapingChar(start) and
this.getChar(start + 1) = "N" and
this.getChar(start + 2) = "{" and
this.getChar(end - 1) = "}" and
end > start and
not exists(int i | start + 2 < i and i < end - 1 |
this.getChar(i) = "}"
)
}
private predicate escapedCharacter(int start, int end) {
this.escapingChar(start) and
not exists(this.getText().substring(start + 1, end + 1).toInt()) and
(
// hex value \xhh
this.getChar(start + 1) = "x" and end = start + 4
or
// octal value \ooo
end in [start + 2 .. start + 4] and
exists(this.getText().substring(start + 1, end).toInt())
or
this.getChar(start + 1) != "x" and end = start + 2
// 16-bit hex value \uhhhh
this.getChar(start + 1) = "u" and end = start + 6
or
// 32-bit hex value \Uhhhhhhhh
this.getChar(start + 1) = "U" and end = start + 10
or
escapedName(start, end)
or
// escape not handled above, update when adding a new case
not this.getChar(start + 1) in ["x", "u", "U", "N"] and
end = start + 2
)
}

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.dataflow.Files
import semmle.python.web.Http

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
private import semmle.python.security.SensitiveData
private import semmle.crypto.Crypto as CryptoLib

View File

@@ -4,7 +4,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
private Value traceback_function(string name) { result = Module::named("traceback").attr(name) }

View File

@@ -1,4 +1,4 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
abstract class SqlInjectionSink extends TaintSink { }

View File

@@ -10,7 +10,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.HttpRequest
/**

View File

@@ -7,7 +7,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
/** Abstract taint sink that is potentially vulnerable to malicious shell commands. */
@@ -231,3 +231,41 @@ class FabricV1Commands extends CommandSink {
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* An extension that propagates taint from the arguments of `fabric.api.execute(func, arg0, arg1, ...)`
* to the parameters of `func`, since this will call `func(arg0, arg1, ...)`.
*/
class FabricExecuteExtension extends DataFlowExtension::DataFlowNode {
CallNode call;
FabricExecuteExtension() {
call = Value::named("fabric.api.execute").getACall() and
(
this = call.getArg(any(int i | i > 0))
or
this = call.getArgByName(any(string s | not s = "task"))
)
}
override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) {
tokind = fromkind and
exists(CallableValue func |
(
call.getArg(0).pointsTo(func)
or
call.getArgByName("task").pointsTo(func)
) and
exists(int i |
// execute(func, arg0, arg1) => func(arg0, arg1)
this = call.getArg(i) and
result = func.getParameter(i - 1)
)
or
exists(string name |
this = call.getArgByName(name) and
result = func.getParameterByName(name)
)
)
}
}

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
/** `pickle.loads(untrusted)` vulnerability. */
abstract class DeserializationSink extends TaintSink {

View File

@@ -7,7 +7,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
/**

View File

@@ -7,7 +7,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.injection.Deserialization

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
/**

View File

@@ -7,7 +7,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.injection.Deserialization

View File

@@ -7,7 +7,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.SQL

View File

@@ -7,7 +7,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.injection.Deserialization

View File

@@ -7,7 +7,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.injection.Deserialization

View File

@@ -1,6 +1,6 @@
import python
private import Common
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
/** An extensible kind of taint representing any kind of string. */
abstract class StringKind extends TaintKind {
@@ -107,7 +107,11 @@ private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
tonode.getAnArg() = fromnode
}
/** A kind of "taint", representing a dictionary mapping str->"taint" */
class StringDictKind extends DictKind {
/**
* A kind of "taint", representing a dictionary mapping str->"taint"
*
* DEPRECATED: Use `ExternalStringDictKind` instead.
*/
deprecated class StringDictKind extends DictKind {
StringDictKind() { this.getValue() instanceof StringKind }
}

View File

@@ -60,14 +60,14 @@ class ExternalJsonKind extends TaintKind {
}
}
/** A kind of "taint", representing a dictionary mapping str->"taint" */
/** A kind of "taint", representing a dictionary mapping keys to tainted strings. */
class ExternalStringDictKind extends DictKind {
ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind }
}
/**
* A kind of "taint", representing a dictionary mapping strings to sequences of
* tainted strings
* A kind of "taint", representing a dictionary mapping keys to sequences of
* tainted strings.
*/
class ExternalStringSequenceDictKind extends DictKind {
ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind }

View File

@@ -118,7 +118,7 @@ class BuiltinModuleObject extends ModuleObject {
override predicate hasAttribute(string name) { exists(this.asBuiltin().getMember(name)) }
override predicate exportsComplete() { any() }
deprecated override predicate exportsComplete() { any() }
}
class PythonModuleObject extends ModuleObject {
@@ -132,7 +132,7 @@ class PythonModuleObject extends ModuleObject {
override Container getPath() { result = this.getModule().getFile() }
override predicate exportsComplete() {
deprecated override predicate exportsComplete() {
exists(Module m | m = this.getModule() |
not exists(Call modify, Attribute attr, GlobalVariable all |
modify.getScope() = m and
@@ -196,7 +196,7 @@ class PackageObject extends ModuleObject {
)
}
override predicate exportsComplete() {
deprecated override predicate exportsComplete() {
not exists(this.getInitModule())
or
this.getInitModule().exportsComplete()

View File

@@ -5,7 +5,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.bottle.General

View File

@@ -1,6 +1,6 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.External
import semmle.python.web.Http
import semmle.python.web.bottle.General
@@ -13,7 +13,7 @@ class BottleRequestKind extends TaintKind {
result instanceof BottleFormsDict and
(name = "cookies" or name = "query" or name = "form")
or
result instanceof UntrustedStringKind and
result instanceof ExternalStringKind and
(name = "query_string" or name = "url_args")
or
result.(DictKind).getValue() instanceof FileUpload and
@@ -34,7 +34,7 @@ class BottleFormsDict extends TaintKind {
/* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */
exists(string name |
fromnode = tonode.(AttrNode).getObject(name) and
result instanceof UntrustedStringKind
result instanceof ExternalStringKind
|
name != "get" and name != "getunicode" and name != "getall"
)
@@ -42,9 +42,9 @@ class BottleFormsDict extends TaintKind {
override TaintKind getTaintOfMethodResult(string name) {
(name = "get" or name = "getunicode") and
result instanceof UntrustedStringKind
result instanceof ExternalStringKind
or
name = "getall" and result.(SequenceKind).getItem() instanceof UntrustedStringKind
name = "getall" and result.(SequenceKind).getItem() instanceof ExternalStringKind
}
}
@@ -52,9 +52,9 @@ class FileUpload extends TaintKind {
FileUpload() { this = "bottle.FileUpload" }
override TaintKind getTaintOfAttribute(string name) {
name = "filename" and result instanceof UntrustedStringKind
name = "filename" and result instanceof ExternalStringKind
or
name = "raw_filename" and result instanceof UntrustedStringKind
name = "raw_filename" and result instanceof ExternalStringKind
or
name = "file" and result instanceof UntrustedFile
}
@@ -74,7 +74,7 @@ class BottleRequestParameter extends HttpRequestTaintSource {
exists(BottleRoute route | route.getANamedArgument() = this.(ControlFlowNode).getNode())
}
override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind }
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
override string toString() { result = "bottle handler function argument" }
}

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.web.Http
import semmle.python.web.bottle.General

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.Http
import semmle.python.web.cherrypy.General

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.web.Http
import semmle.python.web.cherrypy.General

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.Http
import semmle.python.security.injection.Sql

View File

@@ -5,7 +5,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
private import semmle.python.web.django.Shared
private import semmle.python.web.Http

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
import semmle.python.web.django.General

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
private import semmle.python.web.django.Shared
private import semmle.python.web.Http

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
import semmle.python.web.falcon.General
import semmle.python.security.strings.External

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
import semmle.python.web.falcon.General
import semmle.python.security.strings.External

View File

@@ -5,7 +5,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.flask.General

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
import semmle.python.web.flask.General
@@ -54,3 +54,35 @@ class FlaskRequestJson extends HttpRequestTaintSource {
override string toString() { result = "flask.request.json" }
}
/**
* A parameter to a flask request handler, that can capture a part of the URL (as specified in
* the url-pattern of a route).
*
* For example, the `name` parameter in:
* ```
* @app.route('/hello/<name>')
* def hello(name):
* ```
*/
class FlaskRoutedParameter extends HttpRequestTaintSource {
FlaskRoutedParameter() {
exists(string name, Function func, StrConst url_pattern |
this.(ControlFlowNode).getNode() = func.getArgByName(name) and
flask_routing(url_pattern.getAFlowNode(), func) and
exists(string match |
match = url_pattern.getS().regexpFind(werkzeug_rule_re(), _, _) and
name = match.regexpCapture(werkzeug_rule_re(), 4)
)
)
}
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
}
private string werkzeug_rule_re() {
// since flask uses werkzeug internally, we are using its routing rules from
// https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151
result =
"(?<static>[^<]*)<(?:(?<converter>[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?<args>.*?)\\))?\\:)?(?<variable>[a-zA-Z_][a-zA-Z0-9_]*)>"
}

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.flask.General

View File

@@ -5,7 +5,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.Http

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
private import semmle.python.web.webob.Request
private import semmle.python.web.pyramid.View

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.Http
private import semmle.python.web.pyramid.View

View File

@@ -4,7 +4,7 @@
* (or subclasses) and form parsing using `cgi.FieldStorage`.
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
/** Source of BaseHTTPRequestHandler instances. */

View File

@@ -3,7 +3,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
private predicate is_wfile(AttrNode wfile) {

View File

@@ -5,7 +5,7 @@
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.Http
import Tornado

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
import Tornado

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
private import semmle.python.web.Http
import Tornado

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
private ClassValue theTornadoRequestHandlerClass() {

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.strings.Untrusted
import semmle.python.security.strings.External
import semmle.python.web.Http
import TurboGears
@@ -22,5 +22,5 @@ class UnvalidatedControllerMethodParameter extends HttpRequestTaintSource {
)
}
override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind }
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.Http
import TurboGears
@@ -27,5 +27,5 @@ class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink {
)
}
override predicate sinks(TaintKind kind) { kind instanceof StringDictKind }
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind }
}

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
private ClassValue theTurboGearsControllerClass() { result = Value::named("tg.TGController") }

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
import Twisted

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
import semmle.python.security.strings.Basic
import Twisted

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
private ClassValue theTwistedHttpRequestClass() {
result = Value::named("twisted.web.http.Request")

View File

@@ -1,5 +1,5 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.TaintTracking
import semmle.python.web.Http
abstract class BaseWebobRequest extends TaintKind {

View File

@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
XMLFile() { xmlEncoding(this, _) }
/** Gets a printable representation of this XML file. */
override string toString() { result = XMLParent.super.toString() }
override string toString() { result = getName() }
/** Gets the name of this XML file. */
override string getName() { result = File.super.getAbsolutePath() }
@@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
/** Gets a printable representation of this XML element. */
override string toString() { result = XMLParent.super.toString() }
override string toString() { result = getName() }
}
/**