mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'master' into python-keyword-only-args
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, _) }
|
||||
}
|
||||
|
||||
@@ -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__" |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isExtension(TaintTracking::Extension extension) {
|
||||
extension instanceof FirstElementFlow
|
||||
or
|
||||
extension instanceof FabricExecuteExtension
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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]])
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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) |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
- description: Security-and-quality queries for Python
|
||||
- qlpack: codeql-python
|
||||
- apply: security-and-quality-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
4
python/ql/src/codeql-suites/python-security-extended.qls
Normal file
4
python/ql/src/codeql-suites/python-security-extended.qls
Normal file
@@ -0,0 +1,4 @@
|
||||
- description: Security-extended queries for Python
|
||||
- qlpack: codeql-python
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
18
python/ql/src/experimental/CWE-091/Xslt.qhelp
Normal file
18
python/ql/src/experimental/CWE-091/Xslt.qhelp
Normal 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>
|
||||
35
python/ql/src/experimental/CWE-091/Xslt.ql
Normal file
35
python/ql/src/experimental/CWE-091/Xslt.ql
Normal 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"
|
||||
14
python/ql/src/experimental/CWE-091/xslt.py
Normal file
14
python/ql/src/experimental/CWE-091/xslt.py
Normal 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
|
||||
30
python/ql/src/experimental/CWE-643/xpath.qhelp
Normal file
30
python/ql/src/experimental/CWE-643/xpath.qhelp
Normal 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 can’t 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 user’s input has been added to the string. This is a better route because you don’t 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>
|
||||
35
python/ql/src/experimental/CWE-643/xpath.ql
Normal file
35
python/ql/src/experimental/CWE-643/xpath.ql
Normal 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"
|
||||
18
python/ql/src/experimental/CWE-643/xpathBad.py
Normal file
18
python/ql/src/experimental/CWE-643/xpathBad.py
Normal 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)
|
||||
]
|
||||
18
python/ql/src/experimental/CWE-643/xpathGood.py
Normal file
18
python/ql/src/experimental/CWE-643/xpathGood.py
Normal 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)
|
||||
]
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
2
python/ql/src/external/CodeDuplication.qll
vendored
2
python/ql/src/external/CodeDuplication.qll
vendored
@@ -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)
|
||||
}
|
||||
|
||||
17
python/ql/src/external/DefectFilter.qll
vendored
17
python/ql/src/external/DefectFilter.qll
vendored
@@ -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. */
|
||||
|
||||
21
python/ql/src/external/ExternalArtifact.qll
vendored
21
python/ql/src/external/ExternalArtifact.qll
vendored
@@ -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
|
||||
|
||||
37
python/ql/src/external/Thrift.qll
vendored
37
python/ql/src/external/Thrift.qll
vendored
@@ -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 }
|
||||
|
||||
1
python/ql/src/external/VCS.qll
vendored
1
python/ql/src/external/VCS.qll
vendored
@@ -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, _, _, _) }
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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)))) }
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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();
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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, _) }
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -27,5 +27,5 @@ class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof StringDictKind }
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind }
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 |
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=2 --max-import-depth=2
|
||||
@@ -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)
|
||||
@@ -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 |
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=2
|
||||
@@ -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)
|
||||
47
python/ql/test/experimental/CWE-091/Xslt.expected
Normal file
47
python/ql/test/experimental/CWE-091/Xslt.expected
Normal 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 |
|
||||
1
python/ql/test/experimental/CWE-091/Xslt.qlref
Normal file
1
python/ql/test/experimental/CWE-091/Xslt.qlref
Normal file
@@ -0,0 +1 @@
|
||||
experimental/CWE-643/Xslt.ql
|
||||
12
python/ql/test/experimental/CWE-091/XsltSinks.expected
Normal file
12
python/ql/test/experimental/CWE-091/XsltSinks.expected
Normal 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 |
|
||||
6
python/ql/test/experimental/CWE-091/XsltSinks.ql
Normal file
6
python/ql/test/experimental/CWE-091/XsltSinks.ql
Normal 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
|
||||
1
python/ql/test/experimental/CWE-091/options
Normal file
1
python/ql/test/experimental/CWE-091/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: -p ../../query-tests/Security/lib/ --max-import-depth=3
|
||||
14
python/ql/test/experimental/CWE-091/xslt.py
Normal file
14
python/ql/test/experimental/CWE-091/xslt.py
Normal 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
|
||||
79
python/ql/test/experimental/CWE-091/xsltInjection.py
Normal file
79
python/ql/test/experimental/CWE-091/xsltInjection.py
Normal 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
|
||||
56
python/ql/test/experimental/CWE-091/xsltSinks.py
Normal file
56
python/ql/test/experimental/CWE-091/xsltSinks.py
Normal 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)
|
||||
1
python/ql/test/experimental/CWE-643/options
Normal file
1
python/ql/test/experimental/CWE-643/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=3 -p ../../query-tests/Security/lib/
|
||||
38
python/ql/test/experimental/CWE-643/xpath.expected
Normal file
38
python/ql/test/experimental/CWE-643/xpath.expected
Normal 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 |
|
||||
40
python/ql/test/experimental/CWE-643/xpath.py
Normal file
40
python/ql/test/experimental/CWE-643/xpath.py
Normal 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()
|
||||
1
python/ql/test/experimental/CWE-643/xpath.qlref
Normal file
1
python/ql/test/experimental/CWE-643/xpath.qlref
Normal file
@@ -0,0 +1 @@
|
||||
experimental/CWE-643/xpath.ql
|
||||
18
python/ql/test/experimental/CWE-643/xpathBad.py
Normal file
18
python/ql/test/experimental/CWE-643/xpathBad.py
Normal 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)
|
||||
]
|
||||
49
python/ql/test/experimental/CWE-643/xpathFlow.py
Normal file
49
python/ql/test/experimental/CWE-643/xpathFlow.py
Normal 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)
|
||||
18
python/ql/test/experimental/CWE-643/xpathGood.py
Normal file
18
python/ql/test/experimental/CWE-643/xpathGood.py
Normal 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)
|
||||
]
|
||||
12
python/ql/test/experimental/CWE-643/xpathSinks.expected
Normal file
12
python/ql/test/experimental/CWE-643/xpathSinks.expected
Normal 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 |
|
||||
6
python/ql/test/experimental/CWE-643/xpathSinks.ql
Normal file
6
python/ql/test/experimental/CWE-643/xpathSinks.ql
Normal 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
Reference in New Issue
Block a user