Python: Add 'attr' predicate as a synomnym for 'getAttribute' to help readability.

This commit is contained in:
Mark Shannon
2019-02-20 11:06:40 +00:00
parent 35fa5d8f60
commit 98be27a73e
47 changed files with 87 additions and 86 deletions

View File

@@ -8,6 +8,9 @@
The constants `MULTILINE` and `VERBOSE` in `re` module, are now understood for Python 3.6 and upward.
Removes false positives seen when using Python 3.6, but not when using earlier versions.
The API has been improved to declutter the global namespace and improve discoverability and readability.
* New predicates `ModuleObject::named(name)` and `ModuleObject.attr(name)` have been added, allowing more readable access to common objects. For example, `(any ModuleObject m | m.getName() = "sys").getAttribute("exit")` can be replaced with `ModuleObject::named("sys").attr("exit")`
* The API for accessing builtin functions has been improved. Predicates of the form `theXXXFunction()`, such as `theLenFunction()`, have been deprecated in favour of `Object::builtin(name)`.
## New queries

View File

@@ -19,7 +19,7 @@ import python
predicate numpy_array_type(ClassObject na) {
exists(ModuleObject np | np.getName() = "numpy" or np.getName() = "numpy.core" |
na.getAnImproperSuperType() = np.getAttribute("ndarray")
na.getAnImproperSuperType() = np.attr("ndarray")
)
}

View File

@@ -15,7 +15,7 @@ import python
ClassObject jinja2EnvironmentOrTemplate() {
exists(ModuleObject jinja2, string name |
jinja2.getName() = "jinja2" and
jinja2.getAttribute(name) = result |
jinja2.attr(name) = result |
name = "Environment" or
name = "Template"
)

View File

@@ -17,7 +17,7 @@ import semmle.python.web.Http
FunctionObject requestFunction() {
exists(ModuleObject req |
req.getName() = "requests" and
result = req.getAttribute(httpVerbLower())
result = req.attr(httpVerbLower())
)
}

View File

@@ -21,7 +21,7 @@ int minimumSecureKeySize(string algo) {
predicate dsaRsaKeySizeArg(FunctionObject obj, string algorithm, string arg) {
exists(ModuleObject mod |
mod.getAttribute(_) = obj |
mod.attr(_) = obj |
algorithm = "DSA" and
(
mod.getName() = "cryptography.hazmat.primitives.asymmetric.dsa" and arg = "key_size"
@@ -44,7 +44,7 @@ predicate dsaRsaKeySizeArg(FunctionObject obj, string algorithm, string arg) {
predicate ecKeySizeArg(FunctionObject obj, string arg) {
exists(ModuleObject mod |
mod.getAttribute(_) = obj |
mod.attr(_) = obj |
mod.getName() = "cryptography.hazmat.primitives.asymmetric.ec" and arg = "curve"
)
}

View File

@@ -13,11 +13,11 @@
import python
FunctionObject ssl_wrap_socket() {
result = any(ModuleObject ssl | ssl.getName() = "ssl").getAttribute("wrap_socket")
result = any(ModuleObject ssl | ssl.getName() = "ssl").attr("wrap_socket")
}
ClassObject ssl_Context_class() {
result = any(ModuleObject ssl | ssl.getName() = "ssl").getAttribute("SSLContext")
result = any(ModuleObject ssl | ssl.getName() = "ssl").attr("SSLContext")
}
CallNode unsafe_call(string method_name) {

View File

@@ -12,11 +12,11 @@
import python
FunctionObject ssl_wrap_socket() {
result = the_ssl_module().getAttribute("wrap_socket")
result = the_ssl_module().attr("wrap_socket")
}
ClassObject ssl_Context_class() {
result = the_ssl_module().getAttribute("SSLContext")
result = the_ssl_module().attr("SSLContext")
}
string insecure_version_name() {
@@ -69,20 +69,20 @@ predicate unsafe_ssl_wrap_socket_call(CallNode call, string method_name, string
insecure_version = insecure_version_name()
and
(
call.getArgByName("ssl_version").refersTo(the_ssl_module().getAttribute(insecure_version))
call.getArgByName("ssl_version").refersTo(the_ssl_module().attr(insecure_version))
or
probable_insecure_ssl_constant(call, insecure_version)
)
}
ClassObject the_pyOpenSSL_Context_class() {
result = any(ModuleObject m | m.getName() = "pyOpenSSL.SSL").getAttribute("Context")
result = any(ModuleObject m | m.getName() = "pyOpenSSL.SSL").attr("Context")
}
predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) {
call = the_pyOpenSSL_Context_class().getACall() and
insecure_version = insecure_version_name() and
call.getArg(0).refersTo(the_pyOpenSSL_module().getAttribute(insecure_version))
call.getArg(0).refersTo(the_pyOpenSSL_module().attr(insecure_version))
}
from CallNode call, string method_name, string insecure_version

View File

@@ -35,12 +35,12 @@ string permissive_permission(int p) {
}
predicate chmod_call(CallNode call, FunctionObject chmod, NumericObject num) {
any(ModuleObject os | os.getName() = "os").getAttribute("chmod") = chmod and
any(ModuleObject os | os.getName() = "os").attr("chmod") = chmod and
chmod.getACall() = call and call.getArg(1).refersTo(num)
}
predicate open_call(CallNode call, FunctionObject open, NumericObject num) {
any(ModuleObject os | os.getName() = "os").getAttribute("open") = open and
any(ModuleObject os | os.getName() = "os").attr("open") = open and
open.getACall() = call and call.getArg(2).refersTo(num)
}

View File

@@ -34,7 +34,7 @@ predicate fewer_characters_than(StrConst str, string char, float fraction) {
}
predicate possible_reflective_name(string name) {
exists(any(ModuleObject m).getAttribute(name))
exists(any(ModuleObject m).attr(name))
or
exists(any(ClassObject c).lookupAttribute(name))
or

View File

@@ -84,7 +84,7 @@ predicate in_notebook(Expr e) {
}
FunctionObject assertRaises() {
result = ModuleObject::named("unittest").getAttribute("TestCase").(ClassObject).lookupAttribute("assertRaises")
result = ModuleObject::named("unittest").attr("TestCase").(ClassObject).lookupAttribute("assertRaises")
}
/** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */

View File

@@ -109,7 +109,7 @@ private Object attribute_in_scope(Object obj, string name) {
or
exists(ModuleObject mod |
mod = obj |
mod.getAttribute(name) = result and result.(ControlFlowNode).getScope() = mod.getModule()
mod.attr(name) = result and result.(ControlFlowNode).getScope() = mod.getModule()
and not result.(ControlFlowNode).isEntryNode()
)
}

View File

@@ -21,7 +21,7 @@ abstract class ExternalPackage extends Object {
abstract string getVersion();
Object getAttribute(string name) {
result = this.(ModuleObject).getAttribute(name)
result = this.(ModuleObject).attr(name)
}
PackageObject getPackage() {

View File

@@ -7,7 +7,7 @@ class UnitTestClass extends TestScope {
UnitTestClass() {
exists(ClassObject c |
this = c.getPyClass() |
c.getASuperType() = theUnitTestPackage().getAttribute(_)
c.getASuperType() = theUnitTestPackage().attr(_)
or
c.getASuperType().getName().toLowerCase() = "testcase"
)

View File

@@ -8,7 +8,7 @@ class ZopeInterfaceMethod extends PyFunctionObject {
/** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */
ZopeInterfaceMethod() {
exists(Object interface, ClassObject owner |
ModuleObject::named("zope.interface").getAttribute("Interface") = interface and
interface = ModuleObject::named("zope.interface").attr("Interface") and
owner.declaredAttribute(_) = this and
owner.getAnImproperSuperType().getABaseType() = interface
)

View File

@@ -18,7 +18,7 @@ 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().refersTo(re.getAttribute(name)) |
call.getFunction().refersTo(re.attr(name)) |
mode = "None"
or
exists(Object obj |
@@ -40,7 +40,7 @@ string mode_from_mode_object(Object obj) {
result = "MULTILINE" or result = "DOTALL" or result = "UNICODE" or
result = "VERBOSE"
) and
ModuleObject::named("sre_constants").getAttribute("SRE_FLAG_" + result) = obj
obj = ModuleObject::named("sre_constants").attr("SRE_FLAG_" + result)
or
exists(BinaryExpr be, Object sub | obj.getOrigin() = be |
be.getOp() instanceof BitOr and

View File

@@ -95,7 +95,7 @@ module Cryptography {
class CipherClass extends ClassObject {
CipherClass() {
ciphers().getAttribute("Cipher") = this
ciphers().attr("Cipher") = this
}
}
@@ -103,7 +103,7 @@ module Cryptography {
class AlgorithmClass extends ClassObject {
AlgorithmClass() {
ciphers().submodule("algorithms").getAttribute(_) = this
ciphers().submodule("algorithms").attr(_) = this
}
string getAlgorithmName() {

View File

@@ -12,7 +12,7 @@ private ModuleObject theTracebackModule() {
}
private FunctionObject traceback_function(string name) {
result = theTracebackModule().getAttribute(name)
result = theTracebackModule().attr(name)
}
/**

View File

@@ -242,7 +242,7 @@ private predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
or
exists(ModuleObject copy, string name |
name = "copy" or name = "deepcopy" |
copy.getAttribute(name).(FunctionObject).getACall() = tonode and
copy.attr(name).(FunctionObject).getACall() = tonode and
tonode.getArg(0) = fromnode
)
or

View File

@@ -11,10 +11,6 @@ import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
private ModuleObject subprocessModule() {
result.getName() = "subprocess"
}
private ModuleObject osOrPopenModule() {
result.getName() = "os" or
result.getName() = "popen2"
@@ -22,7 +18,7 @@ private ModuleObject osOrPopenModule() {
private Object makeOsCall() {
exists(string name |
result = subprocessModule().getAttribute(name) |
result = ModuleObject::named("subprocess").attr(name) |
name = "Popen" or
name = "call" or
name = "check_call" or
@@ -79,7 +75,7 @@ class ShellCommand extends TaintSink {
or
exists(CallNode call, string name |
call.getAnArg() = this and
call.getFunction().refersTo(osOrPopenModule().getAttribute(name)) |
call.getFunction().refersTo(osOrPopenModule().attr(name)) |
name = "system" or
name = "popen" or
name.matches("popen_")

View File

@@ -12,7 +12,7 @@ import semmle.python.security.strings.Untrusted
private FunctionObject marshalLoads() {
result = any(ModuleObject marshal | marshal.getName() = "marshal").getAttribute("loads")
result = ModuleObject::named("marshal").attr("loads")
}

View File

@@ -20,12 +20,12 @@ private class PathSanitizer extends Sanitizer {
}
private FunctionObject abspath() {
exists(ModuleObject os, ModuleObject os_path |
os.getName() = "os" and
os.getAttribute("path") = os_path |
os_path.getAttribute("abspath") = result
exists(ModuleObject os_path |
ModuleObject::named("os").attr("path") = os_path
|
os_path.attr("abspath") = result
or
os_path.getAttribute("normpath") = result
os_path.attr("normpath") = result
)
}

View File

@@ -20,7 +20,7 @@ private ModuleObject pickleModule() {
}
private FunctionObject pickleLoads() {
result = pickleModule().getAttribute("loads")
result = pickleModule().attr("loads")
}
/** `pickle.loads(untrusted)` vulnerability. */

View File

@@ -33,7 +33,7 @@ private class ExpatParser extends TaintKind {
}
private FunctionObject expatCreateParseFunction() {
result = ModuleObject::named("xml.parsers.expat").getAttribute("ParserCreate")
result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate")
}
private class ExpatCreateParser extends TaintSource {
@@ -52,13 +52,13 @@ private class ExpatCreateParser extends TaintSource {
}
private FunctionObject xmlFromString() {
result = xmlElementTreeModule().getAttribute("fromstring")
result = xmlElementTreeModule().attr("fromstring")
or
result = xmlMiniDomModule().getAttribute("parseString")
result = xmlMiniDomModule().attr("parseString")
or
result = xmlPullDomModule().getAttribute("parseString")
result = xmlPullDomModule().attr("parseString")
or
result = xmlSaxModule().getAttribute("parseString")
result = xmlSaxModule().attr("parseString")
}
/** A (potentially) malicious XML string. */

View File

@@ -12,13 +12,8 @@ import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
private ModuleObject yamlModule() {
result.getName() = "yaml"
}
private FunctionObject yamlLoad() {
result = yamlModule().getAttribute("load")
result = ModuleObject::named("yaml").attr("load")
}
/** `yaml.load(untrusted)` vulnerability. */

View File

@@ -110,9 +110,8 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
/* tonode = os.path.join(..., fromnode, ...) */
private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
exists(FunctionObject path_join |
exists(ModuleObject os | os.getName() = "os" |
os.getAttribute("path").(ModuleObject).getAttribute("join") = path_join
) |
path_join = ModuleObject::named("os").attr("path").(ModuleObject).attr("join")
and
tonode = path_join.getACall() and tonode.getAnArg() = fromnode
)
}

View File

@@ -7,7 +7,7 @@ predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
or
exists(ModuleObject copy, string name |
name = "copy" or name = "deepcopy" |
copy.getAttribute(name).(FunctionObject).getACall() = tonode and
copy.attr(name).(FunctionObject).getACall() = tonode and
tonode.getArg(0) = fromnode
)
or

View File

@@ -91,7 +91,7 @@ private predicate json_subscript_taint(SubscriptNode sub, ControlFlowNode obj, E
private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
exists(FunctionObject json_loads |
any(ModuleObject json | json.getName() = "json").getAttribute("loads") = json_loads and
any(ModuleObject json | json.getName() = "json").attr("loads") = json_loads and
json_loads.getACall() = tonode and tonode.getArg(0) = fromnode
)
}

View File

@@ -33,10 +33,18 @@ abstract class ModuleObject extends Object {
}
/** Gets the named attribute of this module. Using attributeRefersTo() instead
* may provide better results for presentation. */
* may provide better results for presentation.
* */
pragma [noinline]
abstract Object getAttribute(string name);
/** Gets the named attribute of this module.
* Synonym for `getAttribute(name)` */
pragma [inline]
final Object attr(string name) {
result = this.getAttribute(name)
}
/** Whether the named attribute of this module "refers-to" value, with a known origin.
*/
abstract predicate attributeRefersTo(string name, Object value, ControlFlowNode origin);

View File

@@ -192,7 +192,7 @@ private Object findByName1(string longName) {
exists(string owner, string attrname |
longName = owner + "." + attrname
|
result = findByName0(owner).(ModuleObject).getAttribute(attrname)
result = findByName0(owner).(ModuleObject).attr(attrname)
or
result = findByName0(owner).(ClassObject).lookupAttribute(attrname)
)
@@ -204,7 +204,7 @@ private Object findByName2(string longName) {
exists(string owner, string attrname |
longName = owner + "." + attrname
|
result = findByName1(owner).(ModuleObject).getAttribute(attrname)
result = findByName1(owner).(ModuleObject).attr(attrname)
or
result = findByName1(owner).(ClassObject).lookupAttribute(attrname)
)
@@ -216,7 +216,7 @@ private Object findByName3(string longName) {
exists(string owner, string attrname |
longName = owner + "." + attrname
|
result = findByName2(owner).(ModuleObject).getAttribute(attrname)
result = findByName2(owner).(ModuleObject).attr(attrname)
or
result = findByName2(owner).(ClassObject).lookupAttribute(attrname)
)

View File

@@ -9,7 +9,7 @@ ModuleObject theBottleModule() {
/** The bottle.Bottle class */
ClassObject theBottleClass() {
result = ModuleObject::named("bottle").getAttribute("Bottle")
result = ModuleObject::named("bottle").attr("Bottle")
}
/** Holds if `route` is routed to `func`
@@ -18,7 +18,7 @@ ClassObject theBottleClass() {
predicate bottle_route(CallNode route_call, ControlFlowNode route, Function func) {
exists(CallNode decorator_call, string name |
route_call.getFunction().(AttrNode).getObject(name).refersTo(_, theBottleClass(), _) or
route_call.getFunction().refersTo(theBottleModule().getAttribute(name))
route_call.getFunction().refersTo(theBottleModule().attr(name))
|
(name = "route" or name = httpVerbLower()) and
decorator_call.getFunction() = route_call and
@@ -73,7 +73,7 @@ class BottleRoutePointToExtension extends CustomPointsToFact {
override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) {
context.isImport() and
ModuleObject::named("bottle").getAttribute("Bottle").(ClassObject).attributeRefersTo(name, value, cls, origin)
ModuleObject::named("bottle").attr("Bottle").(ClassObject).attributeRefersTo(name, value, cls, origin)
}
}

View File

@@ -9,7 +9,7 @@ import semmle.python.security.strings.Basic
import semmle.python.web.bottle.General
FunctionObject bottle_redirect() {
result = theBottleModule().getAttribute("redirect")
result = theBottleModule().attr("redirect")
}
/**

View File

@@ -7,7 +7,7 @@ import semmle.python.web.Http
import semmle.python.web.bottle.General
private Object theBottleRequestObject() {
result = theBottleModule().getAttribute("request")
result = theBottleModule().attr("request")
}
class BottleRequestKind extends TaintKind {

View File

@@ -19,7 +19,7 @@ class BottleResponse extends TaintKind {
}
private Object theBottleResponseObject() {
result = theBottleModule().getAttribute("response")
result = theBottleModule().attr("response")
}
class BottleResponseBodyAssignment extends TaintSink {

View File

@@ -12,7 +12,7 @@ class DjangoDbCursor extends DbCursor {
}
private Object theDjangoConnectionObject() {
any(ModuleObject m | m.getName() = "django.db").getAttribute("connection") = result
any(ModuleObject m | m.getName() = "django.db").attr("connection") = result
}
/** A kind of taint source representing sources of django cursor objects.
@@ -38,7 +38,7 @@ class DjangoDbCursorSource extends DbConnectionSource {
ClassObject theDjangoRawSqlClass() {
result = any(ModuleObject m | m.getName() = "django.db.models.expressions").getAttribute("RawSQL")
result = any(ModuleObject m | m.getName() = "django.db.models.expressions").attr("RawSQL")
}
/**

View File

@@ -8,7 +8,7 @@ import semmle.python.web.Http
class DjangoModel extends ClassObject {
DjangoModel() {
any(ModuleObject m | m.getName() = "django.db.models").getAttribute("Model") = this.getAnImproperSuperType()
any(ModuleObject m | m.getName() = "django.db.models").attr("Model") = this.getAnImproperSuperType()
}
}

View File

@@ -82,7 +82,7 @@ private class DjangoFunctionBasedViewRequestArgument extends DjangoRequestSource
private class DjangoView extends ClassObject {
DjangoView() {
any(ModuleObject m | m.getName() = "django.views.generic").getAttribute("View") = this.getAnImproperSuperType()
any(ModuleObject m | m.getName() = "django.views.generic").attr("View") = this.getAnImproperSuperType()
}
}
@@ -109,7 +109,7 @@ class DjangoClassBasedViewRequestArgument extends DjangoRequestSource {
/* Function based views */
predicate url_dispatch(CallNode call, ControlFlowNode regex, FunctionObject view) {
exists(FunctionObject url |
any(ModuleObject m | m.getName() = "django.conf.urls").getAttribute("url") = url and
any(ModuleObject m | m.getName() = "django.conf.urls").attr("url") = url and
url.getArgumentForCall(call, 0) = regex and
url.getArgumentForCall(call, 1).refersTo(view)
)

View File

@@ -17,7 +17,7 @@ class DjangoResponse extends TaintKind {
}
private ClassObject theDjangoHttpResponseClass() {
result = any(ModuleObject m | m.getName() = "django.http.response").getAttribute("HttpResponse") and
result = any(ModuleObject m | m.getName() = "django.http.response").attr("HttpResponse") and
not result = theDjangoHttpRedirectClass()
}

View File

@@ -1,9 +1,9 @@
import python
FunctionObject redirect() {
result = any(ModuleObject m | m.getName() = "django.shortcuts").getAttribute("redirect")
result = any(ModuleObject m | m.getName() = "django.shortcuts").attr("redirect")
}
ClassObject theDjangoHttpRedirectClass() {
result = any(ModuleObject m | m.getName() = "django.http.response").getAttribute("HttpResponseRedirectBase")
result = any(ModuleObject m | m.getName() = "django.http.response").attr("HttpResponseRedirectBase")
}

View File

@@ -8,16 +8,16 @@ ModuleObject theFlaskModule() {
/** The flask app class */
ClassObject theFlaskClass() {
result = theFlaskModule().getAttribute("Flask")
result = theFlaskModule().attr("Flask")
}
/** The flask MethodView class */
ClassObject theFlaskMethodViewClass() {
result = any(ModuleObject m | m.getName() = "flask.views").getAttribute("MethodView")
result = any(ModuleObject m | m.getName() = "flask.views").attr("MethodView")
}
ClassObject theFlaskReponseClass() {
result = theFlaskModule().getAttribute("Response")
result = theFlaskModule().attr("Response")
}
/** Holds if `route` is routed to `func`

View File

@@ -9,7 +9,7 @@ import semmle.python.security.strings.Basic
import semmle.python.web.flask.General
FunctionObject flask_redirect() {
result = theFlaskModule().getAttribute("redirect")
result = theFlaskModule().attr("redirect")
}
/**

View File

@@ -5,7 +5,7 @@ import semmle.python.web.Http
import semmle.python.web.flask.General
private Object theFlaskRequestObject() {
result = theFlaskModule().getAttribute("request")
result = theFlaskModule().attr("request")
}

View File

@@ -10,9 +10,9 @@ import semmle.python.security.strings.Basic
private ClassObject redirectClass() {
exists(ModuleObject ex |
ex.getName() = "pyramid.httpexceptions" |
ex.getAttribute("HTTPFound") = result
ex.attr("HTTPFound") = result
or
ex.getAttribute("HTTPTemporaryRedirect") = result
ex.attr("HTTPTemporaryRedirect") = result
)
}

View File

@@ -12,7 +12,7 @@ class PyramidRequest extends BaseWebobRequest {
}
override ClassObject getClass() {
result = any(ModuleObject m | m.getName() = "pyramid.request").getAttribute("Request")
result = any(ModuleObject m | m.getName() = "pyramid.request").attr("Request")
}
}

View File

@@ -5,7 +5,7 @@ ModuleObject thePyramidViewModule() {
}
Object thePyramidViewConfig() {
result = thePyramidViewModule().getAttribute("view_config")
result = thePyramidViewModule().attr("view_config")
}
predicate is_pyramid_view_function(Function func) {

View File

@@ -3,7 +3,7 @@ import python
import semmle.python.security.TaintTracking
private ClassObject theTornadoRequestHandlerClass() {
result = any(ModuleObject m | m.getName() = "tornado.web").getAttribute("RequestHandler")
result = any(ModuleObject m | m.getName() = "tornado.web").attr("RequestHandler")
}
ClassObject aTornadoRequestHandlerClass() {

View File

@@ -3,11 +3,11 @@ import python
import semmle.python.security.TaintTracking
private ClassObject theTwistedHttpRequestClass() {
result = any(ModuleObject m | m.getName() = "twisted.web.http").getAttribute("Request")
result = any(ModuleObject m | m.getName() = "twisted.web.http").attr("Request")
}
private ClassObject theTwistedHttpResourceClass() {
result = any(ModuleObject m | m.getName() = "twisted.web.resource").getAttribute("Resource")
result = any(ModuleObject m | m.getName() = "twisted.web.resource").attr("Resource")
}
ClassObject aTwistedRequestHandlerClass() {

View File

@@ -45,7 +45,7 @@ class WebobRequest extends BaseWebobRequest {
}
override ClassObject getClass() {
result = any(ModuleObject m | m.getName() = "webob.request").getAttribute("Request")
result = any(ModuleObject m | m.getName() = "webob.request").attr("Request")
}
}