mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Merge branch 'master' into python-iter-str-seq-with-tests
This commit is contained in:
@@ -22,9 +22,9 @@ predicate does_not_define_special_method(Class cls) {
|
||||
}
|
||||
|
||||
predicate no_inheritance(Class c) {
|
||||
not exists(ClassObject cls, ClassObject other |
|
||||
cls.getPyClass() = c and
|
||||
other != theObjectType()
|
||||
not exists(ClassValue cls, ClassValue other |
|
||||
cls.getScope() = c and
|
||||
other != ClassValue::object()
|
||||
|
|
||||
other.getABaseType() = cls or
|
||||
cls.getABaseType() = other
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
import python
|
||||
import Expressions.CallArgs
|
||||
|
||||
from Call call, ClassObject cls, string name, FunctionObject init
|
||||
from Call call, ClassValue cls, string name, FunctionValue init
|
||||
where
|
||||
illegally_named_parameter_objectapi(call, cls, name) and
|
||||
init = get_function_or_initializer_objectapi(cls)
|
||||
illegally_named_parameter(call, cls, name) and
|
||||
init = get_function_or_initializer(cls)
|
||||
select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init,
|
||||
init.getQualifiedName()
|
||||
|
||||
@@ -15,17 +15,17 @@
|
||||
import python
|
||||
import Expressions.CallArgs
|
||||
|
||||
from Call call, ClassObject cls, string too, string should, int limit, FunctionObject init
|
||||
from Call call, ClassValue cls, string too, string should, int limit, FunctionValue init
|
||||
where
|
||||
(
|
||||
too_many_args_objectapi(call, cls, limit) and
|
||||
too_many_args(call, cls, limit) and
|
||||
too = "too many arguments" and
|
||||
should = "no more than "
|
||||
or
|
||||
too_few_args_objectapi(call, cls, limit) and
|
||||
too_few_args(call, cls, limit) and
|
||||
too = "too few arguments" and
|
||||
should = "no fewer than "
|
||||
) and
|
||||
init = get_function_or_initializer_objectapi(cls)
|
||||
init = get_function_or_initializer(cls)
|
||||
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init,
|
||||
init.getQualifiedName()
|
||||
|
||||
@@ -154,14 +154,17 @@ predicate too_few_args(Call call, Value callable, int limit) {
|
||||
not exists(call.getKwargs()) and
|
||||
arg_count(call) < limit and
|
||||
exists(FunctionValue func | func = get_function_or_initializer(callable) |
|
||||
call = func.getACall().getNode() and
|
||||
call = func.getAFunctionCall().getNode() and
|
||||
limit = func.minParameters() and
|
||||
// The combination of misuse of `mox.Mox().StubOutWithMock()`
|
||||
// and a bug in mox's implementation of methods results in having to
|
||||
// pass 1 too few arguments to the mocked function.
|
||||
/*
|
||||
* The combination of misuse of `mox.Mox().StubOutWithMock()`
|
||||
* and a bug in mox's implementation of methods results in having to
|
||||
* pass 1 too few arguments to the mocked function.
|
||||
*/
|
||||
|
||||
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
|
||||
or
|
||||
call = func.getACall().getNode() and limit = func.minParameters() - 1
|
||||
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
call.getAFlowNode() = get_a_call(callable) and
|
||||
@@ -198,9 +201,9 @@ predicate too_many_args(Call call, Value callable, int limit) {
|
||||
not func.getScope().hasVarArg() and
|
||||
limit >= 0
|
||||
|
|
||||
call = func.getACall().getNode() and limit = func.maxParameters()
|
||||
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
|
||||
or
|
||||
call = func.getACall().getNode() and limit = func.maxParameters() - 1
|
||||
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
call.getAFlowNode() = get_a_call(callable) and
|
||||
@@ -254,3 +257,8 @@ predicate overridden_call(FunctionValue func, FunctionValue overriding, Call cal
|
||||
overriding.overrides(func) and
|
||||
overriding.getACall().getNode() = call
|
||||
}
|
||||
|
||||
/** Holds if `func` will raise a `NotImplemented` error. */
|
||||
predicate isAbstract(FunctionValue func) {
|
||||
func.getARaisedType() = ClassValue::notImplementedError()
|
||||
}
|
||||
|
||||
@@ -14,22 +14,17 @@
|
||||
import python
|
||||
import CallArgs
|
||||
|
||||
from Call call, FunctionObject func, string too, string should, int limit
|
||||
from Call call, FunctionValue func, string too, string should, int limit
|
||||
where
|
||||
(
|
||||
too_many_args_objectapi(call, func, limit) and
|
||||
too = "too many arguments" and
|
||||
should = "no more than "
|
||||
or
|
||||
too_few_args_objectapi(call, func, limit) and
|
||||
too = "too few arguments" and
|
||||
should = "no fewer than "
|
||||
) and
|
||||
not func.isAbstract() and
|
||||
not exists(FunctionObject overridden |
|
||||
func.overrides(overridden) and correct_args_if_called_as_method_objectapi(call, overridden)
|
||||
) and
|
||||
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
|
||||
not func.getName() = "__new__"
|
||||
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func,
|
||||
func.descriptiveString()
|
||||
(
|
||||
too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than "
|
||||
or
|
||||
too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than "
|
||||
) and
|
||||
not isAbstract(func) and
|
||||
not exists(FunctionValue overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden))
|
||||
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
|
||||
and not func.getName() = "__new__"
|
||||
|
||||
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, func.descriptiveString()
|
||||
|
||||
|
||||
@@ -12,17 +12,17 @@
|
||||
import python
|
||||
import Expressions.CallArgs
|
||||
|
||||
from Call call, FunctionObject func, FunctionObject overridden, string problem
|
||||
from Call call, FunctionValue func, FunctionValue overridden, string problem
|
||||
where
|
||||
func.overrides(overridden) and
|
||||
(
|
||||
wrong_args_objectapi(call, func, _, problem) and
|
||||
correct_args_if_called_as_method_objectapi(call, overridden)
|
||||
wrong_args(call, func, _, problem) and
|
||||
correct_args_if_called_as_method(call, overridden)
|
||||
or
|
||||
exists(string name |
|
||||
illegally_named_parameter_objectapi(call, func, name) and
|
||||
illegally_named_parameter(call, func, name) and
|
||||
problem = "an argument named '" + name + "'" and
|
||||
overridden.getFunction().getAnArg().(Name).getId() = name
|
||||
overridden.getScope().getAnArg().(Name).getId() = name
|
||||
)
|
||||
)
|
||||
select func,
|
||||
|
||||
@@ -13,21 +13,21 @@
|
||||
import python
|
||||
import Expressions.CallArgs
|
||||
|
||||
from Call call, FunctionObject func, FunctionObject overriding, string problem
|
||||
from Call call, FunctionValue func, FunctionValue overriding, string problem
|
||||
where
|
||||
not func.getName() = "__init__" and
|
||||
overriding.overrides(func) and
|
||||
call = overriding.getAMethodCall().getNode() and
|
||||
correct_args_if_called_as_method_objectapi(call, overriding) and
|
||||
correct_args_if_called_as_method(call, overriding) and
|
||||
(
|
||||
arg_count_objectapi(call) + 1 < func.minParameters() and problem = "too few arguments"
|
||||
arg_count(call) + 1 < func.minParameters() and problem = "too few arguments"
|
||||
or
|
||||
arg_count_objectapi(call) >= func.maxParameters() and problem = "too many arguments"
|
||||
arg_count(call) >= func.maxParameters() and problem = "too many arguments"
|
||||
or
|
||||
exists(string name |
|
||||
call.getAKeyword().getArg() = name and
|
||||
overriding.getFunction().getAnArg().(Name).getId() = name and
|
||||
not func.getFunction().getAnArg().(Name).getId() = name and
|
||||
overriding.getScope().getAnArg().(Name).getId() = name and
|
||||
not func.getScope().getAnArg().(Name).getId() = name and
|
||||
problem = "an argument named '" + name + "'"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -12,18 +12,10 @@
|
||||
|
||||
import python
|
||||
|
||||
ClassObject return_type(FunctionObject f) {
|
||||
exists(ControlFlowNode n, Return ret |
|
||||
ret.getScope() = f.getFunction() and
|
||||
ret.getValue() = n.getNode() and
|
||||
n.refersTo(_, result, _)
|
||||
)
|
||||
}
|
||||
|
||||
from ClassObject iterable, FunctionObject iter, ClassObject iterator
|
||||
from ClassValue iterable, FunctionValue iter, ClassValue iterator
|
||||
where
|
||||
iter = iterable.lookupAttribute("__iter__") and
|
||||
iterator = return_type(iter) and
|
||||
iter = iterable.lookup("__iter__") and
|
||||
iterator = iter.getAnInferredReturnType() and
|
||||
not iterator.isIterator()
|
||||
select iterator,
|
||||
"Class " + iterator.getName() +
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
|
||||
import python
|
||||
|
||||
from FunctionObject method
|
||||
from FunctionValue method
|
||||
where
|
||||
exists(ClassObject c |
|
||||
exists(ClassValue c |
|
||||
c.declaredAttribute("__del__") = method and
|
||||
method.getFunction().getMetrics().getCyclomaticComplexity() > 3
|
||||
method.getScope().getMetrics().getCyclomaticComplexity() > 3
|
||||
)
|
||||
select method, "Overly complex '__del__' method."
|
||||
|
||||
@@ -62,7 +62,7 @@ private string doctest_in_scope(Scope scope) {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private string typehint_annotation_in_file(File file) {
|
||||
private string typehint_annotation_in_module(Module module_scope) {
|
||||
exists(StrConst annotation |
|
||||
annotation = any(Arguments a).getAnAnnotation().getASubExpression*()
|
||||
or
|
||||
@@ -71,7 +71,7 @@ private string typehint_annotation_in_file(File file) {
|
||||
annotation = any(FunctionExpr f).getReturns().getASubExpression*()
|
||||
|
|
||||
annotation.pointsTo(Value::forString(result)) and
|
||||
file = annotation.getLocation().getFile()
|
||||
annotation.getEnclosingModule() = module_scope
|
||||
)
|
||||
}
|
||||
|
||||
@@ -84,17 +84,19 @@ private string typehint_comment_in_file(File file) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate imported_module_used_in_typehint(Import imp) {
|
||||
exists(string modname, File file |
|
||||
imp.getAName().getAsname().(Name).getId() = modname and
|
||||
file = imp.getScope().(Module).getFile()
|
||||
/** Holds if the imported alias `name` from `imp` is used in a typehint (in the same file as `imp`) */
|
||||
predicate imported_alias_used_in_typehint(Import imp, Variable name) {
|
||||
imp.getAName().getAsname().(Name).getVariable() = name and
|
||||
exists(File file, Module module_scope |
|
||||
module_scope = imp.getEnclosingModule() and
|
||||
file = module_scope.getFile()
|
||||
|
|
||||
// Look for type hints containing the patterns:
|
||||
// # type: …name…
|
||||
typehint_comment_in_file(file).regexpMatch("# type:.*" + modname + ".*")
|
||||
typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*")
|
||||
or
|
||||
// Type hint is inside a string annotation, as needed for forward references
|
||||
typehint_annotation_in_file(file).regexpMatch(".*\\b" + modname + "\\b.*")
|
||||
typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -114,7 +116,7 @@ predicate unused_import(Import imp, Variable name) {
|
||||
// Assume that opaque `__all__` includes imported module
|
||||
not all_not_understood(imp.getEnclosingModule()) and
|
||||
not imported_module_used_in_doctest(imp) and
|
||||
not imported_module_used_in_typehint(imp) and
|
||||
not imported_alias_used_in_typehint(imp, name) and
|
||||
// Only consider import statements that actually point-to something (possibly an unknown module).
|
||||
// If this is not the case, it's likely that the import statement never gets executed.
|
||||
imp.getAName().getValue().pointsTo(_)
|
||||
|
||||
@@ -79,7 +79,7 @@ predicate def_is_open(EssaDefinition def, ControlFlowNode open) {
|
||||
passes_open_files(refinement)
|
||||
)
|
||||
or
|
||||
exists(PyNodeRefinement refinement | refinement = def |
|
||||
exists(EssaNodeRefinement refinement | refinement = def |
|
||||
not closes_file(def) and
|
||||
not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and
|
||||
var_is_open(refinement.getInput(), open)
|
||||
|
||||
2
python/ql/src/external/CodeDuplication.qll
vendored
2
python/ql/src/external/CodeDuplication.qll
vendored
@@ -66,7 +66,7 @@ class Copy extends @duplication_or_similarity {
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
sourceFile().getName() = filepath and
|
||||
sourceFile().getAbsolutePath() = filepath and
|
||||
startline = sourceStartLine() and
|
||||
startcolumn = sourceStartColumn() and
|
||||
endline = sourceEndLine() and
|
||||
|
||||
4
python/ql/src/external/DefectFilter.qll
vendored
4
python/ql/src/external/DefectFilter.qll
vendored
@@ -26,7 +26,7 @@ class DefectResult extends int {
|
||||
|
||||
/** Gets the file in which this query result was reported. */
|
||||
File getFile() {
|
||||
exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getName() = path)
|
||||
exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path)
|
||||
}
|
||||
|
||||
/** Gets the file path in which this query result was reported. */
|
||||
@@ -54,7 +54,7 @@ class DefectResult extends int {
|
||||
/** Gets the URL corresponding to the location of this query result. */
|
||||
string getURL() {
|
||||
result =
|
||||
"file://" + getFile().getName() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
|
||||
"file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
|
||||
getEndLine() + ":" + getEndColumn()
|
||||
}
|
||||
}
|
||||
|
||||
2
python/ql/src/external/VCS.qll
vendored
2
python/ql/src/external/VCS.qll
vendored
@@ -23,7 +23,7 @@ class Commit extends @svnentry {
|
||||
string getMessage() { svnentrymsg(this, result) }
|
||||
|
||||
string getAnAffectedFilePath(string action) {
|
||||
exists(File rawFile | svnaffectedfiles(this, rawFile, action) | result = rawFile.getName())
|
||||
exists(File rawFile | svnaffectedfiles(this, rawFile, action) | result = rawFile.getAbsolutePath())
|
||||
}
|
||||
|
||||
string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) }
|
||||
|
||||
@@ -2,3 +2,4 @@ name: codeql-python
|
||||
version: 0.0.0
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
suites: codeql-suites
|
||||
extractor: python
|
||||
|
||||
@@ -11,7 +11,7 @@ class OpenFileConfiguration extends TaintTracking::Configuration {
|
||||
OpenFileConfiguration() { this = "Open file configuration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src, TaintKind kind) {
|
||||
theOpenFunction().(FunctionObject).getACall() = src.asCfgNode() and
|
||||
src.asCfgNode() = Value::named("open").getACall() and
|
||||
kind instanceof OpenFile
|
||||
}
|
||||
|
||||
|
||||
@@ -131,15 +131,6 @@ abstract class TaintKind extends string {
|
||||
edgeLabel = "custom taint flow step for " + this
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED -- Use `TaintFlow.additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind)` instead.
|
||||
*
|
||||
* Holds if this kind of taint passes from variable `fromvar` to variable `tovar`
|
||||
* This predicate is present for completeness. It is unlikely that any `TaintKind`
|
||||
* implementation will ever need to override it.
|
||||
*/
|
||||
deprecated predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this kind of taint "taints" `expr`.
|
||||
*/
|
||||
@@ -155,7 +146,7 @@ abstract class TaintKind extends string {
|
||||
* For example, if this were a kind of string taint
|
||||
* the `result` would be `theStrType()`.
|
||||
*/
|
||||
ClassValue getType() { result.(ClassObjectInternal).getSource() = this.getClass() }
|
||||
ClassValue getType() { none() }
|
||||
|
||||
/**
|
||||
* Gets the boolean values (may be one, neither, or both) that
|
||||
@@ -180,7 +171,10 @@ abstract class TaintKind extends string {
|
||||
TaintKind getTaintForIteration() { none() }
|
||||
|
||||
predicate flowStep(DataFlow::Node fromnode, DataFlow::Node tonode, string edgeLabel) {
|
||||
this.additionalFlowStepVar(fromnode.asVariable(), tonode.asVariable()) and
|
||||
exists(DataFlowExtension::DataFlowVariable v |
|
||||
v = fromnode.asVariable() and
|
||||
v.getASuccessorVariable() = tonode.asVariable()
|
||||
) and
|
||||
edgeLabel = "custom taint variable step"
|
||||
}
|
||||
}
|
||||
@@ -355,41 +349,6 @@ abstract class Sanitizer extends string {
|
||||
predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED -- Use DataFlowExtension instead.
|
||||
* An extension to taint-flow. For adding library or framework specific flows.
|
||||
* Examples include flow from a request to untrusted part of that request or
|
||||
* from a socket to data from that socket.
|
||||
*/
|
||||
abstract deprecated class TaintFlow extends string {
|
||||
bindingset[this]
|
||||
TaintFlow() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `fromnode` being tainted with `fromkind` will result in `tonode` being tainted with `tokind`.
|
||||
* Extensions to `TaintFlow` should override this to provide additional taint steps.
|
||||
*/
|
||||
predicate additionalFlowStep(
|
||||
ControlFlowNode fromnode, TaintKind fromkind, ControlFlowNode tonode, TaintKind tokind
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `kind` of taint passes from variable `fromvar` to variable `tovar`.
|
||||
* This predicate is present for completeness. Most `TaintFlow` implementations will not need to override it.
|
||||
*/
|
||||
predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `kind` of taint cannot pass from variable `fromvar` to variable `tovar`.
|
||||
* This predicate is present for completeness. Most `TaintFlow` implementations will not need to override it.
|
||||
*/
|
||||
predicate prunedFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of taintedness.
|
||||
* Users of the taint tracking library should override this
|
||||
@@ -659,7 +618,7 @@ module DataFlow {
|
||||
}
|
||||
}
|
||||
|
||||
private class ConfigurationAdapter extends TaintTracking::Configuration {
|
||||
deprecated private class ConfigurationAdapter extends TaintTracking::Configuration {
|
||||
ConfigurationAdapter() { this instanceof Configuration }
|
||||
|
||||
override predicate isSource(DataFlow::Node node, TaintKind kind) {
|
||||
@@ -727,7 +686,7 @@ module DataFlow {
|
||||
}
|
||||
}
|
||||
|
||||
private class DataFlowType extends TaintKind {
|
||||
deprecated private class DataFlowType extends TaintKind {
|
||||
DataFlowType() {
|
||||
this = "Data flow" and
|
||||
exists(DataFlow::Configuration c)
|
||||
|
||||
@@ -170,7 +170,7 @@ string from_mako_import(Module m) {
|
||||
/** File generated by Google's protobuf tool. */
|
||||
class ProtobufGeneratedFile extends SpecificGeneratedFile {
|
||||
ProtobufGeneratedFile() {
|
||||
this.getName().regexpMatch(".*_pb2?.py") and
|
||||
this.getAbsolutePath().regexpMatch(".*_pb2?.py") and
|
||||
exists(Module m | m.getFile() = this |
|
||||
exists(ImportExpr imp | imp.getEnclosingModule() = m |
|
||||
imp.getImportedModuleName() = "google.net.proto2.python.public"
|
||||
|
||||
@@ -540,10 +540,10 @@ class ClassValue extends Value {
|
||||
Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) }
|
||||
|
||||
/**
|
||||
* Holds if this class has the attribute `name`, including
|
||||
* attributes declared by super classes.
|
||||
* Holds if this class has the attribute `name`, including attributes
|
||||
* declared by super classes.
|
||||
*/
|
||||
predicate hasAttribute(string name) { this.getMro().declares(name) }
|
||||
override predicate hasAttribute(string name) { this.getMro().declares(name) }
|
||||
|
||||
/**
|
||||
* Holds if this class declares the attribute `name`,
|
||||
@@ -573,6 +573,9 @@ class ClassValue extends Value {
|
||||
abstract class FunctionValue extends CallableValue {
|
||||
abstract string getQualifiedName();
|
||||
|
||||
/** Gets a longer, more descriptive version of toString() */
|
||||
abstract string descriptiveString();
|
||||
|
||||
/** Gets the minimum number of parameters that can be correctly passed to this function */
|
||||
abstract int minParameters();
|
||||
|
||||
@@ -605,6 +608,20 @@ abstract class FunctionValue extends CallableValue {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a class that may be raised by this function */
|
||||
abstract ClassValue getARaisedType();
|
||||
|
||||
/** Gets a call-site from where this function is called as a function */
|
||||
CallNode getAFunctionCall() { result.getFunction().pointsTo() = this }
|
||||
|
||||
/** Gets a call-site from where this function is called as a method */
|
||||
CallNode getAMethodCall() {
|
||||
exists(BoundMethodObjectInternal bm |
|
||||
result.getFunction().pointsTo() = bm and
|
||||
bm.getFunction() = this
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a class that this function may return */
|
||||
abstract ClassValue getAnInferredReturnType();
|
||||
}
|
||||
@@ -617,6 +634,15 @@ class PythonFunctionValue extends FunctionValue {
|
||||
result = this.(PythonFunctionObjectInternal).getScope().getQualifiedName()
|
||||
}
|
||||
|
||||
override string descriptiveString() {
|
||||
if this.getScope().isMethod()
|
||||
then
|
||||
exists(Class cls | this.getScope().getScope() = cls |
|
||||
result = "method " + this.getQualifiedName()
|
||||
)
|
||||
else result = "function " + this.getQualifiedName()
|
||||
}
|
||||
|
||||
override int minParameters() {
|
||||
exists(Function f |
|
||||
f = this.getScope() and
|
||||
@@ -636,6 +662,8 @@ class PythonFunctionValue extends FunctionValue {
|
||||
/** Gets a control flow node corresponding to a return statement in this function */
|
||||
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
|
||||
|
||||
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
|
||||
|
||||
override ClassValue getAnInferredReturnType() {
|
||||
/* We have to do a special version of this because builtin functions have no
|
||||
* explicit return nodes that we can query and get the class of.
|
||||
@@ -650,10 +678,17 @@ class BuiltinFunctionValue extends FunctionValue {
|
||||
|
||||
override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() }
|
||||
|
||||
override string descriptiveString() { result = "builtin-function " + this.getName() }
|
||||
|
||||
override int minParameters() { none() }
|
||||
|
||||
override int maxParameters() { none() }
|
||||
|
||||
override ClassValue getARaisedType() {
|
||||
/* Information is unavailable for C code in general */
|
||||
none()
|
||||
}
|
||||
|
||||
override ClassValue getAnInferredReturnType() {
|
||||
/* We have to do a special version of this because builtin functions have no
|
||||
* explicit return nodes that we can query and get the class of.
|
||||
@@ -674,10 +709,17 @@ class BuiltinMethodValue extends FunctionValue {
|
||||
)
|
||||
}
|
||||
|
||||
override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() }
|
||||
|
||||
override int minParameters() { none() }
|
||||
|
||||
override int maxParameters() { none() }
|
||||
|
||||
override ClassValue getARaisedType() {
|
||||
/* Information is unavailable for C code in general */
|
||||
none()
|
||||
}
|
||||
|
||||
override ClassValue getAnInferredReturnType() {
|
||||
result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType())
|
||||
}
|
||||
@@ -905,6 +947,9 @@ module ClassValue {
|
||||
/** Get the `ClassValue` for the `LookupError` class. */
|
||||
ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) }
|
||||
|
||||
/** Get the `ClassValue` for the `IndexError` class. */
|
||||
ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) }
|
||||
|
||||
/** Get the `ClassValue` for the `IOError` class. */
|
||||
ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) }
|
||||
|
||||
@@ -925,4 +970,7 @@ module ClassValue {
|
||||
ClassValue unicodeDecodeError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `SystemExit` class. */
|
||||
ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) }
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ module PointsTo {
|
||||
|
||||
/* Backwards compatibility */
|
||||
cached
|
||||
deprecated predicate points_to(
|
||||
predicate points_to(
|
||||
ControlFlowNode f, PointsToContext context, Object obj, ClassObject cls, ControlFlowNode origin
|
||||
) {
|
||||
exists(ObjectInternal value |
|
||||
|
||||
@@ -10,12 +10,6 @@ import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
private FunctionObject exec_or_eval() {
|
||||
result = Object::builtin("exec")
|
||||
or
|
||||
result = Object::builtin("eval")
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint sink that represents an argument to exec or eval that is vulnerable to malicious input.
|
||||
* The `vuln` in `exec(vuln)` or similar.
|
||||
@@ -26,10 +20,9 @@ class StringEvaluationNode extends TaintSink {
|
||||
StringEvaluationNode() {
|
||||
exists(Exec exec | exec.getASubExpression().getAFlowNode() = this)
|
||||
or
|
||||
exists(CallNode call |
|
||||
exec_or_eval().getACall() = call and
|
||||
call.getAnArg() = this
|
||||
)
|
||||
Value::named("exec").getACall().getAnArg() = this
|
||||
or
|
||||
Value::named("eval").getACall().getAnArg() = this
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
|
||||
@@ -64,8 +64,12 @@ class OpenNode extends TaintSink {
|
||||
|
||||
OpenNode() {
|
||||
exists(CallNode call |
|
||||
call.getFunction().refersTo(Object::builtin("open")) and
|
||||
call.getAnArg() = this
|
||||
call = Value::named("open").getACall() and
|
||||
(
|
||||
call.getArg(0) = this
|
||||
or
|
||||
call.getArgByName("file") = this
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,21 @@ abstract class StringKind extends TaintKind {
|
||||
bindingset[this]
|
||||
StringKind() { this = this }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust",
|
||||
"lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper",
|
||||
"zfill",
|
||||
/* encode/decode is technically not correct, but close enough */
|
||||
"encode", "decode"] and
|
||||
result = this
|
||||
or
|
||||
name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and
|
||||
result.(SequenceKind).getItem() = this
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = this and
|
||||
(
|
||||
str_method_call(fromnode, tonode) or
|
||||
slice(fromnode, tonode) or
|
||||
tonode.(BinaryExprNode).getAnOperand() = fromnode or
|
||||
os_path_join(fromnode, tonode) or
|
||||
@@ -50,20 +61,6 @@ private class StringEqualitySanitizer extends Sanitizer {
|
||||
}
|
||||
}
|
||||
|
||||
/* tonode = fromnode.xxx() where the call to xxx returns an identical or similar string */
|
||||
private predicate str_method_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
exists(string method_name | tonode.getFunction().(AttrNode).getObject(method_name) = fromnode |
|
||||
method_name = "strip" or
|
||||
method_name = "format" or
|
||||
method_name = "lstrip" or
|
||||
method_name = "rstrip" or
|
||||
method_name = "ljust" or
|
||||
method_name = "rjust" or
|
||||
method_name = "title" or
|
||||
method_name = "capitalize"
|
||||
)
|
||||
}
|
||||
|
||||
/* tonode = ....format(fromnode) */
|
||||
private predicate str_format(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getName() = "format" and
|
||||
|
||||
@@ -36,19 +36,36 @@ class RaisingNode extends ControlFlowNode {
|
||||
* Gets the type of an exception that may be raised
|
||||
* at this control flow node
|
||||
*/
|
||||
ClassObject getARaisedType() {
|
||||
result = this.localRaisedType()
|
||||
ClassObject getARaisedType_objectapi() {
|
||||
result = this.localRaisedType_objectapi()
|
||||
or
|
||||
exists(FunctionObject func | this = func.getACall() | result = func.getARaisedType())
|
||||
or
|
||||
result = systemExitRaise_objectapi()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of an exception that may be raised
|
||||
* at this control flow node
|
||||
*/
|
||||
ClassValue getARaisedType() {
|
||||
result = this.localRaisedType()
|
||||
or
|
||||
exists(FunctionValue func | this = func.getACall() | result = func.getARaisedType())
|
||||
or
|
||||
result = systemExitRaise()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ClassObject systemExitRaise() { this.quits() and result = Object::builtin("SystemExit") }
|
||||
private ClassObject systemExitRaise_objectapi() {
|
||||
this.quits() and result = Object::builtin("SystemExit")
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ClassValue systemExitRaise() { this.quits() and result = ClassValue::systemExit() }
|
||||
|
||||
pragma[noinline, nomagic]
|
||||
private ClassObject localRaisedType() {
|
||||
private ClassObject localRaisedType_objectapi() {
|
||||
result.isSubclassOf(theBaseExceptionType()) and
|
||||
(
|
||||
exists(ControlFlowNode ex |
|
||||
@@ -62,8 +79,8 @@ class RaisingNode extends ControlFlowNode {
|
||||
or
|
||||
exists(ExceptFlowNode except |
|
||||
except = this.getAnExceptionalSuccessor() and
|
||||
except.handles(result) and
|
||||
result = this.innateException()
|
||||
except.handles_objectapi(result) and
|
||||
result = this.innateException_objectapi()
|
||||
)
|
||||
or
|
||||
not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and
|
||||
@@ -74,8 +91,36 @@ class RaisingNode extends ControlFlowNode {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline, nomagic]
|
||||
private ClassValue localRaisedType() {
|
||||
result.getASuperType() = ClassValue::baseException() and
|
||||
(
|
||||
exists(ControlFlowNode ex |
|
||||
ex = this.getExceptionNode() and
|
||||
(ex.pointsTo(result) or ex.pointsTo().getClass() = result)
|
||||
)
|
||||
or
|
||||
this.getNode() instanceof ImportExpr and result = ClassValue::importError()
|
||||
or
|
||||
this.getNode() instanceof Print and result = ClassValue::ioError()
|
||||
or
|
||||
exists(ExceptFlowNode except |
|
||||
except = this.getAnExceptionalSuccessor() and
|
||||
except.handles(result) and
|
||||
result = this.innateException()
|
||||
)
|
||||
or
|
||||
not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and
|
||||
sequence_or_mapping(this) and
|
||||
result = ClassValue::lookupError()
|
||||
or
|
||||
this.read_write_call() and result = ClassValue::ioError()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */
|
||||
pragma[noinline]
|
||||
ClassObject innateException() {
|
||||
ClassObject innateException_objectapi() {
|
||||
this.getNode() instanceof Attribute and result = theAttributeErrorType()
|
||||
or
|
||||
this.getNode() instanceof Name and result = theNameErrorType()
|
||||
@@ -85,6 +130,18 @@ class RaisingNode extends ControlFlowNode {
|
||||
this.getNode() instanceof Subscript and result = theKeyErrorType()
|
||||
}
|
||||
|
||||
/** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */
|
||||
pragma[noinline]
|
||||
ClassValue innateException() {
|
||||
this.getNode() instanceof Attribute and result = ClassValue::attributeError()
|
||||
or
|
||||
this.getNode() instanceof Name and result = ClassValue::nameError()
|
||||
or
|
||||
this.getNode() instanceof Subscript and result = ClassValue::indexError()
|
||||
or
|
||||
this.getNode() instanceof Subscript and result = ClassValue::keyError()
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this control flow node raises an exception,
|
||||
* but the type of the exception it raises cannot be inferred.
|
||||
@@ -114,7 +171,7 @@ class RaisingNode extends ControlFlowNode {
|
||||
/** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. */
|
||||
predicate unlikelySuccessor(ControlFlowNode succ) {
|
||||
succ = this.getAnExceptionalSuccessor() and
|
||||
not this.viableExceptionEdge(succ, _) and
|
||||
not this.viableExceptionEdge_objectapi(succ, _) and
|
||||
not this.raisesUnknownType()
|
||||
or
|
||||
exists(FunctionObject func |
|
||||
@@ -132,7 +189,30 @@ class RaisingNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
/** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */
|
||||
predicate viableExceptionEdge(ControlFlowNode succ, ClassObject raised) {
|
||||
predicate viableExceptionEdge_objectapi(ControlFlowNode succ, ClassObject raised) {
|
||||
raised.isLegalExceptionType() and
|
||||
raised = this.getARaisedType_objectapi() and
|
||||
succ = this.getAnExceptionalSuccessor() and
|
||||
(
|
||||
/* An 'except' that handles raised and there is no more previous handler */
|
||||
succ.(ExceptFlowNode).handles_objectapi(raised) and
|
||||
not exists(ExceptFlowNode other, StmtList s, int i, int j |
|
||||
not other = succ and
|
||||
other.handles_objectapi(raised) and
|
||||
s.getItem(i) = succ.getNode() and
|
||||
s.getItem(j) = other.getNode()
|
||||
|
|
||||
j < i
|
||||
)
|
||||
or
|
||||
/* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */
|
||||
not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and
|
||||
not succ instanceof ExceptFlowNode
|
||||
)
|
||||
}
|
||||
|
||||
/** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */
|
||||
predicate viableExceptionEdge(ControlFlowNode succ, ClassValue raised) {
|
||||
raised.isLegalExceptionType() and
|
||||
raised = this.getARaisedType() and
|
||||
succ = this.getAnExceptionalSuccessor() and
|
||||
@@ -159,7 +239,19 @@ class RaisingNode extends ControlFlowNode {
|
||||
* plausible that the scope `s` can be exited with exception `raised`
|
||||
* at this point.
|
||||
*/
|
||||
predicate viableExceptionalExit(Scope s, ClassObject raised) {
|
||||
predicate viableExceptionalExit_objectapi(Scope s, ClassObject raised) {
|
||||
raised.isLegalExceptionType() and
|
||||
raised = this.getARaisedType_objectapi() and
|
||||
this.isExceptionalExit(s) and
|
||||
not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this exceptional exit is viable. That is, is it
|
||||
* plausible that the scope `s` can be exited with exception `raised`
|
||||
* at this point.
|
||||
*/
|
||||
predicate viableExceptionalExit(Scope s, ClassValue raised) {
|
||||
raised.isLegalExceptionType() and
|
||||
raised = this.getARaisedType() and
|
||||
this.isExceptionalExit(s) and
|
||||
@@ -170,7 +262,29 @@ class RaisingNode extends ControlFlowNode {
|
||||
/** Is this a sequence or mapping subscript x[i]? */
|
||||
private predicate sequence_or_mapping(RaisingNode r) { r.getNode() instanceof Subscript }
|
||||
|
||||
private predicate current_exception(ClassObject ex, BasicBlock b) {
|
||||
private predicate current_exception_objectapi(ClassObject ex, BasicBlock b) {
|
||||
exists(RaisingNode r |
|
||||
r.viableExceptionEdge_objectapi(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode
|
||||
)
|
||||
or
|
||||
exists(BasicBlock prev |
|
||||
current_exception_objectapi(ex, prev) and
|
||||
exists(ControlFlowNode pred, ControlFlowNode succ |
|
||||
pred = prev.getLastNode() and succ = b.getNode(0)
|
||||
|
|
||||
pred.getASuccessor() = succ and
|
||||
(
|
||||
/* Normal control flow */
|
||||
not pred.getAnExceptionalSuccessor() = succ
|
||||
or
|
||||
/* Re-raise the current exception, propagating to the successor */
|
||||
pred instanceof ReraisingNode
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate current_exception(ClassValue ex, BasicBlock b) {
|
||||
exists(RaisingNode r |
|
||||
r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode
|
||||
)
|
||||
@@ -211,7 +325,19 @@ private predicate unknown_current_exception(BasicBlock b) {
|
||||
}
|
||||
|
||||
/** INTERNAL -- Use FunctionObject.getARaisedType() instead */
|
||||
predicate scope_raises(ClassObject ex, Scope s) {
|
||||
predicate scope_raises_objectapi(ClassObject ex, Scope s) {
|
||||
exists(BasicBlock b |
|
||||
current_exception_objectapi(ex, b) and
|
||||
b.getLastNode().isExceptionalExit(s)
|
||||
|
|
||||
b.getLastNode() instanceof ReraisingNode
|
||||
)
|
||||
or
|
||||
exists(RaisingNode r | r.viableExceptionalExit_objectapi(s, ex))
|
||||
}
|
||||
|
||||
/** INTERNAL -- Use FunctionObject.getARaisedType() instead */
|
||||
predicate scope_raises(ClassValue ex, Scope s) {
|
||||
exists(BasicBlock b |
|
||||
current_exception(ex, b) and
|
||||
b.getLastNode().isExceptionalExit(s)
|
||||
@@ -299,11 +425,18 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
/** Whether this `except` handles `cls` */
|
||||
predicate handles(ClassObject cls) {
|
||||
predicate handles_objectapi(ClassObject cls) {
|
||||
exists(ClassObject handled | this.handledException_objectapi(handled, _, _) |
|
||||
cls.getAnImproperSuperType() = handled
|
||||
)
|
||||
}
|
||||
|
||||
/** Whether this `except` handles `cls` */
|
||||
predicate handles(ClassValue cls) {
|
||||
exists(ClassValue handled | this.handledException(handled, _, _) |
|
||||
cls.getASuperType() = handled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private ControlFlowNode element_from_tuple_objectapi(Object tuple) {
|
||||
@@ -324,7 +457,15 @@ class ReraisingNode extends RaisingNode {
|
||||
}
|
||||
|
||||
/** Gets a class that may be raised by this node */
|
||||
override ClassObject getARaisedType() {
|
||||
override ClassObject getARaisedType_objectapi() {
|
||||
exists(BasicBlock b |
|
||||
current_exception_objectapi(result, b) and
|
||||
b.getNode(_) = this
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a class that may be raised by this node */
|
||||
override ClassValue getARaisedType() {
|
||||
exists(BasicBlock b |
|
||||
current_exception(result, b) and
|
||||
b.getNode(_) = this
|
||||
|
||||
@@ -37,8 +37,8 @@ abstract deprecated class CustomPointsToFact extends @py_flow_node {
|
||||
abstract predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin);
|
||||
}
|
||||
|
||||
/* For backwards compatibility */
|
||||
class FinalCustomPointsToFact = CustomPointsToFact;
|
||||
/** DEPRECATED -- Use PointsToExtension instead */
|
||||
deprecated class FinalCustomPointsToFact = CustomPointsToFact;
|
||||
|
||||
abstract deprecated class CustomPointsToOriginFact extends CustomPointsToFact {
|
||||
abstract predicate pointsTo(Object value, ClassObject cls);
|
||||
@@ -151,7 +151,7 @@ class ReModulePointToExtension extends PointsToExtension {
|
||||
private predicate pointsTo_helper(Context context) { context.appliesTo(this) }
|
||||
}
|
||||
|
||||
private class BackwardCompatiblePointToExtension extends PointsToExtension {
|
||||
deprecated private class BackwardCompatiblePointToExtension extends PointsToExtension {
|
||||
BackwardCompatiblePointToExtension() { this instanceof CustomPointsToFact }
|
||||
|
||||
override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) {
|
||||
@@ -174,7 +174,7 @@ private class BackwardCompatiblePointToExtension extends PointsToExtension {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate additionalAttribute(
|
||||
deprecated private predicate additionalAttribute(
|
||||
ObjectInternal owner, string name, ObjectInternal value, ControlFlowNode origin
|
||||
) {
|
||||
exists(Object obj, ClassObject cls |
|
||||
|
||||
@@ -135,7 +135,7 @@ class PyFunctionObject extends FunctionObject {
|
||||
/** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */
|
||||
override predicate isProcedure() { this.getFunction().isProcedure() }
|
||||
|
||||
override ClassObject getARaisedType() { scope_raises(result, this.getFunction()) }
|
||||
override ClassObject getARaisedType() { scope_raises_objectapi(result, this.getFunction()) }
|
||||
|
||||
override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) }
|
||||
|
||||
|
||||
@@ -7,3 +7,4 @@ import semmle.python.web.bottle.Request
|
||||
import semmle.python.web.turbogears.Request
|
||||
import semmle.python.web.falcon.Request
|
||||
import semmle.python.web.cherrypy.Request
|
||||
import semmle.python.web.stdlib.Request
|
||||
|
||||
@@ -7,3 +7,4 @@ import semmle.python.web.bottle.Response
|
||||
import semmle.python.web.turbogears.Response
|
||||
import semmle.python.web.falcon.Response
|
||||
import semmle.python.web.cherrypy.Response
|
||||
import semmle.python.web.stdlib.Response
|
||||
|
||||
124
python/ql/src/semmle/python/web/stdlib/Request.qll
Normal file
124
python/ql/src/semmle/python/web/stdlib/Request.qll
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Provides the sources and taint-flow for HTTP servers defined using the standard library (stdlib).
|
||||
* Specifically, we model `HttpRequestTaintSource`s from instances of `BaseHTTPRequestHandler`
|
||||
* (or subclasses) and form parsing using `cgi.FieldStorage`.
|
||||
*/
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
|
||||
/** Source of BaseHTTPRequestHandler instances. */
|
||||
class StdLibRequestSource extends HttpRequestTaintSource {
|
||||
StdLibRequestSource() {
|
||||
exists(ClassValue cls |
|
||||
cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler")
|
||||
or
|
||||
cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler")
|
||||
|
|
||||
this.(ControlFlowNode).pointsTo().getClass() = cls
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind }
|
||||
}
|
||||
|
||||
/** TaintKind for an instance of BaseHTTPRequestHandler. */
|
||||
class BaseHTTPRequestHandlerKind extends TaintKind {
|
||||
BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name in ["requestline", "path"] and
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
name = "headers" and
|
||||
result instanceof HTTPMessageKind
|
||||
or
|
||||
name = "rfile" and
|
||||
result instanceof ExternalFileObject
|
||||
}
|
||||
}
|
||||
|
||||
/** TaintKind for headers (instance of HTTPMessage). */
|
||||
class HTTPMessageKind extends ExternalStringDictKind {
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
result = super.getTaintOfMethodResult(name)
|
||||
or
|
||||
name = "get_all" and
|
||||
result.(SequenceKind).getItem() = this.getValue()
|
||||
or
|
||||
name in ["as_bytes", "as_string"] and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = super.getTaintForFlowStep(fromnode, tonode)
|
||||
or
|
||||
exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() |
|
||||
tonode = cls.getACall() and
|
||||
tonode.(CallNode).getArg(0) = fromnode and
|
||||
result instanceof ExternalStringKind
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Source of parsed HTTP forms (by using the `cgi` module). */
|
||||
class CgiFieldStorageSource extends HttpRequestTaintSource {
|
||||
CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind }
|
||||
}
|
||||
|
||||
/** TaintKind for a parsed HTTP form. */
|
||||
class CgiFieldStorageFormKind extends TaintKind {
|
||||
/*
|
||||
* There is a slight difference between how we model form/fields and how it is handled by the code.
|
||||
* In the code
|
||||
* ```
|
||||
* form = cgi.FieldStorage()
|
||||
* field = form['myfield']
|
||||
* ```
|
||||
* both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent
|
||||
* nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested
|
||||
* we ignore that detail since it allows for a more clean modeling.
|
||||
*/
|
||||
CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "getvalue" and
|
||||
(
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
result.(SequenceKind).getItem() instanceof ExternalStringKind
|
||||
)
|
||||
or
|
||||
name = "getfirst" and
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
name = "getlist" and
|
||||
result.(SequenceKind).getItem() instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.(SubscriptNode).getObject() = fromnode and
|
||||
(
|
||||
result instanceof CgiFieldStorageFieldKind
|
||||
or
|
||||
result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** TaintKind for the field of a parsed HTTP form. */
|
||||
class CgiFieldStorageFieldKind extends TaintKind {
|
||||
CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name in ["filename", "value"] and result instanceof ExternalStringKind
|
||||
or
|
||||
name = "file" and result instanceof ExternalFileObject
|
||||
}
|
||||
}
|
||||
43
python/ql/src/semmle/python/web/stdlib/Response.qll
Normal file
43
python/ql/src/semmle/python/web/stdlib/Response.qll
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Provides the sinks for HTTP servers defined with standard library (stdlib).
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
|
||||
private predicate is_wfile(AttrNode wfile) {
|
||||
exists(ClassValue cls |
|
||||
// Python 2
|
||||
cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler")
|
||||
or
|
||||
// Python 3
|
||||
cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler")
|
||||
|
|
||||
wfile.getObject("wfile").pointsTo().getClass() = cls
|
||||
)
|
||||
}
|
||||
|
||||
/** Sink for `h.wfile.write` where `h` is an instance of BaseHTTPRequestHandler. */
|
||||
class StdLibWFileWriteSink extends HttpResponseTaintSink {
|
||||
StdLibWFileWriteSink() {
|
||||
exists(CallNode call |
|
||||
is_wfile(call.getFunction().(AttrNode).getObject("write")) and
|
||||
call.getArg(0) = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
/** Sink for `h.wfile.writelines` where `h` is an instance of BaseHTTPRequestHandler. */
|
||||
class StdLibWFileWritelinesSink extends HttpResponseTaintSink {
|
||||
StdLibWFileWritelinesSink() {
|
||||
exists(CallNode call |
|
||||
is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and
|
||||
call.getArg(0) = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind }
|
||||
}
|
||||
Reference in New Issue
Block a user