Merge branch 'master' into python-keyword-only-args

This commit is contained in:
Rasmus Wriedt Larsen
2020-07-02 12:27:02 +02:00
1367 changed files with 60109 additions and 22507 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,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

@@ -1,3 +1,5 @@
/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */
import python
import Testing.Mox
@@ -71,36 +73,38 @@ private int positional_arg_count_for_call(Call call, Value callable) {
)
}
/** Gets the number of arguments in `call`. */
int arg_count_objectapi(Call call) {
result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword())
}
/** Gets the number of arguments in `call`. */
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*/
/** Gets a call corresponding to the given class or function. */
private ControlFlowNode get_a_call_objectapi(Object callable) {
result = callable.(ClassObject).getACall()
or
result = callable.(FunctionObject).getACall()
}
/* Gets a call corresponding to the given class or function*/
/** 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*/
/** 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)
or
result = func_or_cls.(ClassObject).declaredAttribute("__init__")
}
/* Gets the function object corresponding to the given class or function*/
/** 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

View File

@@ -1,5 +1,6 @@
import python
/** A string constant that looks like it may be used in string formatting operations. */
library class PossibleAdvancedFormatString extends StrConst {
PossibleAdvancedFormatString() { this.getText().matches("%{%}%") }
@@ -51,6 +52,7 @@ library class PossibleAdvancedFormatString extends StrConst {
predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) }
}
/** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */
predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) {
exists(string text | text = fmt.getText() |
text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1
@@ -61,10 +63,12 @@ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) {
)
}
/** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */
predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) {
exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0)
}
/** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */
predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) {
escaped_brace(fmt, index + 1)
}
@@ -105,15 +109,18 @@ private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatS
)
}
/** A string constant that has the `format` method applied to it. */
class AdvancedFormatString extends PossibleAdvancedFormatString {
AdvancedFormatString() { advanced_format_call(_, this, _) }
}
/** A string formatting operation that uses the `format` method. */
class AdvancedFormattingCall extends Call {
AdvancedFormattingCall() { advanced_format_call(this, _, _) }
/** Count of the arguments actually provided */
int providedArgCount() { advanced_format_call(this, _, result) }
/** Gets a formatting string for this call. */
AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) }
}

View File

@@ -1,5 +1,8 @@
/** INTERNAL - Helper predicates for queries that inspect the comparison of objects using `is`. */
import python
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
fcomp.operands(left, op, right) and
@@ -7,6 +10,7 @@ predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, Cont
)
}
/** Holds if the class `c` overrides the default notion of equality or comparison. */
predicate overrides_eq_or_cmp(ClassValue c) {
major_version() = 2 and c.hasAttribute("__eq__")
or
@@ -19,12 +23,14 @@ predicate overrides_eq_or_cmp(ClassValue c) {
major_version() = 2 and c.hasAttribute("__cmp__")
}
/** Holds if the class `cls` is likely to only have a single instance throughout the program. */
predicate probablySingleton(ClassValue cls) {
strictcount(Value inst | inst.getClass() = cls) = 1
or
cls = Value::named("None").getClass()
}
/** Holds if using `is` to compare instances of the class `c` is likely to cause unexpected behavior. */
predicate invalid_to_use_is_portably(ClassValue c) {
overrides_eq_or_cmp(c) and
// Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__
@@ -35,6 +41,7 @@ predicate invalid_to_use_is_portably(ClassValue c) {
not probablySingleton(c)
}
/** Holds if the control flow node `f` points to either `True`, `False`, or `None`. */
predicate simple_constant(ControlFlowNode f) {
exists(Value val | f.pointsTo(val) |
val = Value::named("True") or val = Value::named("False") or val = Value::named("None")
@@ -66,10 +73,12 @@ private predicate universally_interned_value(Expr e) {
e.(StrConst).getText() = ""
}
/** Holds if the expression `e` points to an interned constant in CPython. */
predicate cpython_interned_constant(Expr e) {
exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const))
}
/** Holds if the expression `e` points to a value that can be reasonably expected to be interned across all implementations of Python. */
predicate universally_interned_constant(Expr e) {
exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const))
}
@@ -92,6 +101,9 @@ private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) {
)
}
/**
* Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`.
*/
predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) {
// OK to use 'is' when defining '__eq__'
not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" |

View File

@@ -1,5 +1,8 @@
/** Helper functions for queries that test redundant comparisons. */
import python
/** A comparison where the left and right hand sides appear to be identical. */
class RedundantComparison extends Compare {
RedundantComparison() {
exists(Expr left, Expr right |
@@ -8,6 +11,15 @@ class RedundantComparison extends Compare {
)
}
/** Holds if this comparison could be redundant due to a missing `self.`, for example
* ```python
* foo == foo
* ```
* instead of
* ```python
* self.foo == foo
* ```
*/
predicate maybeMissingSelf() {
exists(Name left |
this.compares(left, _, _) and

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

@@ -175,6 +175,7 @@ class CommentedOutCodeLine extends Comment {
class CommentedOutCodeBlock extends @py_comment {
CommentedOutCodeBlock() { commented_out_code_block(this, _) }
/** Gets a textual representation of this element. */
string toString() { result = "Commented out code" }
/** Whether this commented-out code block contains the comment c */
@@ -191,10 +192,19 @@ class CommentedOutCodeBlock extends @py_comment {
/** The length of this comment block (in comments) */
int length() { result = count(Comment c | this.contains(c)) }
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
this.(Comment).getLocation().hasLocationInfo(filepath, bl, bc, _, _) and
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
exists(Comment end | commented_out_code_block(this, end) |
end.getLocation().hasLocationInfo(_, _, _, el, ec)
end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn)
)
}

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

@@ -15,9 +15,17 @@ import python
* including the body (if any), as opposed to the location of its name only.
*/
class RangeFunction extends Function {
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
super.getLocation().hasLocationInfo(path, sl, sc, _, _) and
this.getBody().getLastItem().getLocation().hasLocationInfo(path, _, _, el, ec)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn)
}
}
@@ -26,8 +34,16 @@ class RangeFunction extends Function {
* including the body (if any), as opposed to the location of its name only.
*/
class RangeClass extends Class {
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
super.getLocation().hasLocationInfo(path, sl, sc, _, _) and
this.getBody().getLastItem().getLocation().hasLocationInfo(path, _, _, el, ec)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn)
}
}

View File

@@ -1,3 +1,5 @@
/** Contains predicates concerning when and where files are opened and closed. */
import python
import semmle.python.GuardedControlFlow
import semmle.python.pointsto.Filters
@@ -113,12 +115,14 @@ predicate close_method_call(CallNode call, ControlFlowNode self) {
call.getFunction().(AttrNode).getObject("close") = self
}
/** Holds if `close` is a function that appears to close files that are passed to it as an argument. */
predicate function_closes_file(FunctionValue close) {
close = Value::named("os.close")
or
function_should_close_parameter(close.getScope())
}
/** INTERNAL - Helper predicate for `function_closes_file` */
predicate function_should_close_parameter(Function func) {
exists(EssaDefinition def |
closes_file(def) and
@@ -126,6 +130,7 @@ predicate function_should_close_parameter(Function func) {
)
}
/** Holds if the function `f` opens a file, either directly or indirectly. */
predicate function_opens_file(FunctionValue f) {
f = Value::named("open")
or
@@ -140,6 +145,7 @@ predicate function_opens_file(FunctionValue f) {
)
}
/** Holds if the variable `v` refers to a file opened at `open` which is subsequently returned from a function. */
predicate file_is_returned(EssaVariable v, ControlFlowNode open) {
exists(NameNode n, Return ret |
var_is_open(v, open) and

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

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

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

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

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

@@ -10,6 +10,7 @@ private newtype TDefinition =
/** A definition for the purposes of jump-to-definition. */
class Definition extends TLocalDefinition {
/** Gets a textual representation of this element. */
string toString() { result = "Definition " + this.getAstNode().getLocation().toString() }
AstNode getAstNode() { this = TLocalDefinition(result) }
@@ -467,8 +468,15 @@ Definition getUniqueDefinition(Expr use) {
/** Helper class to get suitable locations for attributes */
class NiceLocationExpr extends @py_expr {
/** Gets a textual representation of this element. */
string toString() { result = this.(Expr).toString() }
/**
* Holds if this element is at the specified location.
* The location spans column `bc` of line `bl` to
* column `ec` of line `el` in file `f`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) {
/* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */
exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) |

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

@@ -0,0 +1,18 @@
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp>
<overview>
<p>
Processing an unvalidated XSL stylesheet can allow an attacker to change the structure and contents of the resultant XML, include arbitrary files from the file system, or execute arbitrary code.
</p>
</overview>
<recommendation>
<p>
This vulnerability can be prevented by not allowing untrusted user input to be passed as an XSL stylesheet.
If the application logic necessiates processing untrusted XSL stylesheets, the input should be properly filtered and sanitized before use.
</p>
</recommendation>
<example>
<p>In the example below, the XSL stylesheet is controlled by the user and hence leads to a vulnerability.</p>
<sample src="xslt.py" />
</example>
</qhelp>

View File

@@ -0,0 +1,35 @@
/**
* @name XSLT query built from user-controlled sources
* @description Building a XSLT query from user-controlled sources is vulnerable to insertion of
* malicious XSLT code by the user.
* @kind path-problem
* @problem.severity error
* @precision high
* @id py/xslt-injection
* @tags security
* external/cwe/cwe-643
*/
import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import experimental.semmle.python.security.injection.XSLT
class XSLTInjectionConfiguration extends TaintTracking::Configuration {
XSLTInjectionConfiguration() { this = "XSLT injection configuration" }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) {
sink instanceof XSLTInjection::XSLTInjectionSink
}
}
from XSLTInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "This XSLT query depends on $@.", src.getSource(),
"a user-provided value"

View File

@@ -0,0 +1,14 @@
from lxml import etree
from io import StringIO
from flask import Flask, request
app = Flask(__name__)
@app.route("/xslt")
def bad():
xsltQuery = request.args.get('xml', '')
xslt_root = etree.XML(xsltQuery)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result_tree = tree.xslt(xslt_root) # Not OK

View File

@@ -0,0 +1,30 @@
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp>
<overview>
<p>
Using user-supplied information to construct an XPath query for XML data can
result in an XPath injection flaw. By sending intentionally malformed information,
an attacker can access data that he may not normally have access to.
He/She may even be able to elevate his privileges on the web site if the XML data
is being used for authentication (such as an XML based user file).
</p>
</overview>
<recommendation>
<p>
XPath injection can be prevented using parameterized XPath interface or escaping the user input to make it safe to include in a dynamically constructed query.
If you are using quotes to terminate untrusted input in a dynamically constructed XPath query, then you need to escape that quote in the untrusted input to ensure the untrusted data cant try to break out of that quoted context.
</p>
<p>
Another better mitigation option is to use a precompiled XPath query. Precompiled XPath queries are already preset before the program executes, rather than created on the fly after the users input has been added to the string. This is a better route because you dont have to worry about missing a character that should have been escaped.
</p>
</recommendation>
<example>
<p>In the example below, the xpath query is controlled by the user and hence leads to a vulnerability.</p>
<sample src="xpathBad.py" />
<p> This can be fixed by using a parameterized query as shown below.</p>
<sample src="xpathGood.py" />
</example>
<references>
<li>OWASP XPath injection : <a href="https://owasp.org/www-community/attacks/XPATH_Injection"></a>/>> </li>
</references>
</qhelp>

View File

@@ -0,0 +1,35 @@
/**
* @name XPath query built from user-controlled sources
* @description Building a XPath query from user-controlled sources is vulnerable to insertion of
* malicious Xpath code by the user.
* @kind path-problem
* @problem.severity error
* @precision high
* @id py/xpath-injection
* @tags security
* external/cwe/cwe-643
*/
import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import experimental.semmle.python.security.injection.Xpath
class XpathInjectionConfiguration extends TaintTracking::Configuration {
XpathInjectionConfiguration() { this = "Xpath injection configuration" }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) {
sink instanceof XpathInjection::XpathInjectionSink
}
}
from XpathInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "This Xpath query depends on $@.", src.getSource(),
"a user-provided value"

View File

@@ -0,0 +1,18 @@
from lxml import etree
from io import StringIO
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath("/tag[@id='%s']" % value)
urlpatterns = [
path('a', a)
]

View File

@@ -0,0 +1,18 @@
from lxml import etree
from io import StringIO
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath("/tag[@id=$tagid]", tagid=value)
urlpatterns = [
path('a', a)
]

View File

@@ -0,0 +1,115 @@
/**
* Provides class and predicates to track external data that
* may represent malicious XSLT query objects.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*/
import python
import semmle.python.dataflow.TaintTracking
import semmle.python.web.HttpRequest
/** Models XSLT Injection related classes and functions */
module XSLTInjection {
/** Returns a class value which refers to `lxml.etree` */
Value etree() { result = Value::named("lxml.etree") }
/** A generic taint sink that is vulnerable to XSLT injection. */
abstract class XSLTInjectionSink extends TaintSink { }
/**
* A kind of "taint", representing an untrusted XML string
*/
private class ExternalXmlStringKind extends ExternalStringKind {
ExternalXmlStringKind() { this = "etree.XML string" }
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
etreeXML(fromnode, tonode) and result instanceof ExternalXmlKind
or
etreeFromStringList(fromnode, tonode) and result instanceof ExternalXmlKind
or
etreeFromString(fromnode, tonode) and result instanceof ExternalXmlKind
}
}
/**
* A kind of "taint", representing a XML encoded string
*/
class ExternalXmlKind extends TaintKind {
ExternalXmlKind() { this = "lxml etree xml" }
}
private predicate etreeXML(ControlFlowNode fromnode, CallNode tonode) {
// etree.XML("<xmlContent>")
exists(CallNode call | call.getFunction().(AttrNode).getObject("XML").pointsTo(etree()) |
call.getArg(0) = fromnode and
call = tonode
)
}
private predicate etreeFromString(ControlFlowNode fromnode, CallNode tonode) {
// etree.fromstring(text, parser=None)
exists(CallNode call | call.getFunction().(AttrNode).getObject("fromstring").pointsTo(etree()) |
call.getArg(0) = fromnode and
call = tonode
)
}
private predicate etreeFromStringList(ControlFlowNode fromnode, CallNode tonode) {
// etree.fromstringlist(strings, parser=None)
exists(CallNode call |
call.getFunction().(AttrNode).getObject("fromstringlist").pointsTo(etree())
|
call.getArg(0) = fromnode and
call = tonode
)
}
/**
* A Sink representing an argument to the `etree.XSLT` call.
*
* from lxml import etree
* root = etree.XML("<xmlContent>")
* find_text = etree.XSLT("`sink`")
*/
private class EtreeXSLTArgument extends XSLTInjectionSink {
override string toString() { result = "lxml.etree.XSLT" }
EtreeXSLTArgument() {
exists(CallNode call | call.getFunction().(AttrNode).getObject("XSLT").pointsTo(etree()) |
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlKind }
}
/**
* A Sink representing an argument to the `XSLT` call to a parsed xml document.
*
* from lxml import etree
* from io import StringIO
* `sink` = etree.XML(xsltQuery)
* tree = etree.parse(f)
* result_tree = tree.xslt(`sink`)
*/
private class ParseXSLTArgument extends XSLTInjectionSink {
override string toString() { result = "lxml.etree.parse.xslt" }
ParseXSLTArgument() {
exists(
CallNode parseCall, CallNode xsltCall, ControlFlowNode obj, Variable var, AssignStmt assign
|
parseCall.getFunction().(AttrNode).getObject("parse").pointsTo(etree()) and
assign.getValue().(Call).getAFlowNode() = parseCall and
xsltCall.getFunction().(AttrNode).getObject("xslt") = obj and
var.getAUse() = obj and
assign.getATarget() = var.getAStore() and
xsltCall.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlKind }
}
}

View File

@@ -0,0 +1,115 @@
/**
* Provides class and predicates to track external data that
* may represent malicious xpath query objects.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*/
import python
import semmle.python.dataflow.TaintTracking
import semmle.python.web.HttpRequest
/** Models Xpath Injection related classes and functions */
module XpathInjection {
/** Returns a class value which refers to `lxml.etree` */
Value etree() { result = Value::named("lxml.etree") }
/** Returns a class value which refers to `lxml.etree` */
Value libxml2parseFile() { result = Value::named("libxml2.parseFile") }
/** A generic taint sink that is vulnerable to Xpath injection. */
abstract class XpathInjectionSink extends TaintSink { }
/**
* A Sink representing an argument to the `etree.Xpath` call.
*
* from lxml import etree
* root = etree.XML("<xmlContent>")
* find_text = etree.XPath("`sink`")
*/
private class EtreeXpathArgument extends XpathInjectionSink {
override string toString() { result = "lxml.etree.Xpath" }
EtreeXpathArgument() {
exists(CallNode call | call.getFunction().(AttrNode).getObject("XPath").pointsTo(etree()) |
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* A Sink representing an argument to the `etree.EtXpath` call.
*
* from lxml import etree
* root = etree.XML("<xmlContent>")
* find_text = etree.EtXPath("`sink`")
*/
private class EtreeETXpathArgument extends XpathInjectionSink {
override string toString() { result = "lxml.etree.ETXpath" }
EtreeETXpathArgument() {
exists(CallNode call | call.getFunction().(AttrNode).getObject("ETXPath").pointsTo(etree()) |
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* A Sink representing an argument to the `xpath` call to a parsed xml document.
*
* from lxml import etree
* from io import StringIO
* f = StringIO('<foo><bar></bar></foo>')
* tree = etree.parse(f)
* r = tree.xpath('`sink`')
*/
private class ParseXpathArgument extends XpathInjectionSink {
override string toString() { result = "lxml.etree.parse.xpath" }
ParseXpathArgument() {
exists(
CallNode parseCall, CallNode xpathCall, ControlFlowNode obj, Variable var, AssignStmt assign
|
parseCall.getFunction().(AttrNode).getObject("parse").pointsTo(etree()) and
assign.getValue().(Call).getAFlowNode() = parseCall and
xpathCall.getFunction().(AttrNode).getObject("xpath") = obj and
var.getAUse() = obj and
assign.getATarget() = var.getAStore() and
xpathCall.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* A Sink representing an argument to the `xpathEval` call to a parsed libxml2 document.
*
* import libxml2
* tree = libxml2.parseFile("file.xml")
* r = tree.xpathEval('`sink`')
*/
private class ParseFileXpathEvalArgument extends XpathInjectionSink {
override string toString() { result = "libxml2.parseFile.xpathEval" }
ParseFileXpathEvalArgument() {
exists(
CallNode parseCall, CallNode xpathCall, ControlFlowNode obj, Variable var, AssignStmt assign
|
parseCall.getFunction().(AttrNode).pointsTo(libxml2parseFile()) and
assign.getValue().(Call).getAFlowNode() = parseCall and
xpathCall.getFunction().(AttrNode).getObject("xpathEval") = obj and
var.getAUse() = obj and
assign.getATarget() = var.getAStore() and
xpathCall.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
}

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

@@ -26,7 +26,9 @@ class DefectResult extends int {
/** Gets the file in which this query result was reported. */
File getFile() {
exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path)
exists(string path |
defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
)
}
/** Gets the file path in which this query result was reported. */
@@ -47,8 +49,17 @@ class DefectResult extends int {
/** Gets the message associated with this query result. */
string getMessage() { defectResults(this, _, _, _, _, _, _, result) }
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
defectResults(this, _, path, sl, sc, el, ec, _)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _)
}
/** Gets the URL corresponding to the location of this query result. */

View File

@@ -1,3 +1,7 @@
/**
* Provides classes for working with external data.
*/
import python
class ExternalDefect extends @externalDefect {
@@ -14,6 +18,7 @@ class ExternalDefect extends @externalDefect {
Location getLocation() { externalDefects(this, _, result, _, _) }
/** Gets a textual representation of this element. */
string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getMessage() }
}
@@ -24,26 +29,42 @@ class ExternalMetric extends @externalMetric {
Location getLocation() { externalMetrics(this, _, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() }
}
/**
* An external data item.
*/
class ExternalData extends @externalDataElement {
/** Gets the path of the file this data was loaded from. */
string getDataPath() { externalData(this, result, _, _) }
/**
* Gets the path of the file this data was loaded from, with its
* extension replaced by `.ql`.
*/
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
/** Gets the number of fields in this data item. */
int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) }
/** Gets the value of the field at position `index` of this data item. */
string getField(int index) { externalData(this, _, index, result) }
/** Gets the integer value of the field at position `index` of this data item. */
int getFieldAsInt(int index) { result = getField(index).toInt() }
/** Gets the floating-point value of the field at position `index` of this data item. */
float getFieldAsFloat(int index) { result = getField(index).toFloat() }
/** Gets the value of the field at position `index` of this data item, interpreted as a date. */
date getFieldAsDate(int index) { result = getField(index).toDate() }
/** Gets a textual representation of this data item. */
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
/** Gets a textual representation of this data item, starting with the field at position `start`. */
private string buildTupleString(int start) {
start = getNumFields() - 1 and result = getField(start)
or

View File

@@ -33,18 +33,27 @@ class ThriftElement extends ExternalData {
private int column() { result = this.getFieldAsInt(6) }
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
fp = this.getPath() and
bl = this.line() and
bc = this.column() and
el = this.line() and
ec = this.column() + this.getValue().length() - 1
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = this.getPath() and
startline = this.line() and
startcolumn = this.column() and
endline = this.line() and
endcolumn = this.column() + this.getValue().length() - 1
or
exists(ThriftElement first, ThriftElement last |
first = this.getChild(min(int l | exists(this.getChild(l)))) and
last = this.getChild(max(int l | exists(this.getChild(l)))) and
first.hasLocationInfo(fp, bl, bc, _, _) and
last.hasLocationInfo(fp, _, _, el, ec)
first.hasLocationInfo(filepath, startline, startcolumn, _, _) and
last.hasLocationInfo(filepath, _, _, endline, endcolumn)
)
}
@@ -62,11 +71,11 @@ abstract class ThriftNamedElement extends ThriftElement {
not exists(this.getName()) and result = this.getKind() + " ???"
}
override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
exists(ThriftElement first |
first = this.getChild(min(int l | exists(this.getChild(l)))) and
first.hasLocationInfo(fp, bl, bc, _, _) and
this.getNameElement().hasLocationInfo(fp, _, _, el, ec)
first.hasLocationInfo(filepath, startline, startcolumn, _, _) and
this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn)
)
}
}
@@ -142,9 +151,9 @@ class ThriftFunction extends ThriftNamedElement {
ThriftType getReturnType() { result = this.getChild(1).getChild(0) }
override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
this.getChild(1).hasLocationInfo(fp, bl, bc, _, _) and
this.getChild(2).hasLocationInfo(fp, _, _, el, ec)
override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and
this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn)
}
ThriftService getService() { result.getAFunction() = this }

View File

@@ -10,6 +10,7 @@ class Commit extends @svnentry {
)
}
/** Gets a textual representation of this element. */
string toString() { result = this.getRevisionName() }
string getRevisionName() { svnentries(this, result, _, _, _) }

View File

@@ -1,4 +1,4 @@
/*
/**
* This library file is auto-generated by 'semmle/query_gen.py'.
* WARNING: Any modifications to this file will be lost.
* Relations can be changed by modifying master.py.
@@ -6,14 +6,17 @@
import python
/** INTERNAL: See the class `Add` for further information. */
library class Add_ extends @py_Add, Operator {
override string toString() { result = "Add" }
}
/** INTERNAL: See the class `And` for further information. */
library class And_ extends @py_And, Boolop {
override string toString() { result = "And" }
}
/** INTERNAL: See the class `AnnAssign` for further information. */
library class AnnAssign_ extends @py_AnnAssign, Stmt {
/** Gets the value of this annotated assignment. */
Expr getValue() { py_exprs(result, _, this, 1) }
@@ -27,6 +30,7 @@ library class AnnAssign_ extends @py_AnnAssign, Stmt {
override string toString() { result = "AnnAssign" }
}
/** INTERNAL: See the class `Assert` for further information. */
library class Assert_ extends @py_Assert, Stmt {
/** Gets the value being tested of this assert statement. */
Expr getTest() { py_exprs(result, _, this, 1) }
@@ -37,6 +41,7 @@ library class Assert_ extends @py_Assert, Stmt {
override string toString() { result = "Assert" }
}
/** INTERNAL: See the class `Assign` for further information. */
library class Assign_ extends @py_Assign, Stmt {
/** Gets the value of this assignment statement. */
Expr getValue() { py_exprs(result, _, this, 1) }
@@ -53,6 +58,7 @@ library class Assign_ extends @py_Assign, Stmt {
override string toString() { result = "Assign" }
}
/** INTERNAL: See the class `AssignExpr` for further information. */
library class AssignExpr_ extends @py_AssignExpr, Expr {
/** Gets the value of this assignment expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -63,6 +69,7 @@ library class AssignExpr_ extends @py_AssignExpr, Expr {
override string toString() { result = "AssignExpr" }
}
/** INTERNAL: See the class `Attribute` for further information. */
library class Attribute_ extends @py_Attribute, Expr {
/** Gets the object of this attribute expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -76,6 +83,7 @@ library class Attribute_ extends @py_Attribute, Expr {
override string toString() { result = "Attribute" }
}
/** INTERNAL: See the class `AugAssign` for further information. */
library class AugAssign_ extends @py_AugAssign, Stmt {
/** Gets the operation of this augmented assignment statement. */
BinaryExpr getOperation() { py_exprs(result, _, this, 1) }
@@ -83,14 +91,17 @@ library class AugAssign_ extends @py_AugAssign, Stmt {
override string toString() { result = "AugAssign" }
}
/** INTERNAL: See the class `AugLoad` for further information. */
library class AugLoad_ extends @py_AugLoad, ExprContext {
override string toString() { result = "AugLoad" }
}
/** INTERNAL: See the class `AugStore` for further information. */
library class AugStore_ extends @py_AugStore, ExprContext {
override string toString() { result = "AugStore" }
}
/** INTERNAL: See the class `Await` for further information. */
library class Await_ extends @py_Await, Expr {
/** Gets the expression waited upon of this await expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -98,6 +109,7 @@ library class Await_ extends @py_Await, Expr {
override string toString() { result = "Await" }
}
/** INTERNAL: See the class `BinaryExpr` for further information. */
library class BinaryExpr_ extends @py_BinaryExpr, Expr {
/** Gets the left sub-expression of this binary expression. */
Expr getLeft() { py_exprs(result, _, this, 2) }
@@ -113,18 +125,22 @@ library class BinaryExpr_ extends @py_BinaryExpr, Expr {
override string toString() { result = "BinaryExpr" }
}
/** INTERNAL: See the class `BitAnd` for further information. */
library class BitAnd_ extends @py_BitAnd, Operator {
override string toString() { result = "BitAnd" }
}
/** INTERNAL: See the class `BitOr` for further information. */
library class BitOr_ extends @py_BitOr, Operator {
override string toString() { result = "BitOr" }
}
/** INTERNAL: See the class `BitXor` for further information. */
library class BitXor_ extends @py_BitXor, Operator {
override string toString() { result = "BitXor" }
}
/** INTERNAL: See the class `BoolExpr` for further information. */
library class BoolExpr_ extends @py_BoolExpr, Expr {
/** Gets the operator of this boolean expression. */
Boolop getOp() { py_boolops(result, _, this) }
@@ -141,10 +157,12 @@ library class BoolExpr_ extends @py_BoolExpr, Expr {
override string toString() { result = "BoolExpr" }
}
/** INTERNAL: See the class `Break` for further information. */
library class Break_ extends @py_Break, Stmt {
override string toString() { result = "Break" }
}
/** INTERNAL: See the class `Bytes` for further information. */
library class Bytes_ extends @py_Bytes, Expr {
/** Gets the value of this bytes expression. */
string getS() { py_bytes(result, this, 2) }
@@ -168,10 +186,13 @@ library class Bytes_ extends @py_Bytes, Expr {
override string toString() { result = "Bytes" }
}
/** INTERNAL: See the class `BytesOrStr` for further information. */
library class BytesOrStr_ extends @py_Bytes_or_Str {
/** Gets a textual representation of this element. */
string toString() { result = "BytesOrStr" }
}
/** INTERNAL: See the class `Call` for further information. */
library class Call_ extends @py_Call, Expr {
/** Gets the callable of this call expression. */
Expr getFunc() { py_exprs(result, _, this, 2) }
@@ -197,6 +218,7 @@ library class Call_ extends @py_Call, Expr {
override string toString() { result = "Call" }
}
/** INTERNAL: See the class `Class` for further information. */
library class Class_ extends @py_Class {
/** Gets the name of this class. */
string getName() { py_strs(result, this, 0) }
@@ -212,9 +234,11 @@ library class Class_ extends @py_Class {
ClassExpr getParent() { py_Classes(this, result) }
/** Gets a textual representation of this element. */
string toString() { result = "Class" }
}
/** INTERNAL: See the class `ClassExpr` for further information. */
library class ClassExpr_ extends @py_ClassExpr, Expr {
/** Gets the name of this class definition. */
string getName() { py_strs(result, this, 2) }
@@ -243,6 +267,7 @@ library class ClassExpr_ extends @py_ClassExpr, Expr {
override string toString() { result = "ClassExpr" }
}
/** INTERNAL: See the class `Compare` for further information. */
library class Compare_ extends @py_Compare, Expr {
/** Gets the left sub-expression of this compare expression. */
Expr getLeft() { py_exprs(result, _, this, 2) }
@@ -268,14 +293,17 @@ library class Compare_ extends @py_Compare, Expr {
override string toString() { result = "Compare" }
}
/** INTERNAL: See the class `Continue` for further information. */
library class Continue_ extends @py_Continue, Stmt {
override string toString() { result = "Continue" }
}
/** INTERNAL: See the class `Del` for further information. */
library class Del_ extends @py_Del, ExprContext {
override string toString() { result = "Del" }
}
/** INTERNAL: See the class `Delete` for further information. */
library class Delete_ extends @py_Delete, Stmt {
/** Gets the targets of this delete statement. */
ExprList getTargets() { py_expr_lists(result, this, 1) }
@@ -289,6 +317,7 @@ library class Delete_ extends @py_Delete, Stmt {
override string toString() { result = "Delete" }
}
/** INTERNAL: See the class `Dict` for further information. */
library class Dict_ extends @py_Dict, Expr {
/** Gets the items of this dictionary expression. */
DictItemList getItems() { py_dict_item_lists(result, this) }
@@ -302,6 +331,7 @@ library class Dict_ extends @py_Dict, Expr {
override string toString() { result = "Dict" }
}
/** INTERNAL: See the class `DictComp` for further information. */
library class DictComp_ extends @py_DictComp, Expr {
/** Gets the implementation of this dictionary comprehension. */
Function getFunction() { py_Functions(result, this) }
@@ -312,6 +342,7 @@ library class DictComp_ extends @py_DictComp, Expr {
override string toString() { result = "DictComp" }
}
/** INTERNAL: See the class `DictUnpacking` for further information. */
library class DictUnpacking_ extends @py_DictUnpacking, DictItem {
/** Gets the location of this dictionary unpacking. */
override Location getLocation() { py_locations(result, this) }
@@ -322,18 +353,22 @@ library class DictUnpacking_ extends @py_DictUnpacking, DictItem {
override string toString() { result = "DictUnpacking" }
}
/** INTERNAL: See the class `Div` for further information. */
library class Div_ extends @py_Div, Operator {
override string toString() { result = "Div" }
}
/** INTERNAL: See the class `Ellipsis` for further information. */
library class Ellipsis_ extends @py_Ellipsis, Expr {
override string toString() { result = "Ellipsis" }
}
/** INTERNAL: See the class `Eq` for further information. */
library class Eq_ extends @py_Eq, Cmpop {
override string toString() { result = "Eq" }
}
/** INTERNAL: See the class `ExceptStmt` for further information. */
library class ExceptStmt_ extends @py_ExceptStmt, Stmt {
/** Gets the type of this except block. */
Expr getType() { py_exprs(result, _, this, 1) }
@@ -353,6 +388,7 @@ library class ExceptStmt_ extends @py_ExceptStmt, Stmt {
override string toString() { result = "ExceptStmt" }
}
/** INTERNAL: See the class `Exec` for further information. */
library class Exec_ extends @py_Exec, Stmt {
/** Gets the body of this exec statement. */
Expr getBody() { py_exprs(result, _, this, 1) }
@@ -366,6 +402,7 @@ library class Exec_ extends @py_Exec, Stmt {
override string toString() { result = "Exec" }
}
/** INTERNAL: See the class `ExprStmt` for further information. */
library class ExprStmt_ extends @py_Expr_stmt, Stmt {
/** Gets the value of this expr statement. */
Expr getValue() { py_exprs(result, _, this, 1) }
@@ -373,6 +410,7 @@ library class ExprStmt_ extends @py_Expr_stmt, Stmt {
override string toString() { result = "ExprStmt" }
}
/** INTERNAL: See the class `Filter` for further information. */
library class Filter_ extends @py_Filter, Expr {
/** Gets the filtered value of this template filter expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -383,10 +421,12 @@ library class Filter_ extends @py_Filter, Expr {
override string toString() { result = "Filter" }
}
/** INTERNAL: See the class `FloorDiv` for further information. */
library class FloorDiv_ extends @py_FloorDiv, Operator {
override string toString() { result = "FloorDiv" }
}
/** INTERNAL: See the class `For` for further information. */
library class For_ extends @py_For, Stmt {
/** Gets the target of this for statement. */
Expr getTarget() { py_exprs(result, _, this, 1) }
@@ -418,6 +458,7 @@ library class For_ extends @py_For, Stmt {
override string toString() { result = "For" }
}
/** INTERNAL: See the class `FormattedValue` for further information. */
library class FormattedValue_ extends @py_FormattedValue, Expr {
/** Gets the expression to be formatted of this formatted value. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -431,6 +472,7 @@ library class FormattedValue_ extends @py_FormattedValue, Expr {
override string toString() { result = "FormattedValue" }
}
/** INTERNAL: See the class `Function` for further information. */
library class Function_ extends @py_Function {
/** Gets the name of this function. */
string getName() { py_strs(result, this, 0) }
@@ -473,9 +515,11 @@ library class Function_ extends @py_Function {
FunctionParent getParent() { py_Functions(this, result) }
/** Gets a textual representation of this element. */
string toString() { result = "Function" }
}
/** INTERNAL: See the class `FunctionExpr` for further information. */
library class FunctionExpr_ extends @py_FunctionExpr, Expr {
/** Gets the name of this function definition. */
string getName() { py_strs(result, this, 2) }
@@ -492,10 +536,13 @@ library class FunctionExpr_ extends @py_FunctionExpr, Expr {
override string toString() { result = "FunctionExpr" }
}
/** INTERNAL: See the class `FunctionParent` for further information. */
library class FunctionParent_ extends @py_Function_parent {
/** Gets a textual representation of this element. */
string toString() { result = "FunctionParent" }
}
/** INTERNAL: See the class `GeneratorExp` for further information. */
library class GeneratorExp_ extends @py_GeneratorExp, Expr {
/** Gets the implementation of this generator expression. */
Function getFunction() { py_Functions(result, this) }
@@ -506,6 +553,7 @@ library class GeneratorExp_ extends @py_GeneratorExp, Expr {
override string toString() { result = "GeneratorExp" }
}
/** INTERNAL: See the class `Global` for further information. */
library class Global_ extends @py_Global, Stmt {
/** Gets the names of this global statement. */
StringList getNames() { py_str_lists(result, this) }
@@ -519,14 +567,17 @@ library class Global_ extends @py_Global, Stmt {
override string toString() { result = "Global" }
}
/** INTERNAL: See the class `Gt` for further information. */
library class Gt_ extends @py_Gt, Cmpop {
override string toString() { result = "Gt" }
}
/** INTERNAL: See the class `GtE` for further information. */
library class GtE_ extends @py_GtE, Cmpop {
override string toString() { result = "GtE" }
}
/** INTERNAL: See the class `If` for further information. */
library class If_ extends @py_If, Stmt {
/** Gets the test of this if statement. */
Expr getTest() { py_exprs(result, _, this, 1) }
@@ -552,6 +603,7 @@ library class If_ extends @py_If, Stmt {
override string toString() { result = "If" }
}
/** INTERNAL: See the class `IfExp` for further information. */
library class IfExp_ extends @py_IfExp, Expr {
/** Gets the test of this if expression. */
Expr getTest() { py_exprs(result, _, this, 2) }
@@ -565,6 +617,7 @@ library class IfExp_ extends @py_IfExp, Expr {
override string toString() { result = "IfExp" }
}
/** INTERNAL: See the class `Import` for further information. */
library class Import_ extends @py_Import, Stmt {
/** Gets the alias list of this import statement. */
AliasList getNames() { py_alias_lists(result, this) }
@@ -578,6 +631,7 @@ library class Import_ extends @py_Import, Stmt {
override string toString() { result = "Import" }
}
/** INTERNAL: See the class `ImportExpr` for further information. */
library class ImportExpr_ extends @py_ImportExpr, Expr {
/** Gets the level of this import expression. */
int getLevel() { py_ints(result, this) }
@@ -591,6 +645,7 @@ library class ImportExpr_ extends @py_ImportExpr, Expr {
override string toString() { result = "ImportExpr" }
}
/** INTERNAL: See the class `ImportStar` for further information. */
library class ImportStar_ extends @py_ImportStar, Stmt {
/** Gets the module of this import * statement. */
Expr getModule() { py_exprs(result, _, this, 1) }
@@ -598,6 +653,7 @@ library class ImportStar_ extends @py_ImportStar, Stmt {
override string toString() { result = "ImportStar" }
}
/** INTERNAL: See the class `ImportMember` for further information. */
library class ImportMember_ extends @py_ImportMember, Expr {
/** Gets the module of this from import. */
Expr getModule() { py_exprs(result, _, this, 2) }
@@ -608,22 +664,27 @@ library class ImportMember_ extends @py_ImportMember, Expr {
override string toString() { result = "ImportMember" }
}
/** INTERNAL: See the class `In` for further information. */
library class In_ extends @py_In, Cmpop {
override string toString() { result = "In" }
}
/** INTERNAL: See the class `Invert` for further information. */
library class Invert_ extends @py_Invert, Unaryop {
override string toString() { result = "Invert" }
}
/** INTERNAL: See the class `Is` for further information. */
library class Is_ extends @py_Is, Cmpop {
override string toString() { result = "Is" }
}
/** INTERNAL: See the class `IsNot` for further information. */
library class IsNot_ extends @py_IsNot, Cmpop {
override string toString() { result = "IsNot" }
}
/** INTERNAL: See the class `Fstring` for further information. */
library class Fstring_ extends @py_Fstring, Expr {
/** Gets the values of this formatted string literal. */
ExprList getValues() { py_expr_lists(result, this, 2) }
@@ -639,6 +700,7 @@ library class Fstring_ extends @py_Fstring, Expr {
override string toString() { result = "Fstring" }
}
/** INTERNAL: See the class `KeyValuePair` for further information. */
library class KeyValuePair_ extends @py_KeyValuePair, DictItem {
/** Gets the location of this key-value pair. */
override Location getLocation() { py_locations(result, this) }
@@ -652,10 +714,12 @@ library class KeyValuePair_ extends @py_KeyValuePair, DictItem {
override string toString() { result = "KeyValuePair" }
}
/** INTERNAL: See the class `LShift` for further information. */
library class LShift_ extends @py_LShift, Operator {
override string toString() { result = "LShift" }
}
/** INTERNAL: See the class `Lambda` for further information. */
library class Lambda_ extends @py_Lambda, Expr {
/** Gets the arguments of this lambda expression. */
Arguments getArgs() { py_arguments(result, this) }
@@ -666,6 +730,7 @@ library class Lambda_ extends @py_Lambda, Expr {
override string toString() { result = "Lambda" }
}
/** INTERNAL: See the class `List` for further information. */
library class List_ extends @py_List, Expr {
/** Gets the element list of this list expression. */
ExprList getElts() { py_expr_lists(result, this, 2) }
@@ -682,6 +747,7 @@ library class List_ extends @py_List, Expr {
override string toString() { result = "List" }
}
/** INTERNAL: See the class `ListComp` for further information. */
library class ListComp_ extends @py_ListComp, Expr {
/** Gets the implementation of this list comprehension. */
Function getFunction() { py_Functions(result, this) }
@@ -704,26 +770,32 @@ library class ListComp_ extends @py_ListComp, Expr {
override string toString() { result = "ListComp" }
}
/** INTERNAL: See the class `Load` for further information. */
library class Load_ extends @py_Load, ExprContext {
override string toString() { result = "Load" }
}
/** INTERNAL: See the class `Lt` for further information. */
library class Lt_ extends @py_Lt, Cmpop {
override string toString() { result = "Lt" }
}
/** INTERNAL: See the class `LtE` for further information. */
library class LtE_ extends @py_LtE, Cmpop {
override string toString() { result = "LtE" }
}
/** INTERNAL: See the class `MatMult` for further information. */
library class MatMult_ extends @py_MatMult, Operator {
override string toString() { result = "MatMult" }
}
/** INTERNAL: See the class `Mod` for further information. */
library class Mod_ extends @py_Mod, Operator {
override string toString() { result = "Mod" }
}
/** INTERNAL: See the class `Module` for further information. */
library class Module_ extends @py_Module {
/** Gets the name of this module. */
string getName() { py_strs(result, this, 0) }
@@ -743,13 +815,16 @@ library class Module_ extends @py_Module {
/** Gets the kind of this module. */
string getKind() { py_strs(result, this, 3) }
/** Gets a textual representation of this element. */
string toString() { result = "Module" }
}
/** INTERNAL: See the class `Mult` for further information. */
library class Mult_ extends @py_Mult, Operator {
override string toString() { result = "Mult" }
}
/** INTERNAL: See the class `Name` for further information. */
library class Name_ extends @py_Name, Expr {
/** Gets the variable of this name expression. */
Variable getVariable() { py_variables(result, this) }
@@ -762,6 +837,7 @@ library class Name_ extends @py_Name, Expr {
override string toString() { result = "Name" }
}
/** INTERNAL: See the class `Nonlocal` for further information. */
library class Nonlocal_ extends @py_Nonlocal, Stmt {
/** Gets the names of this nonlocal statement. */
StringList getNames() { py_str_lists(result, this) }
@@ -775,18 +851,22 @@ library class Nonlocal_ extends @py_Nonlocal, Stmt {
override string toString() { result = "Nonlocal" }
}
/** INTERNAL: See the class `Not` for further information. */
library class Not_ extends @py_Not, Unaryop {
override string toString() { result = "Not" }
}
/** INTERNAL: See the class `NotEq` for further information. */
library class NotEq_ extends @py_NotEq, Cmpop {
override string toString() { result = "NotEq" }
}
/** INTERNAL: See the class `NotIn` for further information. */
library class NotIn_ extends @py_NotIn, Cmpop {
override string toString() { result = "NotIn" }
}
/** INTERNAL: See the class `Num` for further information. */
library class Num_ extends @py_Num, Expr {
/** Gets the value of this numeric literal. */
string getN() { py_numbers(result, this, 2) }
@@ -797,18 +877,22 @@ library class Num_ extends @py_Num, Expr {
override string toString() { result = "Num" }
}
/** INTERNAL: See the class `Or` for further information. */
library class Or_ extends @py_Or, Boolop {
override string toString() { result = "Or" }
}
/** INTERNAL: See the class `Param` for further information. */
library class Param_ extends @py_Param, ExprContext {
override string toString() { result = "Param" }
}
/** INTERNAL: See the class `Pass` for further information. */
library class Pass_ extends @py_Pass, Stmt {
override string toString() { result = "Pass" }
}
/** INTERNAL: See the class `PlaceHolder` for further information. */
library class PlaceHolder_ extends @py_PlaceHolder, Expr {
/** Gets the variable of this template place-holder expression. */
Variable getVariable() { py_variables(result, this) }
@@ -819,10 +903,12 @@ library class PlaceHolder_ extends @py_PlaceHolder, Expr {
override string toString() { result = "PlaceHolder" }
}
/** INTERNAL: See the class `Pow` for further information. */
library class Pow_ extends @py_Pow, Operator {
override string toString() { result = "Pow" }
}
/** INTERNAL: See the class `Print` for further information. */
library class Print_ extends @py_Print, Stmt {
/** Gets the destination of this print statement. */
Expr getDest() { py_exprs(result, _, this, 1) }
@@ -842,10 +928,12 @@ library class Print_ extends @py_Print, Stmt {
override string toString() { result = "Print" }
}
/** INTERNAL: See the class `RShift` for further information. */
library class RShift_ extends @py_RShift, Operator {
override string toString() { result = "RShift" }
}
/** INTERNAL: See the class `Raise` for further information. */
library class Raise_ extends @py_Raise, Stmt {
/** Gets the exception of this raise statement. */
Expr getExc() { py_exprs(result, _, this, 1) }
@@ -865,6 +953,7 @@ library class Raise_ extends @py_Raise, Stmt {
override string toString() { result = "Raise" }
}
/** INTERNAL: See the class `Repr` for further information. */
library class Repr_ extends @py_Repr, Expr {
/** Gets the value of this backtick expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -872,6 +961,7 @@ library class Repr_ extends @py_Repr, Expr {
override string toString() { result = "Repr" }
}
/** INTERNAL: See the class `Return` for further information. */
library class Return_ extends @py_Return, Stmt {
/** Gets the value of this return statement. */
Expr getValue() { py_exprs(result, _, this, 1) }
@@ -879,6 +969,7 @@ library class Return_ extends @py_Return, Stmt {
override string toString() { result = "Return" }
}
/** INTERNAL: See the class `Set` for further information. */
library class Set_ extends @py_Set, Expr {
/** Gets the elements of this set expression. */
ExprList getElts() { py_expr_lists(result, this, 2) }
@@ -892,6 +983,7 @@ library class Set_ extends @py_Set, Expr {
override string toString() { result = "Set" }
}
/** INTERNAL: See the class `SetComp` for further information. */
library class SetComp_ extends @py_SetComp, Expr {
/** Gets the implementation of this set comprehension. */
Function getFunction() { py_Functions(result, this) }
@@ -902,6 +994,7 @@ library class SetComp_ extends @py_SetComp, Expr {
override string toString() { result = "SetComp" }
}
/** INTERNAL: See the class `Slice` for further information. */
library class Slice_ extends @py_Slice, Expr {
/** Gets the start of this slice. */
Expr getStart() { py_exprs(result, _, this, 2) }
@@ -915,6 +1008,7 @@ library class Slice_ extends @py_Slice, Expr {
override string toString() { result = "Slice" }
}
/** INTERNAL: See the class `SpecialOperation` for further information. */
library class SpecialOperation_ extends @py_SpecialOperation, Expr {
/** Gets the name of this special operation. */
string getName() { py_strs(result, this, 2) }
@@ -931,6 +1025,7 @@ library class SpecialOperation_ extends @py_SpecialOperation, Expr {
override string toString() { result = "SpecialOperation" }
}
/** INTERNAL: See the class `Starred` for further information. */
library class Starred_ extends @py_Starred, Expr {
/** Gets the value of this starred expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -941,10 +1036,12 @@ library class Starred_ extends @py_Starred, Expr {
override string toString() { result = "Starred" }
}
/** INTERNAL: See the class `Store` for further information. */
library class Store_ extends @py_Store, ExprContext {
override string toString() { result = "Store" }
}
/** INTERNAL: See the class `Str` for further information. */
library class Str_ extends @py_Str, Expr {
/** Gets the text of this string literal. */
string getS() { py_strs(result, this, 2) }
@@ -968,6 +1065,7 @@ library class Str_ extends @py_Str, Expr {
override string toString() { result = "Str" }
}
/** INTERNAL: See the class `StringPart` for further information. */
library class StringPart_ extends @py_StringPart {
/** Gets the text of this implicitly concatenated part. */
string getText() { py_strs(result, this, 0) }
@@ -977,9 +1075,11 @@ library class StringPart_ extends @py_StringPart {
StringPartList getParent() { py_StringParts(this, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = "StringPart" }
}
/** INTERNAL: See the class `StringPartList` for further information. */
library class StringPartList_ extends @py_StringPart_list {
BytesOrStr getParent() { py_StringPart_lists(this, result) }
@@ -989,13 +1089,16 @@ library class StringPartList_ extends @py_StringPart_list {
/** Gets the nth item of this implicitly concatenated part list */
StringPart getItem(int index) { py_StringParts(result, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "StringPartList" }
}
/** INTERNAL: See the class `Sub` for further information. */
library class Sub_ extends @py_Sub, Operator {
override string toString() { result = "Sub" }
}
/** INTERNAL: See the class `Subscript` for further information. */
library class Subscript_ extends @py_Subscript, Expr {
/** Gets the value of this subscript expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -1009,6 +1112,7 @@ library class Subscript_ extends @py_Subscript, Expr {
override string toString() { result = "Subscript" }
}
/** INTERNAL: See the class `TemplateDottedNotation` for further information. */
library class TemplateDottedNotation_ extends @py_TemplateDottedNotation, Expr {
/** Gets the object of this template dotted notation expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -1022,6 +1126,7 @@ library class TemplateDottedNotation_ extends @py_TemplateDottedNotation, Expr {
override string toString() { result = "TemplateDottedNotation" }
}
/** INTERNAL: See the class `TemplateWrite` for further information. */
library class TemplateWrite_ extends @py_TemplateWrite, Stmt {
/** Gets the value of this template write statement. */
Expr getValue() { py_exprs(result, _, this, 1) }
@@ -1029,6 +1134,7 @@ library class TemplateWrite_ extends @py_TemplateWrite, Stmt {
override string toString() { result = "TemplateWrite" }
}
/** INTERNAL: See the class `Try` for further information. */
library class Try_ extends @py_Try, Stmt {
/** Gets the body of this try statement. */
StmtList getBody() { py_stmt_lists(result, this, 1) }
@@ -1069,6 +1175,7 @@ library class Try_ extends @py_Try, Stmt {
override string toString() { result = "Try" }
}
/** INTERNAL: See the class `Tuple` for further information. */
library class Tuple_ extends @py_Tuple, Expr {
/** Gets the elements of this tuple expression. */
ExprList getElts() { py_expr_lists(result, this, 2) }
@@ -1087,14 +1194,17 @@ library class Tuple_ extends @py_Tuple, Expr {
override string toString() { result = "Tuple" }
}
/** INTERNAL: See the class `UAdd` for further information. */
library class UAdd_ extends @py_UAdd, Unaryop {
override string toString() { result = "UAdd" }
}
/** INTERNAL: See the class `USub` for further information. */
library class USub_ extends @py_USub, Unaryop {
override string toString() { result = "USub" }
}
/** INTERNAL: See the class `UnaryExpr` for further information. */
library class UnaryExpr_ extends @py_UnaryExpr, Expr {
/** Gets the operator of this unary expression. */
Unaryop getOp() { py_unaryops(result, _, this) }
@@ -1105,6 +1215,7 @@ library class UnaryExpr_ extends @py_UnaryExpr, Expr {
override string toString() { result = "UnaryExpr" }
}
/** INTERNAL: See the class `While` for further information. */
library class While_ extends @py_While, Stmt {
/** Gets the test of this while statement. */
Expr getTest() { py_exprs(result, _, this, 1) }
@@ -1130,6 +1241,7 @@ library class While_ extends @py_While, Stmt {
override string toString() { result = "While" }
}
/** INTERNAL: See the class `With` for further information. */
library class With_ extends @py_With, Stmt {
/** Gets the context manager of this with statement. */
Expr getContextExpr() { py_exprs(result, _, this, 1) }
@@ -1152,6 +1264,7 @@ library class With_ extends @py_With, Stmt {
override string toString() { result = "With" }
}
/** INTERNAL: See the class `Yield` for further information. */
library class Yield_ extends @py_Yield, Expr {
/** Gets the value of this yield expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -1159,6 +1272,7 @@ library class Yield_ extends @py_Yield, Expr {
override string toString() { result = "Yield" }
}
/** INTERNAL: See the class `YieldFrom` for further information. */
library class YieldFrom_ extends @py_YieldFrom, Expr {
/** Gets the value of this yield-from expression. */
Expr getValue() { py_exprs(result, _, this, 2) }
@@ -1166,6 +1280,7 @@ library class YieldFrom_ extends @py_YieldFrom, Expr {
override string toString() { result = "YieldFrom" }
}
/** INTERNAL: See the class `Alias` for further information. */
library class Alias_ extends @py_alias {
/** Gets the value of this alias. */
Expr getValue() { py_exprs(result, _, this, 0) }
@@ -1175,9 +1290,11 @@ library class Alias_ extends @py_alias {
AliasList getParent() { py_aliases(this, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = "Alias" }
}
/** INTERNAL: See the class `AliasList` for further information. */
library class AliasList_ extends @py_alias_list {
Import getParent() { py_alias_lists(this, result) }
@@ -1187,9 +1304,11 @@ library class AliasList_ extends @py_alias_list {
/** Gets the nth item of this alias list */
Alias getItem(int index) { py_aliases(result, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "AliasList" }
}
/** INTERNAL: See the class `Arguments` for further information. */
library class Arguments_ extends @py_arguments {
/** Gets the keyword-only default values of this parameters definition. */
ExprList getKwDefaults() { py_expr_lists(result, this, 0) }
@@ -1235,33 +1354,45 @@ library class Arguments_ extends @py_arguments {
ArgumentsParent getParent() { py_arguments(this, result) }
/** Gets a textual representation of this element. */
string toString() { result = "Arguments" }
}
/** INTERNAL: See the class `ArgumentsParent` for further information. */
library class ArgumentsParent_ extends @py_arguments_parent {
/** Gets a textual representation of this element. */
string toString() { result = "ArgumentsParent" }
}
/** INTERNAL: See the class `AstNode` for further information. */
library class AstNode_ extends @py_ast_node {
/** Gets a textual representation of this element. */
string toString() { result = "AstNode" }
}
/** INTERNAL: See the class `BoolParent` for further information. */
library class BoolParent_ extends @py_bool_parent {
/** Gets a textual representation of this element. */
string toString() { result = "BoolParent" }
}
/** INTERNAL: See the class `Boolop` for further information. */
library class Boolop_ extends @py_boolop {
BoolExpr getParent() { py_boolops(this, _, result) }
/** Gets a textual representation of this element. */
string toString() { result = "Boolop" }
}
/** INTERNAL: See the class `Cmpop` for further information. */
library class Cmpop_ extends @py_cmpop {
CmpopList getParent() { py_cmpops(this, _, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = "Cmpop" }
}
/** INTERNAL: See the class `CmpopList` for further information. */
library class CmpopList_ extends @py_cmpop_list {
Compare getParent() { py_cmpop_lists(this, result) }
@@ -1271,9 +1402,11 @@ library class CmpopList_ extends @py_cmpop_list {
/** Gets the nth item of this comparison operator list */
Cmpop getItem(int index) { py_cmpops(result, _, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "CmpopList" }
}
/** INTERNAL: See the class `Comprehension` for further information. */
library class Comprehension_ extends @py_comprehension {
/** Gets the location of this comprehension. */
Location getLocation() { py_locations(result, this) }
@@ -1295,9 +1428,11 @@ library class Comprehension_ extends @py_comprehension {
ComprehensionList getParent() { py_comprehensions(this, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = "Comprehension" }
}
/** INTERNAL: See the class `ComprehensionList` for further information. */
library class ComprehensionList_ extends @py_comprehension_list {
ListComp getParent() { py_comprehension_lists(this, result) }
@@ -1307,15 +1442,19 @@ library class ComprehensionList_ extends @py_comprehension_list {
/** Gets the nth item of this comprehension list */
Comprehension getItem(int index) { py_comprehensions(result, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "ComprehensionList" }
}
/** INTERNAL: See the class `DictItem` for further information. */
library class DictItem_ extends @py_dict_item {
DictItemList getParent() { py_dict_items(this, _, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = "DictItem" }
}
/** INTERNAL: See the class `DictItemList` for further information. */
library class DictItemList_ extends @py_dict_item_list {
DictItemListParent getParent() { py_dict_item_lists(this, result) }
@@ -1325,13 +1464,17 @@ library class DictItemList_ extends @py_dict_item_list {
/** Gets the nth item of this dict_item list */
DictItem getItem(int index) { py_dict_items(result, _, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "DictItemList" }
}
/** INTERNAL: See the class `DictItemListParent` for further information. */
library class DictItemListParent_ extends @py_dict_item_list_parent {
/** Gets a textual representation of this element. */
string toString() { result = "DictItemListParent" }
}
/** INTERNAL: See the class `Expr` for further information. */
library class Expr_ extends @py_expr {
/** Gets the location of this expression. */
Location getLocation() { py_locations(result, this) }
@@ -1341,19 +1484,25 @@ library class Expr_ extends @py_expr {
ExprParent getParent() { py_exprs(this, _, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = "Expr" }
}
/** INTERNAL: See the class `ExprContext` for further information. */
library class ExprContext_ extends @py_expr_context {
ExprContextParent getParent() { py_expr_contexts(this, _, result) }
/** Gets a textual representation of this element. */
string toString() { result = "ExprContext" }
}
/** INTERNAL: See the class `ExprContextParent` for further information. */
library class ExprContextParent_ extends @py_expr_context_parent {
/** Gets a textual representation of this element. */
string toString() { result = "ExprContextParent" }
}
/** INTERNAL: See the class `ExprList` for further information. */
library class ExprList_ extends @py_expr_list {
ExprListParent getParent() { py_expr_lists(this, result, _) }
@@ -1363,21 +1512,29 @@ library class ExprList_ extends @py_expr_list {
/** Gets the nth item of this expression list */
Expr getItem(int index) { py_exprs(result, _, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "ExprList" }
}
/** INTERNAL: See the class `ExprListParent` for further information. */
library class ExprListParent_ extends @py_expr_list_parent {
/** Gets a textual representation of this element. */
string toString() { result = "ExprListParent" }
}
/** INTERNAL: See the class `ExprOrStmt` for further information. */
library class ExprOrStmt_ extends @py_expr_or_stmt {
/** Gets a textual representation of this element. */
string toString() { result = "ExprOrStmt" }
}
/** INTERNAL: See the class `ExprParent` for further information. */
library class ExprParent_ extends @py_expr_parent {
/** Gets a textual representation of this element. */
string toString() { result = "ExprParent" }
}
/** INTERNAL: See the class `Keyword` for further information. */
library class Keyword_ extends @py_keyword, DictItem {
/** Gets the location of this keyword argument. */
override Location getLocation() { py_locations(result, this) }
@@ -1391,33 +1548,44 @@ library class Keyword_ extends @py_keyword, DictItem {
override string toString() { result = "Keyword" }
}
/** INTERNAL: See the class `LocationParent` for further information. */
library class LocationParent_ extends @py_location_parent {
/** Gets a textual representation of this element. */
string toString() { result = "LocationParent" }
}
/** INTERNAL: See the class `Operator` for further information. */
library class Operator_ extends @py_operator {
BinaryExpr getParent() { py_operators(this, _, result) }
/** Gets a textual representation of this element. */
string toString() { result = "Operator" }
}
/** INTERNAL: See the class `Parameter` for further information. */
library class Parameter_ extends @py_parameter {
/** Gets a textual representation of this element. */
string toString() { result = "Parameter" }
}
/** INTERNAL: See the class `Scope` for further information. */
library class Scope_ extends @py_scope {
/** Gets a textual representation of this element. */
string toString() { result = "Scope" }
}
/** INTERNAL: See the class `Stmt` for further information. */
library class Stmt_ extends @py_stmt {
/** Gets the location of this statement. */
Location getLocation() { py_locations(result, this) }
StmtList getParent() { py_stmts(this, _, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = "Stmt" }
}
/** INTERNAL: See the class `StmtList` for further information. */
library class StmtList_ extends @py_stmt_list {
StmtListParent getParent() { py_stmt_lists(this, result, _) }
@@ -1427,13 +1595,17 @@ library class StmtList_ extends @py_stmt_list {
/** Gets the nth item of this statement list */
Stmt getItem(int index) { py_stmts(result, _, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "StmtList" }
}
/** INTERNAL: See the class `StmtListParent` for further information. */
library class StmtListParent_ extends @py_stmt_list_parent {
/** Gets a textual representation of this element. */
string toString() { result = "StmtListParent" }
}
/** INTERNAL: See the class `StringList` for further information. */
library class StringList_ extends @py_str_list {
StrListParent getParent() { py_str_lists(this, result) }
@@ -1443,23 +1615,32 @@ library class StringList_ extends @py_str_list {
/** Gets the nth item of this string list */
string getItem(int index) { py_strs(result, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "StringList" }
}
/** INTERNAL: See the class `StrListParent` for further information. */
library class StrListParent_ extends @py_str_list_parent {
/** Gets a textual representation of this element. */
string toString() { result = "StrListParent" }
}
/** INTERNAL: See the class `StrParent` for further information. */
library class StrParent_ extends @py_str_parent {
/** Gets a textual representation of this element. */
string toString() { result = "StrParent" }
}
/** INTERNAL: See the class `Unaryop` for further information. */
library class Unaryop_ extends @py_unaryop {
UnaryExpr getParent() { py_unaryops(this, _, result) }
/** Gets a textual representation of this element. */
string toString() { result = "Unaryop" }
}
/** INTERNAL: See the class `VariableParent` for further information. */
library class VariableParent_ extends @py_variable_parent {
/** Gets a textual representation of this element. */
string toString() { result = "VariableParent" }
}

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing Python classes.
*/
import python
/**
@@ -37,6 +41,7 @@ class ClassExpr extends ClassExpr_ {
result = this.getStarargs()
}
/** Gets a call corresponding to a decorator of this class definition. */
Call getADecoratorCall() {
result.getArg(0) = this or
result.getArg(0) = this.getADecoratorCall()
@@ -114,9 +119,10 @@ class Class extends Class_, Scope, AstNode {
/** Gets the name used to define this class */
override string getName() { result = Class_.super.getName() }
/** Holds if this expression may have a side effect (as determined purely from its syntax). */
predicate hasSideEffects() { any() }
/** Whether this is probably a mixin (has 'mixin' or similar in name or docstring) */
/** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */
predicate isProbableMixin() {
(
this.getName().toLowerCase().matches("%mixin%")
@@ -129,6 +135,7 @@ class Class extends Class_, Scope, AstNode {
override AstNode getAChildNode() { result = this.getAStmt() }
/** Gets a decorator of this class. */
Expr getADecorator() { result = this.getParent().getADecorator() }
/** Gets the metaclass expression */

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing comments in Python.
*/
import python
/** A source code comment */
@@ -10,6 +14,7 @@ class Comment extends @py_comment {
Location getLocation() { py_comments(this, _, result) }
/** Gets a textual representation of this element. */
string toString() { result = "Comment " + this.getText() }
/**
@@ -51,22 +56,33 @@ class CommentBlock extends @py_comment {
private Comment last() { comment_block_part(this, result, this.length()) }
/** Gets a textual representation of this element. */
string toString() { result = "Comment block" }
/** The length of this comment block (in comments) */
int length() { result = max(int i | comment_block_part(this, _, i)) }
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
this.(Comment).getLocation().hasLocationInfo(filepath, bl, bc, _, _) and
exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, el, ec))
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn))
}
/** Holds if this comment block contains `c`. */
predicate contains(Comment c) {
comment_block_part(this, c, _)
or
this = c
}
/** Gets a string representation of this comment block. */
string getContents() {
result =
concat(Comment c, int i |

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing comparison operators.
*/
import python
/** A class representing the six comparison operators, ==, !=, <, <=, > and >=. */
@@ -34,6 +38,7 @@ class CompareOp extends int {
this = ge() and result = le()
}
/** Gets the textual representation of `this`. */
string repr() {
this = eq() and result = "=="
or
@@ -48,6 +53,7 @@ class CompareOp extends int {
this = ge() and result = ">="
}
/** Holds if `op` is the `Cmpop` corresponding to `this`. */
predicate forOp(Cmpop op) {
op instanceof Eq and this = eq()
or
@@ -70,16 +76,22 @@ class CompareOp extends int {
}
}
/** The `CompareOp` for "equals". */
CompareOp eq() { result = 1 }
/** The `CompareOp` for "not equals". */
CompareOp ne() { result = 2 }
/** The `CompareOp` for "less than". */
CompareOp lt() { result = 3 }
/** The `CompareOp` for "less than or equal to". */
CompareOp le() { result = 4 }
/** The `CompareOp` for "greater than". */
CompareOp gt() { result = 5 }
/** The `CompareOp` for "greater than or equal to". */
CompareOp ge() { result = 6 }
/* Workaround precision limits in floating point numbers */

View File

@@ -7,6 +7,7 @@ class Expr extends Expr_, AstNode {
/** Gets the scope of this expression */
override Scope getScope() { py_scopes(this, result) }
/** Gets a textual representation of this element. */
override string toString() { result = "Expression" }
/** Gets the module in which this expression occurs */

View File

@@ -10,8 +10,21 @@ class File extends Container {
/** DEPRECATED: Use `getAbsolutePath` instead. */
deprecated string getFullName() { result = this.getAbsolutePath() }
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
this.getAbsolutePath() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getAbsolutePath() = filepath and
startline = 0 and
startcolumn = 0 and
endline = 0 and
endcolumn = 0
}
/** Whether this file is a source code file. */
@@ -79,8 +92,21 @@ class Folder extends Container {
/** DEPRECATED: Use `getBaseName` instead. */
deprecated string getSimple() { folders(this, _, result) }
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
this.getAbsolutePath() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getAbsolutePath() = filepath and
startline = 0 and
startcolumn = 0 and
endline = 0 and
endcolumn = 0
}
override string getAbsolutePath() { folders(this, result, _) }
@@ -367,30 +393,48 @@ class Location extends @location {
locations_ast(this, _, _, _, _, result)
}
/** Gets a textual representation of this element. */
string toString() {
result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString()
}
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
exists(File f | f.getAbsolutePath() = filepath |
locations_default(this, f, bl, bc, el, ec)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { exists(File f | f.getAbsolutePath() = filepath |
locations_default(this, f, startline, startcolumn, endline, endcolumn)
or
exists(Module m | m.getFile() = f | locations_ast(this, m, bl, bc, el, ec))
exists(Module m | m.getFile() = f | locations_ast(this, m, startline, startcolumn, endline, endcolumn))
)
}
}
/** A non-empty line in the source code */
class Line extends @py_line {
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
exists(Module m |
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { exists(Module m |
m.getFile().getAbsolutePath() = filepath and
el = bl and
bc = 1 and
py_line_lengths(this, m, bl, ec)
endline = startline and
startcolumn = 1 and
py_line_lengths(this, m, startline, endcolumn)
)
}
/** Gets a textual representation of this element. */
string toString() {
exists(Module m | py_line_lengths(this, m, _, _) |
result = m.getFile().getShortName() + ":" + this.getLineNumber().toString()

View File

@@ -139,6 +139,7 @@ class ControlFlowNode extends @py_flow_node {
/** Gets the syntactic element corresponding to this flow node */
AstNode getNode() { py_flow_bb_node(this, result, _, _) }
/** Gets a textual representation of this element. */
string toString() {
exists(Scope s | s.getEntryNode() = this | result = "Entry node for " + s.toString())
or
@@ -1014,6 +1015,7 @@ class BasicBlock extends @py_flow_node {
/** Gets the nth node in this basic block */
ControlFlowNode getNode(int n) { py_flow_bb_node(result, _, this, n) }
/** Gets a textual representation of this element. */
string toString() { result = "BasicBlock" }
/** Whether this basic block strictly dominates the other */
@@ -1079,9 +1081,17 @@ class BasicBlock extends @py_flow_node {
this.getASuccessor().reachesExit()
}
predicate hasLocationInfo(string file, int line, int col, int endl, int endc) {
this.startLocationInfo(file, line, col) and
this.endLocationInfo(endl, endc)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { this.startLocationInfo(filepath, startline, startcolumn) and
this.endLocationInfo(endline, endcolumn)
}
/** Gets a true successor to this basic block */

View File

@@ -76,6 +76,7 @@ class SsaVariable extends @py_ssa_var {
result = this.getAPhiInput().getAnUltimateDefinition()
}
/** Gets a textual representation of this element. */
string toString() { result = "SSA Variable " + this.getId() }
Location getLocation() { result = this.getDefinition().getLocation() }

View File

@@ -90,8 +90,16 @@ class AugAssign extends AugAssign_ {
/* syntax: Expr += Expr */
override Expr getASubExpression() { result = this.getOperation() }
/**
* Gets the target of this augmented assignment statement.
* That is, the `a` in `a += b`.
*/
Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() }
/**
* Gets the value of this augmented assignment statement.
* That is, the `b` in `a += b`.
*/
Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() }
override Stmt getASubStatement() { none() }
@@ -405,11 +413,13 @@ class TemplateWrite extends TemplateWrite_ {
override Stmt getASubStatement() { none() }
}
/** An asynchronous `for` statement, such as `async for varname in Expr: ...` */
class AsyncFor extends For {
/* syntax: async for varname in Expr: ... */
AsyncFor() { this.isAsync() }
}
/** An asynchronous `with` statement, such as `async with varname as Expr: ...` */
class AsyncWith extends With {
/* syntax: async with Expr as varname: ... */
AsyncWith() { this.isAsync() }
@@ -417,10 +427,11 @@ class AsyncWith extends With {
/** A list of statements */
class StmtList extends StmtList_ {
/** Whether this list of statements contains s */
/** Holds if this list of statements contains the AST node `a` */
predicate contains(AstNode a) {
exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a))
}
/** Gets the last item in this list of statements, if any. */
Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) }
}

View File

@@ -13,6 +13,7 @@ class Variable extends @py_variable {
/** Gets the identifier (name) of this variable */
string getId() { variable(this, _, result) }
/** Gets a textual representation of this element. */
string toString() { result = "Variable " + this.getId() }
/** Gets an access (load or store) of this variable */

View File

@@ -24,6 +24,7 @@ newtype TTaintTrackingContext =
* Used to track taint through calls accurately and reasonably efficiently.
*/
class TaintTrackingContext extends TTaintTrackingContext {
/** Gets a textual representation of this element. */
string toString() {
this = TNoParam() and result = ""
or
@@ -66,6 +67,7 @@ private newtype TAttributePath =
* Used for tracking tainted attributes of objects.
*/
abstract class AttributePath extends TAttributePath {
/** Gets a textual representation of this element. */
abstract string toString();
abstract string extension();
@@ -126,6 +128,7 @@ newtype TTaintTrackingNode =
* Used for context-sensitive path-aware taint-tracking.
*/
class TaintTrackingNode extends TTaintTrackingNode {
/** Gets a textual representation of this element. */
string toString() {
if this.getPath() instanceof NoAttribute
then result = this.getTaintKind().repr()

View File

@@ -355,6 +355,7 @@ abstract class Sanitizer extends string {
* class to provide their own sources.
*/
abstract class TaintSource extends @py_flow_node {
/** Gets a textual representation of this element. */
string toString() { result = "Taint source" }
/**
@@ -378,8 +379,16 @@ abstract class TaintSource extends @py_flow_node {
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
this.getLocation().hasLocationInfo(fp, bl, bc, el, ec)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets a TaintedNode for this taint source */
@@ -470,6 +479,7 @@ private class SequenceExtends extends DataFlowExtension::DataFlowNode {
* class to provide their own sink nodes.
*/
abstract class TaintSink extends @py_flow_node {
/** Gets a textual representation of this element. */
string toString() { result = "Taint sink" }
/**
@@ -482,8 +492,16 @@ abstract class TaintSink extends @py_flow_node {
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
this.getLocation().hasLocationInfo(fp, bl, bc, el, ec)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -495,6 +513,7 @@ abstract class TaintSink extends @py_flow_node {
module DataFlowExtension {
/** A control flow node that modifies the basic data-flow. */
abstract class DataFlowNode extends @py_flow_node {
/** Gets a textual representation of this element. */
string toString() { result = "Dataflow extension node" }
/**
@@ -641,6 +660,7 @@ module DataFlow {
abstract EssaVariable asVariable();
/** Gets a textual representation of this element. */
abstract string toString();
abstract Scope getScope();
@@ -660,6 +680,7 @@ module DataFlow {
override EssaVariable asVariable() { none() }
/** Gets a textual representation of this element. */
override string toString() { result = this.asAstNode().toString() }
override Scope getScope() { result = this.asCfgNode().getScope() }
@@ -674,6 +695,7 @@ module DataFlow {
override EssaVariable asVariable() { this = TEssaNode(result) }
/** Gets a textual representation of this element. */
override string toString() { result = this.asVariable().toString() }
override Scope getScope() { result = this.asVariable().getScope() }

View File

@@ -29,6 +29,7 @@ abstract class SsaSourceVariable extends @py_variable {
abstract ControlFlowNode getScopeEntryDefinition();
/** Gets a textual representation of this element. */
string toString() { result = "SsaSourceVariable " + this.getName() }
/** Gets a use of this variable, either explicit or implicit. */

View File

@@ -24,6 +24,7 @@ class EssaVariable extends TEssaDefinition {
/** Gets the name of this variable. */
string getName() { result = this.getSourceVariable().getName() }
/** Gets a textual representation of this element. */
string toString() { result = "SSA variable " + this.getName() }
/**
@@ -131,6 +132,7 @@ private newtype TEssaDefinition =
* and exactly one variable for each definition.
*/
abstract class EssaDefinition extends TEssaDefinition {
/** Gets a textual representation of this element. */
string toString() { result = "EssaDefinition" }
/** Gets the source variable for which this a definition, either explicit or implicit. */

View File

@@ -36,6 +36,7 @@ class Value extends TObject {
this != ObjectInternal::undefined()
}
/** Gets a textual representation of this element. */
string toString() { result = this.(ObjectInternal).toString() }
/** Gets a `ControlFlowNode` that refers to this object. */
@@ -73,15 +74,28 @@ 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)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this
.(ObjectInternal)
.getOrigin()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
or
not exists(this.(ObjectInternal).getOrigin()) and
filepath = "" and
bl = 0 and
bc = 0 and
el = 0 and
ec = 0
startline = 0 and
startcolumn = 0 and
endline = 0 and
endcolumn = 0
}
/**
@@ -518,7 +532,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,
@@ -539,16 +560,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()
}
@@ -633,6 +664,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() */
@@ -862,6 +897,7 @@ class PropertyValue extends Value {
/** A method-resolution-order sequence of classes */
class MRO extends TClassList {
/** Gets a textual representation of this element. */
string toString() { result = this.(ClassList).toString() }
/** Gets the `n`th class in this MRO */

View File

@@ -17,6 +17,7 @@ import semmle.python.objects.Sequences
import semmle.python.objects.Descriptors
class ObjectInternal extends TObject {
/** Gets a textual representation of this element. */
abstract string toString();
/**

View File

@@ -1,3 +1,5 @@
/** Contains the internal algebraic datatype backing the various values tracked by the points-to implementation. */
import python
private import semmle.python.types.Builtins
private import semmle.python.objects.ObjectInternal
@@ -10,19 +12,19 @@ private import semmle.python.pointsto.PointsToContext
*/
cached
newtype TObject =
/* Builtin class objects */
/** Builtin class objects */
TBuiltinClassObject(Builtin bltn) {
bltn.isClass() and
not bltn = Builtin::unknownType() and
not bltn = Builtin::special("type")
} or
/* Builtin function objects (module members) */
/** Builtin function objects (module members) */
TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or
/* Builtin method objects (class members) */
/** Builtin method objects (class members) */
TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or
/* Builtin module objects */
/** Builtin module objects */
TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or
/* Other builtin objects from the interpreter */
/** Other builtin objects from the interpreter */
TBuiltinOpaqueObject(Builtin bltn) {
not bltn.isClass() and
not bltn.isFunction() and
@@ -34,31 +36,31 @@ newtype TObject =
not exists(bltn.strValue()) and
not py_special_objects(bltn, _)
} or
/* Python function objects (including lambdas) */
/** Python function objects (including lambdas) */
TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or
/* Python class objects */
/** Python class objects */
TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or
/* Package objects */
/** Package objects */
TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or
/* Python module objects */
/** Python module objects */
TPythonModule(Module m) {
not m.isPackage() and
isPreferredModuleForName(m.getFile(), _) and
not exists(SyntaxError se | se.getFile() = m.getFile())
} or
/* `True` */
/** `True` */
TTrue() or
/* `False` */
/** `False` */
TFalse() or
/* `None` */
/** `None` */
TNone() or
/* Represents any value about which nothing useful is known */
/** Represents any value about which nothing useful is known */
TUnknown() or
/* Represents any value known to be a class, but not known to be any specific class */
/** Represents any value known to be a class, but not known to be any specific class */
TUnknownClass() or
/* Represents the absence of a value. Used by points-to for tracking undefined variables */
/** Represents the absence of a value. Used by points-to for tracking undefined variables */
TUndefined() or
/* The integer `n` */
/** The integer `n` */
TInt(int n) {
// Powers of 2 are used for flags
is_power_2(n)
@@ -76,9 +78,9 @@ newtype TObject =
or
n = any(Builtin b).intValue()
} or
/* The float `f` */
/** The float `f` */
TFloat(float f) { f = any(FloatLiteral num).getValue() } or
/* The unicode string `s` */
/** The unicode string `s` */
TUnicode(string s) {
// Any string explicitly mentioned in the source code.
exists(StrConst str |
@@ -94,7 +96,7 @@ newtype TObject =
or
s = "__main__"
} or
/* The byte string `s` */
/** The byte string `s` */
TBytes(string s) {
// Any string explicitly mentioned in the source code.
exists(StrConst str |
@@ -110,74 +112,74 @@ newtype TObject =
or
s = "__main__"
} or
/* An instance of `cls`, instantiated at `instantiation` given the `context`. */
/** An instance of `cls`, instantiated at `instantiation` given the `context`. */
TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) {
PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and
cls.isSpecial() = false
or
literal_instantiation(instantiation, cls, context)
} or
/* A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */
/** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */
TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) {
self_parameter(def, context, cls)
} or
/* A bound method */
/** A bound method */
TBoundMethod(ObjectInternal self, CallableObjectInternal function) {
any(ObjectInternal obj).binds(self, _, function) and
function.isDescriptor() = true
} or
/* Represents any value whose class is known, but nothing else */
/** Represents any value whose class is known, but nothing else */
TUnknownInstance(BuiltinClassObjectInternal cls) {
cls != ObjectInternal::superType() and
cls != ObjectInternal::builtin("bool") and
cls != ObjectInternal::noneType()
} or
/* Represents an instance of `super` */
/** Represents an instance of `super` */
TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) {
super_instantiation(_, self, startclass, _)
} or
/* Represents an instance of `classmethod` */
/** Represents an instance of `classmethod` */
TClassMethod(CallNode instantiation, CallableObjectInternal function) {
class_method(instantiation, function, _)
} or
/* Represents an instance of `staticmethod` */
/** Represents an instance of `staticmethod` */
TStaticMethod(CallNode instantiation, CallableObjectInternal function) {
static_method(instantiation, function, _)
} or
/* Represents a builtin tuple */
/** Represents a builtin tuple */
TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or
/* Represents a tuple in the Python source */
/** Represents a tuple in the Python source */
TPythonTuple(TupleNode origin, PointsToContext context) {
origin.isLoad() and
context.appliesTo(origin)
} or
/* Varargs tuple */
/** Varargs tuple */
TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) {
InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length)
} or
/* `type` */
/** `type` */
TType() or
/* Represents an instance of `property` */
/** Represents an instance of `property` */
TProperty(CallNode call, Context ctx, CallableObjectInternal getter) {
PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and
PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _)
} or
/* Represents the `setter` or `deleter` method of a property object. */
/** Represents the `setter` or `deleter` method of a property object. */
TPropertySetterOrDeleter(PropertyInternal property, string method) {
exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and
(method = "setter" or method = "deleter")
} or
/* Represents a dynamically created class */
/** Represents a dynamically created class */
TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) {
PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and
not count(instantiation.getAnArg()) = 1 and
Types::getMro(metacls).contains(TType())
} or
/* Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */
/** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */
TSysVersionInfo() or
/* Represents a module that is inferred to perhaps exist, but is not present in the database. */
/** Represents a module that is inferred to perhaps exist, but is not present in the database. */
TAbsentModule(string name) { missing_imported_module(_, _, name) } or
/* Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */
/** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */
TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) {
(
PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _)
@@ -189,9 +191,9 @@ newtype TObject =
not common_module_name(modname + "." + attrname)
)
} or
/* Opaque object representing the result of calling a decorator on a function that we don't understand */
/** Opaque object representing the result of calling a decorator on a function that we don't understand */
TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or
/* Represents a subscript operation applied to a type. For type-hint analysis */
/** Represents a subscript operation applied to a type. For type-hint analysis */
TSubscriptedType(ObjectInternal generic, ObjectInternal index) {
isType(generic) and
generic.isNotSubscriptedType() and
@@ -199,6 +201,7 @@ newtype TObject =
Expressions::subscriptPartsPointsTo(_, _, generic, index)
}
/** Holds if the object `t` is a type. */
predicate isType(ObjectInternal t) {
t.isClass() = true
or
@@ -421,7 +424,7 @@ predicate missing_imported_module(ControlFlowNode imp, Context ctx, string name)
)
}
/*
/**
* Helper for missing modules to determine if name `x.y` is a module `x.y` or
* an attribute `y` of module `x`. This list should be added to as required.
*/
@@ -444,6 +447,7 @@ library class ClassDecl extends @py_object {
this.(ControlFlowNode).getNode() instanceof ClassExpr
}
/** Gets a textual representation of this element. */
string toString() { result = "ClassDecl" }
/** Gets the class scope for Python class declarations */

View File

@@ -195,6 +195,7 @@ private newtype TIterationDefinition =
* A definition of a variable in a for loop `for v in ...:`
*/
deprecated class IterationDefinition extends TIterationDefinition {
/** Gets a textual representation of this element. */
string toString() { result = "IterationDefinition" }
ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) }

View File

@@ -28,6 +28,7 @@ private newtype TTInvocation =
* all calls made to a function for a given context.
*/
class FunctionInvocation extends TTInvocation {
/** Gets a textual representation of this element. */
string toString() { result = "Invocation" }
FunctionObject getFunction() { this = TInvocation(result, _) }

View File

@@ -68,6 +68,7 @@ private ClassObjectInternal sole_base(ClassObjectInternal cls) {
/** A list of classes, used to represent the MRO of a class */
class ClassList extends TClassList {
/** Gets a textual representation of this element. */
string toString() { result = "[" + this.contents() + "]" }
string contents() {
@@ -243,6 +244,7 @@ private predicate required_list(ClassList head, ClassListList tail) {
}
private class ClassListList extends TClassListList {
/** Gets a textual representation of this element. */
string toString() { result = "[" + this.contents() + "]" }
string contents() {

View File

@@ -9,6 +9,7 @@ private import semmle.python.types.Extensions
/* Use this version for speed */
library class CfgOrigin extends @py_object {
/** Gets a textual representation of this element. */
string toString() {
/* Not to be displayed */
result = "CfgOrigin"

View File

@@ -129,6 +129,7 @@ module Context {
* * All other contexts are call contexts and consist of a pair of call-site and caller context.
*/
class PointsToContext extends TPointsToContext {
/** Gets a textual representation of this element. */
cached
string toString() {
this = TMainContext() and result = "main"

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
)
}
@@ -472,8 +497,12 @@ abstract class RegexString extends Expr {
this.getChar(endin) = "}" and
end > start and
exists(string multiples | multiples = this.getText().substring(start + 1, endin) |
multiples.regexpMatch("0+") and maybe_empty = true
or
multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true
or
multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false
or
multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false
) and
not exists(int mid |
@@ -618,9 +647,13 @@ abstract class RegexString extends Expr {
start = 0 and end = this.getText().length()
or
exists(int y | this.lastPart(start, y) |
this.emptyMatchAtEndGroup(end, y) or
this.qualifiedItem(end, y, true) or
this.emptyMatchAtEndGroup(end, y)
or
this.qualifiedItem(end, y, true)
or
this.specialCharacter(end, y, "$")
or
y = end + 2 and this.escapingChar(end) and this.getChar(end + 1) = "Z"
)
or
exists(int x |

View File

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

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

@@ -17,6 +17,7 @@ class Builtin extends @py_cobject {
)
}
/** Gets a textual representation of this element. */
string toString() {
not this = undefinedVariable().asBuiltin() and
not this = Builtin::unknown() and

View File

@@ -64,20 +64,29 @@ class Object extends @py_object {
private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) }
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec)
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) { this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
or
not this.hasOrigin() and
filepath = ":Compiled Code" and
bl = 0 and
bc = 0 and
el = 0 and
ec = 0
startline = 0 and
startcolumn = 0 and
endline = 0 and
endcolumn = 0
}
/** INTERNAL -- Do not use */
Builtin asBuiltin() { result = this }
/** Gets a textual representation of this element. */
string toString() {
not this = undefinedVariable() and
not this = unknownValue() and

View File

@@ -63,6 +63,7 @@ class UntrustedCookie extends TaintKind {
}
abstract class CookieOperation extends @py_flow_node {
/** Gets a textual representation of this element. */
abstract string toString();
abstract ControlFlowNode getKey();

View File

@@ -1,6 +1,6 @@
import python
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted
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.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

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

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

View File

@@ -0,0 +1,12 @@
| mapping | builtin-class collections.defaultdict |
| mapping | builtin-class dict |
| mapping | class MyDictSubclass |
| mapping | class MyMappingABC |
| mapping | class OrderedDict |
| neither sequence nor mapping | builtin-class set |
| sequence | builtin-class list |
| sequence | builtin-class str |
| sequence | builtin-class tuple |
| sequence | builtin-class unicode |
| sequence | class MySequenceABC |
| sequence | class MySequenceImpl |

View File

@@ -0,0 +1,20 @@
import python
from ClassValue cls, string res
where
exists(CallNode call |
call.getFunction().(NameNode).getId() = "test" and
call.getAnArg().pointsTo(cls)
) and
(
cls.isSequence() and
cls.isMapping() and
res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE."
or
cls.isSequence() and not cls.isMapping() and res = "sequence"
or
not cls.isSequence() and cls.isMapping() and res = "mapping"
or
not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping"
)
select res, cls.toString()

View File

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

View File

@@ -0,0 +1,50 @@
from collections import OrderedDict, defaultdict
# Python 2 specific
from collections import Sequence, Mapping
def test(*args):
pass
class MySequenceABC(Sequence):
pass
class MyMappingABC(Mapping):
pass
class MySequenceImpl(object):
def __getitem__(self, key):
pass
def __len__(self):
pass
class MyDictSubclass(dict):
pass
test(
list,
tuple,
str,
unicode,
bytes,
MySequenceABC,
MySequenceImpl,
set,
dict,
OrderedDict,
defaultdict,
MyMappingABC,
MyDictSubclass,
)
for seq_cls in (list, tuple, str, bytes):
assert issubclass(seq_cls, collections.abc.Sequence)
assert not issubclass(seq_cls, collections.abc.Mapping)
for map_cls in (dict, OrderedDict, defaultdict):
assert not issubclass(map_cls, collections.abc.Sequence)
assert issubclass(map_cls, collections.abc.Mapping)
assert not issubclass(set, collections.abc.Sequence)
assert not issubclass(set, collections.abc.Mapping)

View File

@@ -0,0 +1,13 @@
| mapping | builtin-class collections.OrderedDict |
| mapping | builtin-class collections.defaultdict |
| mapping | builtin-class dict |
| mapping | class MyDictSubclass |
| mapping | class MyMappingABC |
| mapping | class OrderedDict |
| neither sequence nor mapping | builtin-class set |
| sequence | builtin-class bytes |
| sequence | builtin-class list |
| sequence | builtin-class str |
| sequence | builtin-class tuple |
| sequence | class MySequenceABC |
| sequence | class MySequenceImpl |

View File

@@ -0,0 +1,20 @@
import python
from ClassValue cls, string res
where
exists(CallNode call |
call.getFunction().(NameNode).getId() = "test" and
call.getAnArg().pointsTo(cls)
) and
(
cls.isSequence() and
cls.isMapping() and
res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE."
or
cls.isSequence() and not cls.isMapping() and res = "sequence"
or
not cls.isSequence() and cls.isMapping() and res = "mapping"
or
not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping"
)
select res, cls.toString()

View File

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

View File

@@ -0,0 +1,50 @@
from collections import OrderedDict, defaultdict
# Python 3 specific
from collections.abc import Sequence, Mapping
def test(*args):
pass
class MySequenceABC(Sequence):
pass
class MyMappingABC(Mapping):
pass
class MySequenceImpl(object):
def __getitem__(self, key):
pass
def __len__(self):
pass
class MyDictSubclass(dict):
pass
test(
list,
tuple,
str,
unicode,
bytes,
MySequenceABC,
MySequenceImpl,
set,
dict,
OrderedDict,
defaultdict,
MyMappingABC,
MyDictSubclass,
)
for seq_cls in (list, tuple, str, bytes):
assert issubclass(seq_cls, collections.abc.Sequence)
assert not issubclass(seq_cls, collections.abc.Mapping)
for map_cls in (dict, OrderedDict, defaultdict):
assert not issubclass(map_cls, collections.abc.Sequence)
assert issubclass(map_cls, collections.abc.Mapping)
assert not issubclass(set, collections.abc.Sequence)
assert not issubclass(set, collections.abc.Mapping)

View File

@@ -0,0 +1,47 @@
edges
| xslt.py:10:17:10:28 | dict of etree.XML string | xslt.py:10:17:10:43 | etree.XML string |
| xslt.py:10:17:10:28 | dict of etree.XML string | xslt.py:10:17:10:43 | etree.XML string |
| xslt.py:10:17:10:43 | etree.XML string | xslt.py:11:27:11:35 | etree.XML string |
| xslt.py:10:17:10:43 | etree.XML string | xslt.py:11:27:11:35 | etree.XML string |
| xslt.py:11:17:11:36 | lxml etree xml | xslt.py:14:29:14:37 | lxml etree xml |
| xslt.py:11:17:11:36 | lxml etree xml | xslt.py:14:29:14:37 | lxml etree xml |
| xslt.py:11:27:11:35 | etree.XML string | xslt.py:11:17:11:36 | lxml etree xml |
| xslt.py:11:27:11:35 | etree.XML string | xslt.py:11:17:11:36 | lxml etree xml |
| xsltInjection.py:10:17:10:28 | dict of etree.XML string | xsltInjection.py:10:17:10:43 | etree.XML string |
| xsltInjection.py:10:17:10:28 | dict of etree.XML string | xsltInjection.py:10:17:10:43 | etree.XML string |
| xsltInjection.py:10:17:10:43 | etree.XML string | xsltInjection.py:11:27:11:35 | etree.XML string |
| xsltInjection.py:10:17:10:43 | etree.XML string | xsltInjection.py:11:27:11:35 | etree.XML string |
| xsltInjection.py:11:17:11:36 | lxml etree xml | xsltInjection.py:12:28:12:36 | lxml etree xml |
| xsltInjection.py:11:17:11:36 | lxml etree xml | xsltInjection.py:12:28:12:36 | lxml etree xml |
| xsltInjection.py:11:27:11:35 | etree.XML string | xsltInjection.py:11:17:11:36 | lxml etree xml |
| xsltInjection.py:11:27:11:35 | etree.XML string | xsltInjection.py:11:17:11:36 | lxml etree xml |
| xsltInjection.py:17:17:17:28 | dict of etree.XML string | xsltInjection.py:17:17:17:43 | etree.XML string |
| xsltInjection.py:17:17:17:28 | dict of etree.XML string | xsltInjection.py:17:17:17:43 | etree.XML string |
| xsltInjection.py:17:17:17:43 | etree.XML string | xsltInjection.py:18:27:18:35 | etree.XML string |
| xsltInjection.py:17:17:17:43 | etree.XML string | xsltInjection.py:18:27:18:35 | etree.XML string |
| xsltInjection.py:18:17:18:36 | lxml etree xml | xsltInjection.py:21:29:21:37 | lxml etree xml |
| xsltInjection.py:18:17:18:36 | lxml etree xml | xsltInjection.py:21:29:21:37 | lxml etree xml |
| xsltInjection.py:18:27:18:35 | etree.XML string | xsltInjection.py:18:17:18:36 | lxml etree xml |
| xsltInjection.py:18:27:18:35 | etree.XML string | xsltInjection.py:18:17:18:36 | lxml etree xml |
| xsltInjection.py:26:17:26:28 | dict of etree.XML string | xsltInjection.py:26:17:26:43 | etree.XML string |
| xsltInjection.py:26:17:26:28 | dict of etree.XML string | xsltInjection.py:26:17:26:43 | etree.XML string |
| xsltInjection.py:26:17:26:43 | etree.XML string | xsltInjection.py:27:27:27:35 | etree.XML string |
| xsltInjection.py:26:17:26:43 | etree.XML string | xsltInjection.py:27:27:27:35 | etree.XML string |
| xsltInjection.py:27:17:27:36 | lxml etree xml | xsltInjection.py:31:24:31:32 | lxml etree xml |
| xsltInjection.py:27:17:27:36 | lxml etree xml | xsltInjection.py:31:24:31:32 | lxml etree xml |
| xsltInjection.py:27:27:27:35 | etree.XML string | xsltInjection.py:27:17:27:36 | lxml etree xml |
| xsltInjection.py:27:27:27:35 | etree.XML string | xsltInjection.py:27:17:27:36 | lxml etree xml |
| xsltInjection.py:35:17:35:28 | dict of etree.XML string | xsltInjection.py:35:17:35:43 | etree.XML string |
| xsltInjection.py:35:17:35:28 | dict of etree.XML string | xsltInjection.py:35:17:35:43 | etree.XML string |
| xsltInjection.py:35:17:35:43 | etree.XML string | xsltInjection.py:36:34:36:42 | etree.XML string |
| xsltInjection.py:35:17:35:43 | etree.XML string | xsltInjection.py:36:34:36:42 | etree.XML string |
| xsltInjection.py:36:17:36:43 | lxml etree xml | xsltInjection.py:40:24:40:32 | lxml etree xml |
| xsltInjection.py:36:17:36:43 | lxml etree xml | xsltInjection.py:40:24:40:32 | lxml etree xml |
| xsltInjection.py:36:34:36:42 | etree.XML string | xsltInjection.py:36:17:36:43 | lxml etree xml |
| xsltInjection.py:36:34:36:42 | etree.XML string | xsltInjection.py:36:17:36:43 | lxml etree xml |
#select
| xslt.py:14:29:14:37 | xslt_root | xslt.py:10:17:10:28 | dict of etree.XML string | xslt.py:14:29:14:37 | lxml etree xml | This XSLT query depends on $@. | xslt.py:10:17:10:28 | Attribute | a user-provided value |
| xsltInjection.py:12:28:12:36 | xslt_root | xsltInjection.py:10:17:10:28 | dict of etree.XML string | xsltInjection.py:12:28:12:36 | lxml etree xml | This XSLT query depends on $@. | xsltInjection.py:10:17:10:28 | Attribute | a user-provided value |
| xsltInjection.py:21:29:21:37 | xslt_root | xsltInjection.py:17:17:17:28 | dict of etree.XML string | xsltInjection.py:21:29:21:37 | lxml etree xml | This XSLT query depends on $@. | xsltInjection.py:17:17:17:28 | Attribute | a user-provided value |
| xsltInjection.py:31:24:31:32 | xslt_root | xsltInjection.py:26:17:26:28 | dict of etree.XML string | xsltInjection.py:31:24:31:32 | lxml etree xml | This XSLT query depends on $@. | xsltInjection.py:26:17:26:28 | Attribute | a user-provided value |
| xsltInjection.py:40:24:40:32 | xslt_root | xsltInjection.py:35:17:35:28 | dict of etree.XML string | xsltInjection.py:40:24:40:32 | lxml etree xml | This XSLT query depends on $@. | xsltInjection.py:35:17:35:28 | Attribute | a user-provided value |

View File

@@ -0,0 +1 @@
experimental/CWE-643/Xslt.ql

View File

@@ -0,0 +1,12 @@
| xslt.py:14:29:14:37 | lxml.etree.parse.xslt | lxml etree xml |
| xsltInjection.py:12:28:12:36 | lxml.etree.XSLT | lxml etree xml |
| xsltInjection.py:21:29:21:37 | lxml.etree.parse.xslt | lxml etree xml |
| xsltInjection.py:31:24:31:32 | lxml.etree.parse.xslt | lxml etree xml |
| xsltInjection.py:40:24:40:32 | lxml.etree.parse.xslt | lxml etree xml |
| xsltInjection.py:50:24:50:32 | lxml.etree.parse.xslt | lxml etree xml |
| xsltInjection.py:60:24:60:32 | lxml.etree.parse.xslt | lxml etree xml |
| xsltInjection.py:69:24:69:32 | lxml.etree.parse.xslt | lxml etree xml |
| xsltInjection.py:79:24:79:32 | lxml.etree.parse.xslt | lxml etree xml |
| xsltSinks.py:17:28:17:36 | lxml.etree.XSLT | lxml etree xml |
| xsltSinks.py:30:29:30:37 | lxml.etree.parse.xslt | lxml etree xml |
| xsltSinks.py:44:24:44:32 | lxml.etree.parse.xslt | lxml etree xml |

View File

@@ -0,0 +1,6 @@
import python
import experimental.semmle.python.security.injection.XSLT
from XSLTInjection::XSLTInjectionSink sink, TaintKind kind
where sink.sinks(kind)
select sink, kind

View File

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

View File

@@ -0,0 +1,14 @@
from lxml import etree
from io import StringIO
from flask import Flask, request
app = Flask(__name__)
@app.route("/xslt")
def bad():
xsltQuery = request.args.get('xml', '')
xslt_root = etree.XML(xsltQuery)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result_tree = tree.xslt(xslt_root) # Not OK

View File

@@ -0,0 +1,79 @@
from lxml import etree
from io import StringIO
from flask import Flask, request
app = Flask(__name__)
@app.route("/xslt1")
def a():
xsltQuery = request.args.get('xml', '')
xslt_root = etree.XML(xsltQuery)
transform = etree.XSLT(xslt_root) # Not OK
@app.route("/xslt2")
def b():
xsltQuery = request.args.get('xml', '')
xslt_root = etree.XML(xsltQuery)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result_tree = tree.xslt(xslt_root) # Not OK
@app.route("/xslt3")
def c():
xsltQuery = request.args.get('xml', '')
xslt_root = etree.XML(xsltQuery)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result = tree.xslt(xslt_root, a="'A'") # Not OK
@app.route("/xslt4")
def d():
xsltQuery = request.args.get('xml', '')
xslt_root = etree.fromstring(xsltQuery)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result = tree.xslt(xslt_root, a="'A'") # Not OK
@app.route("/xslt5")
def e():
xsltQuery = request.args.get('xml', '')
xsltStrings = [xsltQuery,"asd","random"]
xslt_root = etree.fromstringlist(xsltStrings)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result = tree.xslt(xslt_root, a="'A'") # Not OK
@app.route("/xslt6")
def f():
xsltQuery = '<non><remote><query></query></remote></non>'
xslt_root = etree.XML(xsltQuery)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result = tree.xslt(xslt_root, a="'A'") # OK
@app.route("/xslt7")
def g():
xsltQuery = '<non><remote><query></query></remote></non>'
xslt_root = etree.fromstring(xsltQuery)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result = tree.xslt(xslt_root, a="'A'") # OK
@app.route("/xslt8")
def h():
xsltQuery = '<non><remote><query></query></remote></non>'
xsltStrings = [xsltQuery,"asd","random"]
xslt_root = etree.fromstringlist(xsltStrings)
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result = tree.xslt(xslt_root, a="'A'") # OK

View File

@@ -0,0 +1,56 @@
from lxml import etree
from io import StringIO
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
xslt_root = etree.XML('''\
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<foo><xsl:value-of select="/a/b/text()" /></foo>
</xsl:template>
</xsl:stylesheet>''')
transform = etree.XSLT(xslt_root)
def b(request):
xslt_root = etree.XML('''\
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<foo><xsl:value-of select="/a/b/text()" /></foo>
</xsl:template>
</xsl:stylesheet>''')
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result_tree = tree.xslt(xslt_root)
def c(request):
xslt_root = etree.XML('''\
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<foo><xsl:value-of select="/a/b/text()" /></foo>
</xsl:template>
</xsl:stylesheet>''')
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
result = tree.xslt(xslt_root, a="'A'")
urlpatterns = [
path('a', a),
path('b', b),
path('c', c)
]
if __name__ == "__main__":
a(None)
b(None)
c(None)

View File

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

View File

@@ -0,0 +1,38 @@
edges
| xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:10:13:10:19 | django.request.HttpRequest |
| xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:10:13:10:19 | django.request.HttpRequest |
| xpathBad.py:10:13:10:19 | django.request.HttpRequest | xpathBad.py:10:13:10:23 | django.http.request.QueryDict |
| xpathBad.py:10:13:10:19 | django.request.HttpRequest | xpathBad.py:10:13:10:23 | django.http.request.QueryDict |
| xpathBad.py:10:13:10:23 | django.http.request.QueryDict | xpathBad.py:10:13:10:32 | externally controlled string |
| xpathBad.py:10:13:10:23 | django.http.request.QueryDict | xpathBad.py:10:13:10:32 | externally controlled string |
| xpathBad.py:10:13:10:32 | externally controlled string | xpathBad.py:13:39:13:43 | externally controlled string |
| xpathBad.py:10:13:10:32 | externally controlled string | xpathBad.py:13:39:13:43 | externally controlled string |
| xpathBad.py:13:39:13:43 | externally controlled string | xpathBad.py:13:20:13:43 | externally controlled string |
| xpathBad.py:13:39:13:43 | externally controlled string | xpathBad.py:13:20:13:43 | externally controlled string |
| xpathFlow.py:11:18:11:29 | dict of externally controlled string | xpathFlow.py:11:18:11:44 | externally controlled string |
| xpathFlow.py:11:18:11:29 | dict of externally controlled string | xpathFlow.py:11:18:11:44 | externally controlled string |
| xpathFlow.py:11:18:11:44 | externally controlled string | xpathFlow.py:14:20:14:29 | externally controlled string |
| xpathFlow.py:11:18:11:44 | externally controlled string | xpathFlow.py:14:20:14:29 | externally controlled string |
| xpathFlow.py:20:18:20:29 | dict of externally controlled string | xpathFlow.py:20:18:20:44 | externally controlled string |
| xpathFlow.py:20:18:20:29 | dict of externally controlled string | xpathFlow.py:20:18:20:44 | externally controlled string |
| xpathFlow.py:20:18:20:44 | externally controlled string | xpathFlow.py:23:29:23:38 | externally controlled string |
| xpathFlow.py:20:18:20:44 | externally controlled string | xpathFlow.py:23:29:23:38 | externally controlled string |
| xpathFlow.py:30:18:30:29 | dict of externally controlled string | xpathFlow.py:30:18:30:44 | externally controlled string |
| xpathFlow.py:30:18:30:29 | dict of externally controlled string | xpathFlow.py:30:18:30:44 | externally controlled string |
| xpathFlow.py:30:18:30:44 | externally controlled string | xpathFlow.py:32:29:32:38 | externally controlled string |
| xpathFlow.py:30:18:30:44 | externally controlled string | xpathFlow.py:32:29:32:38 | externally controlled string |
| xpathFlow.py:39:18:39:29 | dict of externally controlled string | xpathFlow.py:39:18:39:44 | externally controlled string |
| xpathFlow.py:39:18:39:29 | dict of externally controlled string | xpathFlow.py:39:18:39:44 | externally controlled string |
| xpathFlow.py:39:18:39:44 | externally controlled string | xpathFlow.py:41:31:41:40 | externally controlled string |
| xpathFlow.py:39:18:39:44 | externally controlled string | xpathFlow.py:41:31:41:40 | externally controlled string |
| xpathFlow.py:47:18:47:29 | dict of externally controlled string | xpathFlow.py:47:18:47:44 | externally controlled string |
| xpathFlow.py:47:18:47:29 | dict of externally controlled string | xpathFlow.py:47:18:47:44 | externally controlled string |
| xpathFlow.py:47:18:47:44 | externally controlled string | xpathFlow.py:49:29:49:38 | externally controlled string |
| xpathFlow.py:47:18:47:44 | externally controlled string | xpathFlow.py:49:29:49:38 | externally controlled string |
#select
| xpathBad.py:13:20:13:43 | BinaryExpr | xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:13:20:13:43 | externally controlled string | This Xpath query depends on $@. | xpathBad.py:9:7:9:13 | request | a user-provided value |
| xpathFlow.py:14:20:14:29 | xpathQuery | xpathFlow.py:11:18:11:29 | dict of externally controlled string | xpathFlow.py:14:20:14:29 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:11:18:11:29 | Attribute | a user-provided value |
| xpathFlow.py:23:29:23:38 | xpathQuery | xpathFlow.py:20:18:20:29 | dict of externally controlled string | xpathFlow.py:23:29:23:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:20:18:20:29 | Attribute | a user-provided value |
| xpathFlow.py:32:29:32:38 | xpathQuery | xpathFlow.py:30:18:30:29 | dict of externally controlled string | xpathFlow.py:32:29:32:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:30:18:30:29 | Attribute | a user-provided value |
| xpathFlow.py:41:31:41:40 | xpathQuery | xpathFlow.py:39:18:39:29 | dict of externally controlled string | xpathFlow.py:41:31:41:40 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:39:18:39:29 | Attribute | a user-provided value |
| xpathFlow.py:49:29:49:38 | xpathQuery | xpathFlow.py:47:18:47:29 | dict of externally controlled string | xpathFlow.py:49:29:49:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:47:18:47:29 | Attribute | a user-provided value |

View File

@@ -0,0 +1,40 @@
from lxml import etree
from io import StringIO
def a():
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath('/foo/bar')
def b():
root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.XPath("//text()")
text = find_text(root)[0]
def c():
root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.XPath("//text()", smart_strings=False)
text = find_text(root)[0]
def d():
root = etree.XML("<root><a>TEXT</a></root>")
find_text = find = etree.ETXPath("//{ns}b")
text = find_text(root)[0]
def e():
import libxml2
doc = libxml2.parseFile('xpath_injection/credential.xml')
results = doc.xpathEval('sink')
if __name__ == "__main__":
a()
b()
c()
d()
e()

View File

@@ -0,0 +1 @@
experimental/CWE-643/xpath.ql

View File

@@ -0,0 +1,18 @@
from lxml import etree
from io import StringIO
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath("/tag[@id='%s']" % value)
urlpatterns = [
path('a', a)
]

View File

@@ -0,0 +1,49 @@
from io import StringIO
from flask import Flask, request
app = Flask(__name__)
@app.route("/xpath1")
def xpath1():
from lxml import etree
xpathQuery = request.args.get('xml', '')
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath(xpathQuery)
@app.route("/xpath2")
def xpath2():
from lxml import etree
xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.XPath(xpathQuery)
text = find_text(root)[0]
@app.route("/xpath3")
def xpath3():
from lxml import etree
xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.XPath(xpathQuery, smart_strings=False)
text = find_text(root)[0]
@app.route("/xpath4")
def xpath4():
from lxml import etree
xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.ETXPath(xpathQuery)
text = find_text(root)[0]
@app.route("/xpath5")
def xpath5():
import libxml2
xpathQuery = request.args.get('xml', '')
doc = libxml2.parseFile('xpath_injection/credential.xml')
results = doc.xpathEval(xpathQuery)

View File

@@ -0,0 +1,18 @@
from lxml import etree
from io import StringIO
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath("/tag[@id=$tagid]", tagid=value)
urlpatterns = [
path('a', a)
]

View File

@@ -0,0 +1,12 @@
| xpath.py:8:20:8:29 | lxml.etree.parse.xpath | externally controlled string |
| xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string |
| xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string |
| xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string |
| xpath.py:32:29:32:34 | libxml2.parseFile.xpathEval | externally controlled string |
| xpathBad.py:13:20:13:43 | lxml.etree.parse.xpath | externally controlled string |
| xpathFlow.py:14:20:14:29 | lxml.etree.parse.xpath | externally controlled string |
| xpathFlow.py:23:29:23:38 | lxml.etree.Xpath | externally controlled string |
| xpathFlow.py:32:29:32:38 | lxml.etree.Xpath | externally controlled string |
| xpathFlow.py:41:31:41:40 | lxml.etree.ETXpath | externally controlled string |
| xpathFlow.py:49:29:49:38 | libxml2.parseFile.xpathEval | externally controlled string |
| xpathGood.py:13:20:13:37 | lxml.etree.parse.xpath | externally controlled string |

View File

@@ -0,0 +1,6 @@
import python
import experimental.semmle.python.security.injection.Xpath
from XpathInjection::XpathInjectionSink sink, TaintKind kind
where sink.sinks(kind)
select sink, kind

Some files were not shown because too many files have changed in this diff Show More