mirror of
https://github.com/github/codeql.git
synced 2025-12-19 18:33:16 +01:00
Merge pull request #8604 from erik-krogh/httpNode
JS: refactor most library models away from AST nodes
This commit is contained in:
@@ -175,7 +175,7 @@ predicate isOtherModeledArgument(DataFlow::Node n, FilteringReason reason) {
|
||||
or
|
||||
n instanceof CryptographicKey and reason instanceof CryptographicKeyReason
|
||||
or
|
||||
any(CryptographicOperation op).getInput().flow() = n and
|
||||
any(CryptographicOperation op).getInput() = n and
|
||||
reason instanceof CryptographicOperationFlowReason
|
||||
or
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
|
||||
@@ -144,7 +144,7 @@ private module AccessPaths {
|
||||
not param = base.getReceiver()
|
||||
|
|
||||
result = param and
|
||||
name = param.asSource().asExpr().(Parameter).getName()
|
||||
name = param.asSource().(DataFlow::ParameterNode).getName()
|
||||
or
|
||||
param.asSource().asExpr() instanceof DestructuringPattern and
|
||||
result = param.getMember(name)
|
||||
|
||||
@@ -42,10 +42,10 @@ module SinkEndpointFilter {
|
||||
result = "modeled database access"
|
||||
or
|
||||
// Remove calls to APIs that aren't relevant to NoSQL injection
|
||||
call.getReceiver().asExpr() instanceof HTTP::RequestExpr and
|
||||
call.getReceiver() instanceof HTTP::RequestNode and
|
||||
result = "receiver is a HTTP request expression"
|
||||
or
|
||||
call.getReceiver().asExpr() instanceof HTTP::ResponseExpr and
|
||||
call.getReceiver() instanceof HTTP::ResponseNode and
|
||||
result = "receiver is a HTTP response expression"
|
||||
)
|
||||
or
|
||||
@@ -115,7 +115,7 @@ predicate isBaseAdditionalFlowStep(
|
||||
inlbl = TaintedObject::label() and
|
||||
outlbl = TaintedObject::label() and
|
||||
exists(NoSql::Query query, DataFlow::SourceNode queryObj |
|
||||
queryObj.flowsToExpr(query) and
|
||||
queryObj.flowsTo(query) and
|
||||
queryObj.flowsTo(trg) and
|
||||
src = queryObj.getAPropertyWrite().getRhs()
|
||||
)
|
||||
|
||||
57
javascript/ql/lib/change-notes/2022-04-04-dataflow-models.md
Normal file
57
javascript/ql/lib/change-notes/2022-04-04-dataflow-models.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* Many library models have been rewritten to use dataflow nodes instead of the AST.
|
||||
The types of some classes have been changed, and these changes may break existing code.
|
||||
Other classes and predicates have been renamed, in these cases the old name is still available as a deprecated feature.
|
||||
|
||||
* The basetype of the following list of classes has changed from an expression to a dataflow node, and thus code using these classes might break.
|
||||
The fix to these breakages is usually to use `asExpr()` to get an expression from a dataflow node, or to use `.flow()` to get a dataflow node from an expression.
|
||||
- DOM.qll#WebStorageWrite
|
||||
- CryptoLibraries.qll#CryptographicOperation
|
||||
- Express.qll#Express::RequestBodyAccess
|
||||
- HTTP.qll#HTTP::ResponseBody
|
||||
- HTTP.qll#HTTP::CookieDefinition
|
||||
- HTTP.qll#HTTP::ServerDefinition
|
||||
- HTTP.qll#HTTP::RouteSetup
|
||||
- NoSQL.qll#NoSql::Query
|
||||
- SQL.qll#SQL::SqlString
|
||||
- SQL.qll#SQL::SqlSanitizer
|
||||
- HTTP.qll#ResponseBody
|
||||
- HTTP.qll#CookieDefinition
|
||||
- HTTP.qll#ServerDefinition
|
||||
- HTTP.qll#RouteSetup
|
||||
- HTTP.qll#HTTP::RedirectInvocation
|
||||
- HTTP.qll#RedirectInvocation
|
||||
- Express.qll#Express::RouterDefinition
|
||||
- AngularJSCore.qll#LinkFunction
|
||||
- Connect.qll#Connect::StandardRouteHandler
|
||||
- CryptoLibraries.qll#CryptographicKeyCredentialsExpr
|
||||
- AWS.qll#AWS::Credentials
|
||||
- Azure.qll#Azure::Credentials
|
||||
- Connect.qll#Connect::Credentials
|
||||
- DigitalOcean.qll#DigitalOcean::Credentials
|
||||
- Express.qll#Express::Credentials
|
||||
- NodeJSLib.qll#NodeJSLib::Credentials
|
||||
- PkgCloud.qll#PkgCloud::Credentials
|
||||
- Request.qll#Request::Credentials
|
||||
- ServiceDefinitions.qll#InjectableFunctionServiceRequest
|
||||
- SensitiveActions.qll#SensitiveVariableAccess
|
||||
- SensitiveActions.qll#CleartextPasswordExpr
|
||||
- Connect.qll#Connect::ServerDefinition
|
||||
- Restify.qll#Restify::ServerDefinition
|
||||
- Connect.qll#Connect::RouteSetup
|
||||
- Express.qll#Express::RouteSetup
|
||||
- Fastify.qll#Fastify::RouteSetup
|
||||
- Hapi.qll#Hapi::RouteSetup
|
||||
- Koa.qll#Koa::RouteSetup
|
||||
- Restify.qll#Restify::RouteSetup
|
||||
- NodeJSLib.qll#NodeJSLib::RouteSetup
|
||||
- Express.qll#Express::StandardRouteHandler
|
||||
- Express.qll#Express::SetCookie
|
||||
- Hapi.qll#Hapi::RouteHandler
|
||||
- HTTP.qll#HTTP::Servers::StandardHeaderDefinition
|
||||
- HTTP.qll#Servers::StandardHeaderDefinition
|
||||
- Hapi.qll#Hapi::ServerDefinition
|
||||
- Koa.qll#Koa::AppDefinition
|
||||
- SensitiveActions.qll#SensitiveCall
|
||||
@@ -167,7 +167,7 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
|
||||
/**
|
||||
* Holds if this expression may refer to the initial value of parameter `p`.
|
||||
*/
|
||||
predicate mayReferToParameter(Parameter p) { this.flow().mayReferToParameter(p) }
|
||||
predicate mayReferToParameter(Parameter p) { DataFlow::parameterNode(p).flowsToExpr(this) }
|
||||
|
||||
/**
|
||||
* Gets the static type of this expression, as determined by the TypeScript type system.
|
||||
|
||||
@@ -139,9 +139,12 @@ module DataFlow {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `DataFlow::ParameterNode::flowsTo()` instead.
|
||||
* Holds if this expression may refer to the initial value of parameter `p`.
|
||||
*/
|
||||
predicate mayReferToParameter(Parameter p) { parameterNode(p).(SourceNode).flowsTo(this) }
|
||||
deprecated predicate mayReferToParameter(Parameter p) {
|
||||
parameterNode(p).(SourceNode).flowsTo(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
|
||||
@@ -472,6 +472,12 @@ class FunctionNode extends DataFlow::ValueNode, DataFlow::SourceNode {
|
||||
/** Gets a parameter of this function. */
|
||||
ParameterNode getAParameter() { result = this.getParameter(_) }
|
||||
|
||||
/** Gets the parameter named `name` of this function, if any. */
|
||||
DataFlow::ParameterNode getParameterByName(string name) {
|
||||
result = this.getAParameter() and
|
||||
result.getName() = name
|
||||
}
|
||||
|
||||
/** Gets the number of parameters declared on this function. */
|
||||
int getNumParameter() { result = count(astNode.getAParameter()) }
|
||||
|
||||
@@ -556,6 +562,14 @@ class ObjectLiteralNode extends DataFlow::ValueNode, DataFlow::SourceNode {
|
||||
DataFlow::FunctionNode getPropertySetter(string name) {
|
||||
result = astNode.getPropertyByName(name).(PropertySetter).getInit().flow()
|
||||
}
|
||||
|
||||
/** Gets the value of a computed property name of this object literal, such as `x` in `{[x]: 1}` */
|
||||
DataFlow::Node getAComputedPropertyName() {
|
||||
exists(Property prop | prop = astNode.getAProperty() |
|
||||
prop.isComputed() and
|
||||
result = prop.getNameExpr().flow()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -482,17 +482,13 @@ module TaintTracking {
|
||||
*/
|
||||
private class HeapTaintStep extends SharedTaintStep {
|
||||
override predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(Expr e, Expr f | e = succ.asExpr() and f = pred.asExpr() |
|
||||
exists(Property prop | e.(ObjectExpr).getAProperty() = prop |
|
||||
prop.isComputed() and f = prop.getNameExpr()
|
||||
)
|
||||
succ.(DataFlow::ObjectLiteralNode).getAComputedPropertyName() = pred
|
||||
or
|
||||
// spreading a tainted object into an object literal gives a tainted object
|
||||
e.(ObjectExpr).getAProperty().(SpreadProperty).getInit().(SpreadElement).getOperand() = f
|
||||
succ.(DataFlow::ObjectLiteralNode).getASpreadProperty() = pred
|
||||
or
|
||||
// spreading a tainted value into an array literal gives a tainted array
|
||||
e.(ArrayExpr).getAnElement().(SpreadElement).getOperand() = f
|
||||
)
|
||||
succ.(DataFlow::ArrayCreationNode).getASpreadArgument() = pred
|
||||
or
|
||||
// arrays with tainted elements and objects with tainted property names are tainted
|
||||
succ.(DataFlow::ArrayCreationNode).getAnElement() = pred and
|
||||
@@ -546,16 +542,16 @@ module TaintTracking {
|
||||
*/
|
||||
private class ComputedPropWriteTaintStep extends SharedTaintStep {
|
||||
override predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(AssignExpr assgn, IndexExpr idx, DataFlow::SourceNode obj |
|
||||
assgn.getTarget() = idx and
|
||||
obj.flowsToExpr(idx.getBase()) and
|
||||
not exists(idx.getPropertyName()) and
|
||||
pred = DataFlow::valueNode(assgn.getRhs()) and
|
||||
exists(DataFlow::PropWrite assgn, DataFlow::SourceNode obj |
|
||||
not exists(assgn.getPropertyName()) and
|
||||
not assgn.getWriteNode() instanceof Property and // not a write inside an object literal
|
||||
pred = assgn.getRhs() and
|
||||
assgn = obj.getAPropertyWrite() and
|
||||
succ = obj
|
||||
|
|
||||
obj instanceof DataFlow::ObjectLiteralNode
|
||||
or
|
||||
obj.getAPropertyRead("length").flowsToExpr(idx.getPropertyNameExpr())
|
||||
obj.getAPropertyRead("length").flowsToExpr(assgn.getPropertyNameExpr())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -580,8 +576,8 @@ module TaintTracking {
|
||||
override predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node target) {
|
||||
exists(DataFlow::ValueNode succ | target = succ |
|
||||
// string operations that propagate taint
|
||||
exists(string name | name = succ.getAstNode().(MethodCallExpr).getMethodName() |
|
||||
pred.asExpr() = succ.getAstNode().(MethodCallExpr).getReceiver() and
|
||||
exists(string name | name = succ.(DataFlow::MethodCallNode).getMethodName() |
|
||||
pred = succ.(DataFlow::MethodCallNode).getReceiver() and
|
||||
(
|
||||
// sorted, interesting, properties of String.prototype
|
||||
name =
|
||||
@@ -600,7 +596,7 @@ module TaintTracking {
|
||||
name = "join"
|
||||
)
|
||||
or
|
||||
exists(int i | pred.asExpr() = succ.getAstNode().(MethodCallExpr).getArgument(i) |
|
||||
exists(int i | pred = succ.(DataFlow::MethodCallNode).getArgument(i) |
|
||||
name = "concat"
|
||||
or
|
||||
name = ["replace", "replaceAll"] and i = 1
|
||||
@@ -615,10 +611,10 @@ module TaintTracking {
|
||||
)
|
||||
or
|
||||
// String.fromCharCode and String.fromCodePoint
|
||||
exists(int i, MethodCallExpr mce |
|
||||
mce = succ.getAstNode() and
|
||||
pred.asExpr() = mce.getArgument(i) and
|
||||
(mce.getMethodName() = "fromCharCode" or mce.getMethodName() = "fromCodePoint")
|
||||
exists(int i, DataFlow::MethodCallNode mcn |
|
||||
mcn = succ and
|
||||
pred = mcn.getArgument(i) and
|
||||
mcn.getMethodName() = ["fromCharCode", "fromCodePoint"]
|
||||
)
|
||||
or
|
||||
// `(encode|decode)URI(Component)?` propagate taint
|
||||
@@ -744,11 +740,11 @@ module TaintTracking {
|
||||
* the parameters in `input`.
|
||||
*/
|
||||
predicate isUrlSearchParams(DataFlow::SourceNode params, DataFlow::Node input) {
|
||||
exists(DataFlow::GlobalVarRefNode urlSearchParams, NewExpr newUrlSearchParams |
|
||||
exists(DataFlow::GlobalVarRefNode urlSearchParams, DataFlow::NewNode newUrlSearchParams |
|
||||
urlSearchParams.getName() = "URLSearchParams" and
|
||||
newUrlSearchParams = urlSearchParams.getAnInstantiation().asExpr() and
|
||||
params.asExpr() = newUrlSearchParams and
|
||||
input.asExpr() = newUrlSearchParams.getArgument(0)
|
||||
newUrlSearchParams = urlSearchParams.getAnInstantiation() and
|
||||
params = newUrlSearchParams and
|
||||
input = newUrlSearchParams.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,19 +8,19 @@ module AWS {
|
||||
/**
|
||||
* Holds if the `i`th argument of `invk` is an object hash for `AWS.Config`.
|
||||
*/
|
||||
private predicate takesConfigurationObject(InvokeExpr invk, int i) {
|
||||
private predicate takesConfigurationObject(DataFlow::InvokeNode invk, int i) {
|
||||
exists(DataFlow::ModuleImportNode mod | mod.getPath() = "aws-sdk" |
|
||||
// `AWS.config.update(nd)`
|
||||
invk = mod.getAPropertyRead("config").getAMemberCall("update").asExpr() and
|
||||
invk = mod.getAPropertyRead("config").getAMemberCall("update") and
|
||||
i = 0
|
||||
or
|
||||
exists(DataFlow::SourceNode cfg | cfg = mod.getAConstructorInvocation("Config") |
|
||||
// `new AWS.Config(nd)`
|
||||
invk = cfg.asExpr() and
|
||||
invk = cfg and
|
||||
i = 0
|
||||
or
|
||||
// `var config = new AWS.Config(...); config.update(nd);`
|
||||
invk = cfg.getAMemberCall("update").asExpr() and
|
||||
invk = cfg.getAMemberCall("update") and
|
||||
i = 0
|
||||
)
|
||||
)
|
||||
@@ -29,13 +29,13 @@ module AWS {
|
||||
/**
|
||||
* An expression that is used as an AWS config value: `{ accessKeyId: <user>, secretAccessKey: <password>}`.
|
||||
*/
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string prop, InvokeExpr invk, int i |
|
||||
exists(string prop, DataFlow::InvokeNode invk, int i |
|
||||
takesConfigurationObject(invk, i) and
|
||||
invk.hasOptionArgument(i, prop, this)
|
||||
this = invk.getOptionArgument(i, prop)
|
||||
|
|
||||
prop = "accessKeyId" and kind = "user name"
|
||||
or
|
||||
|
||||
@@ -68,7 +68,7 @@ private class TrackStringsInAngularCode extends DataFlow::SourceNode::Range, Dat
|
||||
*/
|
||||
private DataFlow::CallNode angularModuleCall(string name) {
|
||||
result = angular().getAMemberCall("module") and
|
||||
result.getArgument(0).asExpr().mayHaveStringValue(name)
|
||||
result.getArgument(0).mayHaveStringValue(name)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,8 +105,8 @@ class AngularModule extends TAngularModule {
|
||||
/**
|
||||
* Get the array of dependencies from this module's definition.
|
||||
*/
|
||||
ArrayExpr getDependencyArray() {
|
||||
this.getADefinition().getArgument(1).getALocalSource().asExpr() = result
|
||||
DataFlow::ArrayCreationNode getDependencyArray() {
|
||||
this.getADefinition().getArgument(1).getALocalSource() = result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,14 +160,10 @@ class ModuleApiCall extends DataFlow::CallNode {
|
||||
string getMethodName() { result = methodName }
|
||||
}
|
||||
|
||||
class ModuleApiCallDependencyInjection extends DependencyInjection {
|
||||
ModuleApiCall call;
|
||||
class ModuleApiCallDependencyInjection extends DependencyInjection instanceof ModuleApiCall {
|
||||
string methodName;
|
||||
|
||||
ModuleApiCallDependencyInjection() {
|
||||
this = call and
|
||||
methodName = call.getMethodName()
|
||||
}
|
||||
ModuleApiCallDependencyInjection() { methodName = super.getMethodName() }
|
||||
|
||||
/**
|
||||
* Gets the argument position for this method call that expects an injectable function.
|
||||
@@ -183,7 +179,7 @@ class ModuleApiCallDependencyInjection extends DependencyInjection {
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInjectableFunction() {
|
||||
result = call.getArgument(this.injectableArgPos())
|
||||
result = super.getArgument(this.injectableArgPos())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,10 +262,10 @@ abstract class CustomDirective extends DirectiveInstance {
|
||||
DataFlow::SourceNode getMember(string name) { result.flowsTo(this.getMemberInit(name)) }
|
||||
|
||||
/** Gets the method `name` of this directive. */
|
||||
Function getMethod(string name) { DataFlow::valueNode(result) = this.getMember(name) }
|
||||
DataFlow::FunctionNode getMethod(string name) { result = this.getMember(name) }
|
||||
|
||||
/** Gets a link function of this directive. */
|
||||
abstract Function getALinkFunction();
|
||||
abstract DataFlow::FunctionNode getALinkFunction();
|
||||
|
||||
/** Holds if this directive's properties are bound to the controller. */
|
||||
abstract predicate bindsToController();
|
||||
@@ -284,7 +280,7 @@ abstract class CustomDirective extends DirectiveInstance {
|
||||
InjectableFunction getController() { result = this.getMember("controller") }
|
||||
|
||||
/** Gets the template URL of this directive, if any. */
|
||||
string getTemplateUrl() { this.getMember("templateUrl").asExpr().mayHaveStringValue(result) }
|
||||
string getTemplateUrl() { this.getMember("templateUrl").mayHaveStringValue(result) }
|
||||
|
||||
/**
|
||||
* Gets a template file for this directive, if any.
|
||||
@@ -302,9 +298,7 @@ abstract class CustomDirective extends DirectiveInstance {
|
||||
else result = DirectiveInstance.super.getAScope()
|
||||
}
|
||||
|
||||
private string getRestrictionString() {
|
||||
this.getMember("restrict").asExpr().mayHaveStringValue(result)
|
||||
}
|
||||
private string getRestrictionString() { this.getMember("restrict").mayHaveStringValue(result) }
|
||||
|
||||
private predicate hasTargetType(DirectiveTargetType type) {
|
||||
not exists(this.getRestrictionString()) or
|
||||
@@ -332,10 +326,10 @@ class GeneralDirective extends CustomDirective, MkCustomDirective {
|
||||
|
||||
/** Gets a node that contributes to the return value of the factory function. */
|
||||
override DataFlow::SourceNode getAnInstantiation() {
|
||||
exists(Function factory, InjectableFunction f |
|
||||
exists(DataFlow::FunctionNode factory, InjectableFunction f |
|
||||
f = definition.getAFactoryFunction() and
|
||||
factory = f.asFunction() and
|
||||
result.flowsToExpr(factory.getAReturnedExpr())
|
||||
result.flowsTo(factory.getAReturn())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -344,7 +338,7 @@ class GeneralDirective extends CustomDirective, MkCustomDirective {
|
||||
}
|
||||
|
||||
/** Gets the compile function of this directive, if any. */
|
||||
Function getCompileFunction() { result = this.getMethod("compile") }
|
||||
DataFlow::FunctionNode getCompileFunction() { result = this.getMethod("compile") }
|
||||
|
||||
/**
|
||||
* Gets a pre/post link function of this directive defined on its definition object.
|
||||
@@ -365,9 +359,8 @@ class GeneralDirective extends CustomDirective, MkCustomDirective {
|
||||
result = this.getMember("link").getAPropertySource(kind)
|
||||
or
|
||||
// { compile: function() { ... return link; } }
|
||||
exists(Expr compileReturn, DataFlow::SourceNode compileReturnSrc |
|
||||
compileReturn = this.getCompileFunction().getAReturnedExpr() and
|
||||
compileReturnSrc.flowsToExpr(compileReturn)
|
||||
exists(DataFlow::SourceNode compileReturnSrc |
|
||||
compileReturnSrc.flowsTo(this.getCompileFunction().getAReturn())
|
||||
|
|
||||
// link = function postLink() { ... }
|
||||
kind = "post" and
|
||||
@@ -380,18 +373,20 @@ class GeneralDirective extends CustomDirective, MkCustomDirective {
|
||||
}
|
||||
|
||||
/** Gets the pre-link function of this directive. */
|
||||
Function getPreLinkFunction() { result = this.getLinkFunction("pre").getAstNode() }
|
||||
DataFlow::FunctionNode getPreLinkFunction() { result = this.getLinkFunction("pre") }
|
||||
|
||||
/** Gets the post-link function of this directive. */
|
||||
Function getPostLinkFunction() { result = this.getLinkFunction("post").getAstNode() }
|
||||
DataFlow::FunctionNode getPostLinkFunction() { result = this.getLinkFunction("post") }
|
||||
|
||||
override Function getALinkFunction() { result = this.getLinkFunction(_).getAstNode() }
|
||||
override DataFlow::FunctionNode getALinkFunction() { result = this.getLinkFunction(_) }
|
||||
|
||||
override predicate bindsToController() {
|
||||
this.getMemberInit("bindToController").asExpr().mayHaveBooleanValue(true)
|
||||
this.getMemberInit("bindToController").mayHaveBooleanValue(true)
|
||||
}
|
||||
|
||||
override predicate hasIsolateScope() { this.getMember("scope").asExpr() instanceof ObjectExpr }
|
||||
override predicate hasIsolateScope() {
|
||||
this.getMember("scope") instanceof DataFlow::ObjectLiteralNode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,7 +407,7 @@ class ComponentDirective extends CustomDirective, MkCustomComponent {
|
||||
comp.getConfig().hasPropertyWrite(name, result)
|
||||
}
|
||||
|
||||
override Function getALinkFunction() { none() }
|
||||
override DataFlow::FunctionNode getALinkFunction() { none() }
|
||||
|
||||
override predicate bindsToController() { none() }
|
||||
|
||||
@@ -561,13 +556,12 @@ class DirectiveTargetName extends string {
|
||||
*
|
||||
* See https://docs.angularjs.org/api/ng/service/$location for details.
|
||||
*/
|
||||
private class LocationFlowSource extends RemoteFlowSource {
|
||||
private class LocationFlowSource extends RemoteFlowSource instanceof DataFlow::MethodCallNode {
|
||||
LocationFlowSource() {
|
||||
exists(ServiceReference service, MethodCallExpr mce, string m, int n |
|
||||
exists(ServiceReference service, string m, int n |
|
||||
service.getName() = "$location" and
|
||||
this.asExpr() = mce and
|
||||
mce = service.getAMethodCall(m) and
|
||||
n = mce.getNumArgument()
|
||||
this = service.getAMethodCall(m) and
|
||||
n = super.getNumArgument()
|
||||
|
|
||||
m = "search" and n < 2
|
||||
or
|
||||
@@ -605,7 +599,7 @@ private class JQLiteObject extends JQuery::ObjectSource::Range {
|
||||
JQLiteObject() {
|
||||
this = angular().getAMemberCall("element")
|
||||
or
|
||||
exists(Parameter param | this = DataFlow::parameterNode(param) |
|
||||
exists(DataFlow::ParameterNode param | this = param |
|
||||
// element parameters to user-functions invoked by AngularJS
|
||||
param = any(LinkFunction link).getElementParameter()
|
||||
or
|
||||
@@ -613,13 +607,13 @@ private class JQLiteObject extends JQuery::ObjectSource::Range {
|
||||
param = d.getCompileFunction().getParameter(0)
|
||||
or
|
||||
exists(DataFlow::FunctionNode f, int i |
|
||||
f.flowsToExpr(d.getCompileFunction().getAReturnedExpr()) and i = 1
|
||||
f.flowsTo(d.getCompileFunction().getAReturn()) and i = 1
|
||||
or
|
||||
f = d.getMember("template") and i = 0
|
||||
or
|
||||
f = d.getMember("templateUrl") and i = 0
|
||||
|
|
||||
param = f.getAstNode().(Function).getParameter(i)
|
||||
param = f.getParameter(i)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -631,99 +625,111 @@ private class JQLiteObject extends JQuery::ObjectSource::Range {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `AngularJSCallNode` instead.
|
||||
* A call to an AngularJS function.
|
||||
*
|
||||
* Used for exposing behavior that is similar to the behavior of other libraries.
|
||||
*/
|
||||
abstract class AngularJSCall extends CallExpr {
|
||||
deprecated class AngularJSCall extends CallExpr {
|
||||
AngularJSCallNode node;
|
||||
|
||||
AngularJSCall() { this.flow() = node }
|
||||
|
||||
/** Holds if `e` is an argument that this call interprets as HTML. */
|
||||
deprecated predicate interpretsArgumentAsHtml(Expr e) { node.interpretsArgumentAsHtml(e.flow()) }
|
||||
|
||||
/** Holds if `e` is an argument that this call stores globally, e.g. in a cookie. */
|
||||
deprecated predicate storesArgumentGlobally(Expr e) { node.storesArgumentGlobally(e.flow()) }
|
||||
|
||||
/** Holds if `e` is an argument that this call interprets as code. */
|
||||
deprecated predicate interpretsArgumentAsCode(Expr e) { node.interpretsArgumentAsCode(e.flow()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to an AngularJS function.
|
||||
*
|
||||
* Used for exposing behavior that is similar to the behavior of other libraries.
|
||||
*/
|
||||
abstract class AngularJSCallNode extends DataFlow::CallNode {
|
||||
/**
|
||||
* Holds if `e` is an argument that this call interprets as HTML.
|
||||
*/
|
||||
abstract predicate interpretsArgumentAsHtml(Expr e);
|
||||
abstract predicate interpretsArgumentAsHtml(DataFlow::Node e);
|
||||
|
||||
/**
|
||||
* Holds if `e` is an argument that this call stores globally, e.g. in a cookie.
|
||||
*/
|
||||
abstract predicate storesArgumentGlobally(Expr e);
|
||||
abstract predicate storesArgumentGlobally(DataFlow::Node e);
|
||||
|
||||
/**
|
||||
* Holds if `e` is an argument that this call interprets as code.
|
||||
*/
|
||||
abstract predicate interpretsArgumentAsCode(Expr e);
|
||||
abstract predicate interpretsArgumentAsCode(DataFlow::Node e);
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a method on the AngularJS object itself.
|
||||
*/
|
||||
private class AngularMethodCall extends AngularJSCall {
|
||||
MethodCallExpr mce;
|
||||
private class AngularMethodCall extends AngularJSCallNode {
|
||||
AngularMethodCall() { this = angular().getAMemberCall(_) }
|
||||
|
||||
AngularMethodCall() {
|
||||
mce = angular().getAMemberCall(_).asExpr() and
|
||||
mce = this
|
||||
override predicate interpretsArgumentAsHtml(DataFlow::Node e) {
|
||||
super.getCalleeName() = "element" and
|
||||
e = super.getArgument(0)
|
||||
}
|
||||
|
||||
override predicate interpretsArgumentAsHtml(Expr e) {
|
||||
mce.getMethodName() = "element" and
|
||||
e = mce.getArgument(0)
|
||||
}
|
||||
override predicate storesArgumentGlobally(DataFlow::Node e) { none() }
|
||||
|
||||
override predicate storesArgumentGlobally(Expr e) { none() }
|
||||
|
||||
override predicate interpretsArgumentAsCode(Expr e) { none() }
|
||||
override predicate interpretsArgumentAsCode(DataFlow::Node e) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a builtin service or one of its methods.
|
||||
*/
|
||||
private class BuiltinServiceCall extends AngularJSCall {
|
||||
CallExpr call;
|
||||
|
||||
private class BuiltinServiceCall extends AngularJSCallNode {
|
||||
BuiltinServiceCall() {
|
||||
exists(BuiltinServiceReference service |
|
||||
service.getAMethodCall(_) = this or
|
||||
service.getACall() = this
|
||||
|
|
||||
call = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate interpretsArgumentAsHtml(Expr e) {
|
||||
override predicate interpretsArgumentAsHtml(DataFlow::Node e) {
|
||||
exists(ServiceReference service, string methodName |
|
||||
service.getName() = "$sce" and
|
||||
call = service.getAMethodCall(methodName)
|
||||
this = service.getAMethodCall(methodName)
|
||||
|
|
||||
// specialized call
|
||||
(methodName = "trustAsHtml" or methodName = "trustAsCss") and
|
||||
e = call.getArgument(0)
|
||||
e = this.getArgument(0)
|
||||
or
|
||||
// generic call with enum argument
|
||||
methodName = "trustAs" and
|
||||
exists(DataFlow::PropRead prn |
|
||||
prn.asExpr() = call.getArgument(0) and
|
||||
prn = this.getArgument(0) and
|
||||
(prn = service.getAPropertyAccess("HTML") or prn = service.getAPropertyAccess("CSS")) and
|
||||
e = call.getArgument(1)
|
||||
e = this.getArgument(1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate storesArgumentGlobally(Expr e) {
|
||||
override predicate storesArgumentGlobally(DataFlow::Node e) {
|
||||
exists(ServiceReference service, string serviceName, string methodName |
|
||||
service.getName() = serviceName and
|
||||
call = service.getAMethodCall(methodName)
|
||||
this = service.getAMethodCall(methodName)
|
||||
|
|
||||
// AngularJS caches (only available during runtime, so similar to sessionStorage)
|
||||
(serviceName = "$cacheFactory" or serviceName = "$templateCache") and
|
||||
methodName = "put" and
|
||||
e = call.getArgument(1)
|
||||
e = this.getArgument(1)
|
||||
or
|
||||
serviceName = "$cookies" and
|
||||
(methodName = "put" or methodName = "putObject") and
|
||||
e = call.getArgument(1)
|
||||
e = this.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate interpretsArgumentAsCode(Expr e) {
|
||||
override predicate interpretsArgumentAsCode(DataFlow::Node e) {
|
||||
exists(ScopeServiceReference scope, string methodName |
|
||||
methodName =
|
||||
[
|
||||
@@ -731,22 +737,21 @@ private class BuiltinServiceCall extends AngularJSCall {
|
||||
"$watchGroup"
|
||||
]
|
||||
|
|
||||
call = scope.getAMethodCall(methodName) and
|
||||
e = call.getArgument(0)
|
||||
this = scope.getAMethodCall(methodName) and
|
||||
e = this.getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(ServiceReference service | service.getName() = ["$compile", "$parse", "$interpolate"] |
|
||||
call = service.getACall() and
|
||||
e = call.getArgument(0)
|
||||
this = service.getACall() and
|
||||
e = this.getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(ServiceReference service, CallExpr filterInvocation |
|
||||
exists(ServiceReference service |
|
||||
// `$filter('orderBy')(collection, expression)`
|
||||
service.getName() = "$filter" and
|
||||
call = service.getACall() and
|
||||
call.getArgument(0).mayHaveStringValue("orderBy") and
|
||||
filterInvocation.getCallee() = call and
|
||||
e = filterInvocation.getArgument(1)
|
||||
this = service.getACall() and
|
||||
this.getArgument(0).mayHaveStringValue("orderBy") and
|
||||
e = this.getACall().getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -754,33 +759,33 @@ private class BuiltinServiceCall extends AngularJSCall {
|
||||
/**
|
||||
* A link-function used in a custom AngularJS directive.
|
||||
*/
|
||||
class LinkFunction extends Function {
|
||||
class LinkFunction extends DataFlow::FunctionNode {
|
||||
LinkFunction() { this = any(GeneralDirective d).getALinkFunction() }
|
||||
|
||||
/**
|
||||
* Gets the scope parameter of this function.
|
||||
*/
|
||||
Parameter getScopeParameter() { result = this.getParameter(0) }
|
||||
DataFlow::ParameterNode getScopeParameter() { result = this.getParameter(0) }
|
||||
|
||||
/**
|
||||
* Gets the element parameter of this function (contains a jqLite-wrapped DOM element).
|
||||
*/
|
||||
Parameter getElementParameter() { result = this.getParameter(1) }
|
||||
DataFlow::ParameterNode getElementParameter() { result = this.getParameter(1) }
|
||||
|
||||
/**
|
||||
* Gets the attributes parameter of this function.
|
||||
*/
|
||||
Parameter getAttributesParameter() { result = this.getParameter(2) }
|
||||
DataFlow::ParameterNode getAttributesParameter() { result = this.getParameter(2) }
|
||||
|
||||
/**
|
||||
* Gets the controller parameter of this function.
|
||||
*/
|
||||
Parameter getControllerParameter() { result = this.getParameter(3) }
|
||||
DataFlow::ParameterNode getControllerParameter() { result = this.getParameter(3) }
|
||||
|
||||
/**
|
||||
* Gets the transclude-function parameter of this function.
|
||||
*/
|
||||
Parameter getTranscludeFnParameter() { result = this.getParameter(4) }
|
||||
DataFlow::ParameterNode getTranscludeFnParameter() { result = this.getParameter(4) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,29 +812,29 @@ class AngularScope extends TAngularScope {
|
||||
/**
|
||||
* Gets an access to this scope object.
|
||||
*/
|
||||
Expr getAnAccess() {
|
||||
DataFlow::Node getAnAccess() {
|
||||
exists(CustomDirective d | this = d.getAScope() |
|
||||
exists(Parameter p |
|
||||
exists(DataFlow::ParameterNode p |
|
||||
p = d.getController().getDependencyParameter("$scope") or
|
||||
p = d.getALinkFunction().getParameter(0)
|
||||
|
|
||||
result.mayReferToParameter(p)
|
||||
p.flowsTo(result)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ThisNode dis |
|
||||
dis.flowsToExpr(result) and
|
||||
dis.getBinder().getAstNode() = d.getController().asFunction() and
|
||||
dis.flowsTo(result) and
|
||||
dis.getBinder() = d.getController().asFunction() and
|
||||
d.bindsToController()
|
||||
)
|
||||
or
|
||||
d.hasIsolateScope() and result = d.getMember("scope").asExpr()
|
||||
d.hasIsolateScope() and result = d.getMember("scope")
|
||||
)
|
||||
or
|
||||
exists(DirectiveController c, DOM::ElementDefinition elem, Parameter p |
|
||||
exists(DirectiveController c, DOM::ElementDefinition elem, DataFlow::ParameterNode p |
|
||||
c.boundTo(elem) and
|
||||
this.mayApplyTo(elem) and
|
||||
p = c.getFactoryFunction().getDependencyParameter("$scope") and
|
||||
result.mayReferToParameter(p)
|
||||
p.flowsTo(result)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -925,9 +930,7 @@ class RouteSetup extends DataFlow::CallNode, DependencyInjection {
|
||||
|
|
||||
result = controllerProperty
|
||||
or
|
||||
exists(ControllerDefinition def |
|
||||
controllerProperty.asExpr().mayHaveStringValue(def.getName())
|
||||
|
|
||||
exists(ControllerDefinition def | controllerProperty.mayHaveStringValue(def.getName()) |
|
||||
result = def.getAService()
|
||||
)
|
||||
)
|
||||
@@ -1007,7 +1010,7 @@ private class RouteInstantiatedController extends Controller {
|
||||
|
||||
override predicate boundTo(DOM::ElementDefinition elem) {
|
||||
exists(string url, HTML::HtmlFile template |
|
||||
setup.getRouteParam("templateUrl").asExpr().mayHaveStringValue(url) and
|
||||
setup.getRouteParam("templateUrl").mayHaveStringValue(url) and
|
||||
template.getAbsolutePath().regexpMatch(".*\\Q" + url + "\\E") and
|
||||
elem.getFile() = template
|
||||
)
|
||||
@@ -1015,22 +1018,19 @@ private class RouteInstantiatedController extends Controller {
|
||||
|
||||
override predicate boundToAs(DOM::ElementDefinition elem, string name) {
|
||||
this.boundTo(elem) and
|
||||
setup.getRouteParam("controllerAs").asExpr().mayHaveStringValue(name)
|
||||
setup.getRouteParam("controllerAs").mayHaveStringValue(name)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataflow for the arguments of AngularJS dependency-injected functions.
|
||||
*/
|
||||
private class DependencyInjectedArgumentInitializer extends DataFlow::AnalyzedNode {
|
||||
private class DependencyInjectedArgumentInitializer extends DataFlow::AnalyzedNode instanceof DataFlow::ParameterNode {
|
||||
DataFlow::AnalyzedNode service;
|
||||
|
||||
DependencyInjectedArgumentInitializer() {
|
||||
exists(
|
||||
AngularJS::InjectableFunction f, Parameter param, AngularJS::CustomServiceDefinition def
|
||||
|
|
||||
this = DataFlow::parameterNode(param) and
|
||||
def.getServiceReference() = f.getAResolvedDependency(param) and
|
||||
exists(AngularJS::InjectableFunction f, AngularJS::CustomServiceDefinition def |
|
||||
def.getServiceReference() = f.getAResolvedDependency(this) and
|
||||
service = def.getAService()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,33 +41,35 @@ abstract class DependencyInjection extends DataFlow::ValueNode {
|
||||
*/
|
||||
abstract class InjectableFunction extends DataFlow::ValueNode {
|
||||
/** Gets the parameter corresponding to dependency `name`. */
|
||||
abstract Parameter getDependencyParameter(string name);
|
||||
abstract DataFlow::ParameterNode getDependencyParameter(string name);
|
||||
|
||||
/**
|
||||
* Gets the `i`th dependency declaration, which is also named `name`.
|
||||
*/
|
||||
abstract AstNode getDependencyDeclaration(int i, string name);
|
||||
abstract DataFlow::Node getDependencyDeclaration(int i, string name);
|
||||
|
||||
/**
|
||||
* Gets an ASTNode for the `name` dependency declaration.
|
||||
* Gets a node for the `name` dependency declaration.
|
||||
*/
|
||||
AstNode getADependencyDeclaration(string name) { result = getDependencyDeclaration(_, name) }
|
||||
DataFlow::Node getADependencyDeclaration(string name) {
|
||||
result = getDependencyDeclaration(_, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ASTNode for the `i`th dependency declaration.
|
||||
* Gets the dataflow node for the `i`th dependency declaration.
|
||||
*/
|
||||
AstNode getDependencyDeclaration(int i) { result = getDependencyDeclaration(i, _) }
|
||||
DataFlow::Node getDependencyDeclaration(int i) { result = getDependencyDeclaration(i, _) }
|
||||
|
||||
/** Gets the function underlying this injectable function. */
|
||||
abstract Function asFunction();
|
||||
abstract DataFlow::FunctionNode asFunction();
|
||||
|
||||
/** Gets a location where this function is explicitly dependency injected. */
|
||||
abstract AstNode getAnExplicitDependencyInjection();
|
||||
/** Gets a node where this function is explicitly dependency injected. */
|
||||
abstract DataFlow::Node getAnExplicitDependencyInjection();
|
||||
|
||||
/**
|
||||
* Gets a service corresponding to the dependency-injected `parameter`.
|
||||
*/
|
||||
ServiceReference getAResolvedDependency(Parameter parameter) {
|
||||
ServiceReference getAResolvedDependency(DataFlow::ParameterNode parameter) {
|
||||
exists(string name, InjectableFunctionServiceRequest request |
|
||||
this = request.getAnInjectedFunction() and
|
||||
parameter = getDependencyParameter(name) and
|
||||
@@ -79,7 +81,7 @@ abstract class InjectableFunction extends DataFlow::ValueNode {
|
||||
* Gets a Custom service corresponding to the dependency-injected `parameter`.
|
||||
* (this is a convenience variant of `getAResolvedDependency`)
|
||||
*/
|
||||
DataFlow::Node getCustomServiceDependency(Parameter parameter) {
|
||||
DataFlow::Node getCustomServiceDependency(DataFlow::ParameterNode parameter) {
|
||||
exists(CustomServiceDefinition custom |
|
||||
custom.getServiceReference() = getAResolvedDependency(parameter) and
|
||||
result = custom.getAService()
|
||||
@@ -91,100 +93,88 @@ abstract class InjectableFunction extends DataFlow::ValueNode {
|
||||
* An injectable function that does not explicitly list its dependencies,
|
||||
* instead relying on implicit matching by parameter names.
|
||||
*/
|
||||
private class FunctionWithImplicitDependencyAnnotation extends InjectableFunction {
|
||||
override Function astNode;
|
||||
|
||||
private class FunctionWithImplicitDependencyAnnotation extends InjectableFunction instanceof DataFlow::FunctionNode {
|
||||
FunctionWithImplicitDependencyAnnotation() {
|
||||
this.(DataFlow::FunctionNode).flowsTo(any(DependencyInjection d).getAnInjectableFunction()) and
|
||||
not exists(getAPropertyDependencyInjection(astNode))
|
||||
not exists(getAPropertyDependencyInjection(this))
|
||||
}
|
||||
|
||||
override Parameter getDependencyParameter(string name) {
|
||||
result = astNode.getParameterByName(name)
|
||||
override DataFlow::ParameterNode getDependencyParameter(string name) {
|
||||
result = super.getParameterByName(name)
|
||||
}
|
||||
|
||||
override Parameter getDependencyDeclaration(int i, string name) {
|
||||
override DataFlow::ParameterNode getDependencyDeclaration(int i, string name) {
|
||||
result.getName() = name and
|
||||
result = astNode.getParameter(i)
|
||||
result = super.getParameter(i)
|
||||
}
|
||||
|
||||
override Function asFunction() { result = astNode }
|
||||
override DataFlow::FunctionNode asFunction() { result = this }
|
||||
|
||||
override AstNode getAnExplicitDependencyInjection() { none() }
|
||||
override DataFlow::Node getAnExplicitDependencyInjection() { none() }
|
||||
}
|
||||
|
||||
private DataFlow::PropWrite getAPropertyDependencyInjection(Function function) {
|
||||
exists(DataFlow::FunctionNode ltf |
|
||||
ltf.getAstNode() = function and
|
||||
result = ltf.getAPropertyWrite("$inject")
|
||||
)
|
||||
private DataFlow::PropWrite getAPropertyDependencyInjection(DataFlow::FunctionNode function) {
|
||||
result = function.getAPropertyWrite("$inject")
|
||||
}
|
||||
|
||||
/**
|
||||
* An injectable function with an `$inject` property that lists its
|
||||
* dependencies.
|
||||
*/
|
||||
private class FunctionWithInjectProperty extends InjectableFunction {
|
||||
override Function astNode;
|
||||
private class FunctionWithInjectProperty extends InjectableFunction instanceof DataFlow::FunctionNode {
|
||||
DataFlow::ArrayCreationNode dependencies;
|
||||
|
||||
FunctionWithInjectProperty() {
|
||||
(
|
||||
this.(DataFlow::FunctionNode).flowsTo(any(DependencyInjection d).getAnInjectableFunction()) or
|
||||
exists(FunctionWithExplicitDependencyAnnotation f | f.asFunction() = astNode)
|
||||
exists(FunctionWithExplicitDependencyAnnotation f | f.asFunction() = this)
|
||||
) and
|
||||
exists(DataFlow::PropWrite pwn |
|
||||
pwn = getAPropertyDependencyInjection(astNode) and
|
||||
pwn = getAPropertyDependencyInjection(this) and
|
||||
pwn.getRhs().getALocalSource() = dependencies
|
||||
)
|
||||
}
|
||||
|
||||
override Parameter getDependencyParameter(string name) {
|
||||
exists(int i | exists(getDependencyDeclaration(i, name)) | result = astNode.getParameter(i))
|
||||
override DataFlow::ParameterNode getDependencyParameter(string name) {
|
||||
exists(int i | exists(getDependencyDeclaration(i, name)) | result = super.getParameter(i))
|
||||
}
|
||||
|
||||
override AstNode getDependencyDeclaration(int i, string name) {
|
||||
exists(DataFlow::ValueNode decl |
|
||||
decl = dependencies.getElement(i) and
|
||||
decl.mayHaveStringValue(name) and
|
||||
result = decl.getAstNode()
|
||||
)
|
||||
override DataFlow::Node getDependencyDeclaration(int i, string name) {
|
||||
result = dependencies.getElement(i) and
|
||||
result.mayHaveStringValue(name)
|
||||
}
|
||||
|
||||
override Function asFunction() { result = astNode }
|
||||
override DataFlow::FunctionNode asFunction() { result = this }
|
||||
|
||||
override AstNode getAnExplicitDependencyInjection() {
|
||||
result = getAPropertyDependencyInjection(astNode).getAstNode()
|
||||
override DataFlow::Node getAnExplicitDependencyInjection() {
|
||||
result = getAPropertyDependencyInjection(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An injectable function embedded in an array of dependencies.
|
||||
*/
|
||||
private class FunctionWithExplicitDependencyAnnotation extends InjectableFunction {
|
||||
private class FunctionWithExplicitDependencyAnnotation extends InjectableFunction instanceof DataFlow::ArrayCreationNode {
|
||||
DataFlow::FunctionNode function;
|
||||
override ArrayExpr astNode;
|
||||
|
||||
FunctionWithExplicitDependencyAnnotation() {
|
||||
this.(DataFlow::SourceNode).flowsTo(any(DependencyInjection d).getAnInjectableFunction()) and
|
||||
function.flowsToExpr(astNode.getElement(astNode.getSize() - 1))
|
||||
function.flowsTo(super.getElement(super.getSize() - 1))
|
||||
}
|
||||
|
||||
override Parameter getDependencyParameter(string name) {
|
||||
exists(int i | astNode.getElement(i).mayHaveStringValue(name) |
|
||||
result = asFunction().getParameter(i)
|
||||
)
|
||||
override DataFlow::ParameterNode getDependencyParameter(string name) {
|
||||
exists(int i | super.getElement(i).mayHaveStringValue(name) | result = function.getParameter(i))
|
||||
}
|
||||
|
||||
override AstNode getDependencyDeclaration(int i, string name) {
|
||||
result = astNode.getElement(i) and
|
||||
result.(Expr).mayHaveStringValue(name)
|
||||
override DataFlow::Node getDependencyDeclaration(int i, string name) {
|
||||
result = super.getElement(i) and
|
||||
result.mayHaveStringValue(name)
|
||||
}
|
||||
|
||||
override Function asFunction() { result = function.getAstNode() }
|
||||
override DataFlow::FunctionNode asFunction() { result = function }
|
||||
|
||||
override AstNode getAnExplicitDependencyInjection() {
|
||||
result = astNode or
|
||||
override DataFlow::Node getAnExplicitDependencyInjection() {
|
||||
result = this or
|
||||
result = function.(InjectableFunction).getAnExplicitDependencyInjection()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ private newtype TServiceReference =
|
||||
*/
|
||||
abstract class ServiceReference extends TServiceReference {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getName() }
|
||||
string toString() { result = this.getName() }
|
||||
|
||||
/**
|
||||
* Gets the name of this reference.
|
||||
@@ -38,26 +38,26 @@ abstract class ServiceReference extends TServiceReference {
|
||||
* Gets a data flow node that may refer to this service.
|
||||
*/
|
||||
DataFlow::SourceNode getAReference() {
|
||||
result = DataFlow::parameterNode(any(ServiceRequest request).getDependencyParameter(this))
|
||||
result = any(ServiceRequestNode request).getDependencyParameter(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the referenced service.
|
||||
*/
|
||||
Expr getAnAccess() {
|
||||
result.mayReferToParameter(any(ServiceRequest request).getDependencyParameter(this))
|
||||
DataFlow::Node getAnAccess() {
|
||||
any(ServiceRequestNode request).getDependencyParameter(this).flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call that invokes the referenced service.
|
||||
*/
|
||||
CallExpr getACall() { result.getCallee() = getAnAccess() }
|
||||
DataFlow::CallNode getACall() { result.getCalleeNode() = this.getAnAccess() }
|
||||
|
||||
/**
|
||||
* Gets a method call that invokes method `methodName` on the referenced service.
|
||||
*/
|
||||
MethodCallExpr getAMethodCall(string methodName) {
|
||||
result.getReceiver() = getAnAccess() and
|
||||
DataFlow::MethodCallNode getAMethodCall(string methodName) {
|
||||
result.getReceiver() = this.getAnAccess() and
|
||||
result.getMethodName() = methodName
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ abstract class ServiceReference extends TServiceReference {
|
||||
* Gets an access to property `propertyName` on the referenced service.
|
||||
*/
|
||||
DataFlow::PropRef getAPropertyAccess(string propertyName) {
|
||||
result.getBase().asExpr() = getAnAccess() and
|
||||
result.getBase() = this.getAnAccess() and
|
||||
result.getPropertyName() = propertyName
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ class BuiltinServiceReference extends ServiceReference, MkBuiltinServiceReferenc
|
||||
DataFlow::ParameterNode builtinServiceRef(string serviceName) {
|
||||
exists(InjectableFunction f, BuiltinServiceReference service |
|
||||
service.getName() = serviceName and
|
||||
result = DataFlow::parameterNode(f.getDependencyParameter(serviceName))
|
||||
result = f.getDependencyParameter(serviceName)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -244,17 +244,17 @@ abstract class RecipeDefinition extends DataFlow::CallNode, CustomServiceDefinit
|
||||
this = moduleRef(_).getAMethodCall(methodName) or
|
||||
this = builtinServiceRef("$provide").getAMethodCall(methodName)
|
||||
) and
|
||||
getArgument(0).asExpr().mayHaveStringValue(name)
|
||||
this.getArgument(0).mayHaveStringValue(name)
|
||||
}
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override DataFlow::SourceNode getAFactoryFunction() { result.flowsTo(getArgument(1)) }
|
||||
override DataFlow::SourceNode getAFactoryFunction() { result.flowsTo(this.getArgument(1)) }
|
||||
|
||||
override DataFlow::Node getAnInjectableFunction() {
|
||||
methodName != "value" and
|
||||
methodName != "constant" and
|
||||
result = getAFactoryFunction()
|
||||
result = this.getAFactoryFunction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ abstract class RecipeDefinition extends DataFlow::CallNode, CustomServiceDefinit
|
||||
*/
|
||||
abstract private class CustomSpecialServiceDefinition extends CustomServiceDefinition,
|
||||
DependencyInjection {
|
||||
override DataFlow::Node getAnInjectableFunction() { result = getAFactoryFunction() }
|
||||
override DataFlow::Node getAnInjectableFunction() { result = this.getAFactoryFunction() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,7 +281,7 @@ private predicate isCustomServiceDefinitionOnModule(
|
||||
DataFlow::Node factoryFunction
|
||||
) {
|
||||
mce = moduleRef(_).getAMethodCall(moduleMethodName) and
|
||||
mce.getArgument(0).asExpr().mayHaveStringValue(serviceName) and
|
||||
mce.getArgument(0).mayHaveStringValue(serviceName) and
|
||||
factoryFunction = mce.getArgument(1)
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ private predicate isCustomServiceDefinitionOnProvider(
|
||||
factoryArgument = mce.getOptionArgument(0, serviceName)
|
||||
or
|
||||
mce.getNumArgument() = 2 and
|
||||
mce.getArgument(0).asExpr().mayHaveStringValue(serviceName) and
|
||||
mce.getArgument(0).mayHaveStringValue(serviceName) and
|
||||
factoryArgument = mce.getArgument(1)
|
||||
)
|
||||
}
|
||||
@@ -338,7 +338,7 @@ class FilterDefinition extends CustomSpecialServiceDefinition {
|
||||
override DataFlow::SourceNode getAService() {
|
||||
exists(InjectableFunction f |
|
||||
f = factoryFunction.getALocalSource() and
|
||||
result.flowsToExpr(f.asFunction().getAReturnedExpr())
|
||||
result.flowsTo(f.asFunction().getAReturn())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -428,7 +428,7 @@ class AnimationDefinition extends CustomSpecialServiceDefinition {
|
||||
override DataFlow::SourceNode getAService() {
|
||||
exists(InjectableFunction f |
|
||||
f = factoryFunction.getALocalSource() and
|
||||
result.flowsToExpr(f.asFunction().getAReturnedExpr())
|
||||
result.flowsTo(f.asFunction().getAReturn())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -446,22 +446,37 @@ BuiltinServiceReference getBuiltinServiceOfKind(string kind) {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ServiceRequestNode` instead.
|
||||
* A request for one or more AngularJS services.
|
||||
*/
|
||||
abstract class ServiceRequest extends Expr {
|
||||
deprecated class ServiceRequest extends Expr {
|
||||
ServiceRequestNode node;
|
||||
|
||||
ServiceRequest() { this.flow() = node }
|
||||
|
||||
/** Gets the parameter of this request into which `service` is injected. */
|
||||
deprecated Parameter getDependencyParameter(ServiceReference service) {
|
||||
result.flow() = node.getDependencyParameter(service)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A request for one or more AngularJS services.
|
||||
*/
|
||||
abstract class ServiceRequestNode extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the parameter of this request into which `service` is injected.
|
||||
*/
|
||||
abstract Parameter getDependencyParameter(ServiceReference service);
|
||||
abstract DataFlow::ParameterNode getDependencyParameter(ServiceReference service);
|
||||
}
|
||||
|
||||
/**
|
||||
* The request for a scope service in the form of the link-function of a directive.
|
||||
*/
|
||||
private class LinkFunctionWithScopeInjection extends ServiceRequest {
|
||||
private class LinkFunctionWithScopeInjection extends ServiceRequestNode {
|
||||
LinkFunctionWithScopeInjection() { this instanceof LinkFunction }
|
||||
|
||||
override Parameter getDependencyParameter(ServiceReference service) {
|
||||
override DataFlow::ParameterNode getDependencyParameter(ServiceReference service) {
|
||||
service instanceof ScopeServiceReference and
|
||||
result = this.(LinkFunction).getScopeParameter()
|
||||
}
|
||||
@@ -470,10 +485,10 @@ private class LinkFunctionWithScopeInjection extends ServiceRequest {
|
||||
/**
|
||||
* A request for a service, in the form of a dependency-injected function.
|
||||
*/
|
||||
class InjectableFunctionServiceRequest extends ServiceRequest {
|
||||
class InjectableFunctionServiceRequest extends ServiceRequestNode {
|
||||
InjectableFunction injectedFunction;
|
||||
|
||||
InjectableFunctionServiceRequest() { injectedFunction.getAstNode() = this }
|
||||
InjectableFunctionServiceRequest() { injectedFunction = this }
|
||||
|
||||
/**
|
||||
* Gets the function of this request.
|
||||
@@ -483,7 +498,9 @@ class InjectableFunctionServiceRequest extends ServiceRequest {
|
||||
/**
|
||||
* Gets a name of a requested service.
|
||||
*/
|
||||
string getAServiceName() { exists(getAnInjectedFunction().getADependencyDeclaration(result)) }
|
||||
string getAServiceName() {
|
||||
exists(this.getAnInjectedFunction().getADependencyDeclaration(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a service with the specified name, relative to this request.
|
||||
@@ -494,16 +511,16 @@ class InjectableFunctionServiceRequest extends ServiceRequest {
|
||||
result.isInjectable()
|
||||
}
|
||||
|
||||
override Parameter getDependencyParameter(ServiceReference service) {
|
||||
override DataFlow::ParameterNode getDependencyParameter(ServiceReference service) {
|
||||
service = injectedFunction.getAResolvedDependency(result)
|
||||
}
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode getFactoryFunctionResult(RecipeDefinition def) {
|
||||
exists(Function factoryFunction, InjectableFunction f |
|
||||
exists(DataFlow::FunctionNode factoryFunction, InjectableFunction f |
|
||||
f = def.getAFactoryFunction() and
|
||||
factoryFunction = f.asFunction() and
|
||||
result.flowsToExpr(factoryFunction.getAReturnedExpr())
|
||||
result.flowsTo(factoryFunction.getAReturn())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -561,8 +578,8 @@ class ServiceRecipeDefinition extends RecipeDefinition {
|
||||
*/
|
||||
|
||||
exists(InjectableFunction f |
|
||||
f = getAFactoryFunction() and
|
||||
result.getAstNode() = f.asFunction()
|
||||
f = this.getAFactoryFunction() and
|
||||
result = f.asFunction()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -574,7 +591,7 @@ class ServiceRecipeDefinition extends RecipeDefinition {
|
||||
class ValueRecipeDefinition extends RecipeDefinition {
|
||||
ValueRecipeDefinition() { methodName = "value" }
|
||||
|
||||
override DataFlow::SourceNode getAService() { result = getAFactoryFunction() }
|
||||
override DataFlow::SourceNode getAService() { result = this.getAFactoryFunction() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -584,7 +601,7 @@ class ValueRecipeDefinition extends RecipeDefinition {
|
||||
class ConstantRecipeDefinition extends RecipeDefinition {
|
||||
ConstantRecipeDefinition() { methodName = "constant" }
|
||||
|
||||
override DataFlow::SourceNode getAService() { result = getAFactoryFunction() }
|
||||
override DataFlow::SourceNode getAService() { result = this.getAFactoryFunction() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,8 +624,8 @@ class ProviderRecipeDefinition extends RecipeDefinition {
|
||||
*/
|
||||
|
||||
exists(DataFlow::ThisNode thiz, InjectableFunction f |
|
||||
f = getAFactoryFunction() and
|
||||
thiz.getBinder().getFunction() = f.asFunction() and
|
||||
f = this.getAFactoryFunction() and
|
||||
thiz.getBinder() = f.asFunction() and
|
||||
result = thiz.getAPropertySource("$get")
|
||||
)
|
||||
}
|
||||
@@ -632,7 +649,9 @@ class ConfigMethodDefinition extends ModuleApiCall {
|
||||
/**
|
||||
* Gets a provided configuration method.
|
||||
*/
|
||||
InjectableFunction getConfigMethod() { result.(DataFlow::SourceNode).flowsTo(getArgument(0)) }
|
||||
InjectableFunction getConfigMethod() {
|
||||
result.(DataFlow::SourceNode).flowsTo(this.getArgument(0))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -645,12 +664,12 @@ class RunMethodDefinition extends ModuleApiCall {
|
||||
/**
|
||||
* Gets a provided run method.
|
||||
*/
|
||||
InjectableFunction getRunMethod() { result.(DataFlow::SourceNode).flowsTo(getArgument(0)) }
|
||||
InjectableFunction getRunMethod() { result.(DataFlow::SourceNode).flowsTo(this.getArgument(0)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `$scope` or `$rootScope` service.
|
||||
*/
|
||||
class ScopeServiceReference extends BuiltinServiceReference {
|
||||
ScopeServiceReference() { getName() = "$scope" or getName() = "$rootScope" }
|
||||
ScopeServiceReference() { this.getName() = "$scope" or this.getName() = "$rootScope" }
|
||||
}
|
||||
|
||||
@@ -8,13 +8,14 @@ module Azure {
|
||||
/**
|
||||
* An expression that is used for authentication at Azure`.
|
||||
*/
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(CallExpr mce, string methodName |
|
||||
(methodName = "loginWithUsernamePassword" or methodName = "loginWithServicePrincipalSecret") and
|
||||
mce = DataFlow::moduleMember("ms-rest-azure", methodName).getACall().asExpr()
|
||||
exists(DataFlow::CallNode mce |
|
||||
mce =
|
||||
DataFlow::moduleMember("ms-rest-azure",
|
||||
["loginWithUsernamePassword", "loginWithServicePrincipalSecret"]).getACall()
|
||||
|
|
||||
this = mce.getArgument(0) and kind = "user name"
|
||||
or
|
||||
|
||||
@@ -270,16 +270,16 @@ module ClientRequest {
|
||||
}
|
||||
|
||||
/** An expression that is used as a credential in a request. */
|
||||
private class AuthorizationHeader extends CredentialsExpr {
|
||||
private class AuthorizationHeader extends CredentialsNode {
|
||||
AuthorizationHeader() {
|
||||
exists(DataFlow::PropWrite write | write.getPropertyName().regexpMatch("(?i)authorization") |
|
||||
this = write.getRhs().asExpr()
|
||||
this = write.getRhs()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::MethodCallNode call | call.getMethodName() = ["append", "set"] |
|
||||
call.getNumArgument() = 2 and
|
||||
call.getArgument(0).getStringValue().regexpMatch("(?i)authorization") and
|
||||
this = call.getArgument(1).asExpr()
|
||||
this = call.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ module Connect {
|
||||
/**
|
||||
* An expression that creates a new Connect server.
|
||||
*/
|
||||
class ServerDefinition extends HTTP::Servers::StandardServerDefinition, CallExpr {
|
||||
class ServerDefinition extends HTTP::Servers::StandardServerDefinition, DataFlow::CallNode {
|
||||
ServerDefinition() {
|
||||
// `app = connect()`
|
||||
this = DataFlow::moduleImport("connect").getAnInvocation().asExpr()
|
||||
this = DataFlow::moduleImport("connect").getAnInvocation()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,43 +30,45 @@ module Connect {
|
||||
*
|
||||
* `kind` is one of: "error", "request", "response", "next".
|
||||
*/
|
||||
abstract Parameter getRouteHandlerParameter(string kind);
|
||||
abstract DataFlow::ParameterNode getRouteHandlerParameter(string kind);
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
override Parameter getRequestParameter() { result = getRouteHandlerParameter("request") }
|
||||
override DataFlow::ParameterNode getRequestParameter() {
|
||||
result = getRouteHandlerParameter("request")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the response object.
|
||||
*/
|
||||
override Parameter getResponseParameter() { result = getRouteHandlerParameter("response") }
|
||||
override DataFlow::ParameterNode getResponseParameter() {
|
||||
result = getRouteHandlerParameter("response")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Connect route handler installed by a route setup.
|
||||
*/
|
||||
class StandardRouteHandler extends RouteHandler {
|
||||
override Function astNode;
|
||||
|
||||
class StandardRouteHandler extends RouteHandler, DataFlow::FunctionNode {
|
||||
StandardRouteHandler() { this = any(RouteSetup setup).getARouteHandler() }
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
result = getRouteHandlerParameter(astNode, kind)
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
result = getRouteHandlerParameter(this, kind)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a Connect method that sets up a route.
|
||||
*/
|
||||
class RouteSetup extends MethodCallExpr, HTTP::Servers::StandardRouteSetup {
|
||||
class RouteSetup extends DataFlow::MethodCallNode, HTTP::Servers::StandardRouteSetup {
|
||||
ServerDefinition server;
|
||||
|
||||
RouteSetup() {
|
||||
getMethodName() = "use" and
|
||||
(
|
||||
// app.use(fun)
|
||||
server.flowsTo(getReceiver())
|
||||
server.ref().getAMethodCall() = this
|
||||
or
|
||||
// app.use(...).use(fun)
|
||||
this.getReceiver().(RouteSetup).getServer() = server
|
||||
@@ -79,24 +81,32 @@ module Connect {
|
||||
|
||||
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = getARouteHandlerExpr().flow().getALocalSource()
|
||||
result = getARouteHandlerNode().getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getARouteHandler(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
override Expr getServer() { result = server }
|
||||
override DataFlow::Node getServer() { result = server }
|
||||
|
||||
/** Gets an argument that represents a route handler being registered. */
|
||||
Expr getARouteHandlerExpr() { result = getAnArgument() }
|
||||
/**
|
||||
* DEPRECATED: Use `getARouteHandlerNode` instead.
|
||||
* Gets an argument that represents a route handler being registered.
|
||||
*/
|
||||
deprecated Expr getARouteHandlerExpr() { result = getARouteHandlerNode().asExpr() }
|
||||
|
||||
/**
|
||||
* Gets an argument that represents a route handler being registered.
|
||||
*/
|
||||
DataFlow::Node getARouteHandlerNode() { result = getAnArgument() }
|
||||
}
|
||||
|
||||
/** An expression that is passed as `basicAuthConnect(<user>, <password>)`. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(CallExpr call |
|
||||
call = DataFlow::moduleImport("basic-auth-connect").getAnInvocation().asExpr() and
|
||||
exists(DataFlow::CallNode call |
|
||||
call = DataFlow::moduleImport("basic-auth-connect").getAnInvocation() and
|
||||
call.getNumArgument() = 2
|
||||
|
|
||||
this = call.getArgument(0) and kind = "user name"
|
||||
@@ -108,22 +118,24 @@ module Connect {
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
|
||||
class RequestExpr = NodeJSLib::RequestExpr;
|
||||
deprecated class RequestExpr = NodeJSLib::RequestExpr;
|
||||
|
||||
class RequestNode = NodeJSLib::RequestNode;
|
||||
|
||||
/**
|
||||
* An access to a user-controlled Connect request input.
|
||||
*/
|
||||
private class RequestInputAccess extends HTTP::RequestInputAccess {
|
||||
RequestExpr request;
|
||||
private class RequestInputAccess extends HTTP::RequestInputAccess instanceof DataFlow::MethodCallNode {
|
||||
RequestNode request;
|
||||
string kind;
|
||||
|
||||
RequestInputAccess() {
|
||||
request.getRouteHandler() instanceof StandardRouteHandler and
|
||||
exists(PropAccess cookies |
|
||||
exists(DataFlow::PropRead cookies |
|
||||
// `req.cookies.get(<name>)`
|
||||
kind = "cookie" and
|
||||
cookies.accesses(request, "cookies") and
|
||||
this.asExpr().(MethodCallExpr).calls(cookies, "get")
|
||||
super.calls(cookies, "get")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ module ConnectExpressShared {
|
||||
/**
|
||||
* Holds if `function` appears to match the given signature based on parameter naming.
|
||||
*/
|
||||
private predicate matchesSignature(Function function, RouteHandlerSignature sig) {
|
||||
private predicate matchesSignature(DataFlow::FunctionNode function, RouteHandlerSignature sig) {
|
||||
function.getNumParameter() = sig.getArity() and
|
||||
function.getParameter(sig.getParameterIndex("request")).getName() = ["req", "request"] and
|
||||
function.getParameter(sig.getParameterIndex("response")).getName() = ["res", "response"] and
|
||||
@@ -71,8 +71,8 @@ module ConnectExpressShared {
|
||||
* so the caller should restrict the function accordingly.
|
||||
*/
|
||||
pragma[inline]
|
||||
private Parameter getRouteHandlerParameter(
|
||||
Function routeHandler, RouteHandlerSignature sig, string kind
|
||||
private DataFlow::ParameterNode getRouteHandlerParameter(
|
||||
DataFlow::FunctionNode routeHandler, RouteHandlerSignature sig, string kind
|
||||
) {
|
||||
result = routeHandler.getParameter(sig.getParameterIndex(kind))
|
||||
}
|
||||
@@ -83,7 +83,9 @@ module ConnectExpressShared {
|
||||
* `kind` is one of: "error", "request", "response", "next".
|
||||
*/
|
||||
pragma[inline]
|
||||
Parameter getRouteParameterHandlerParameter(Function routeHandler, string kind) {
|
||||
DataFlow::ParameterNode getRouteParameterHandlerParameter(
|
||||
DataFlow::FunctionNode routeHandler, string kind
|
||||
) {
|
||||
result =
|
||||
getRouteHandlerParameter(routeHandler, RouteHandlerSignature::requestResponseNextParameter(),
|
||||
kind)
|
||||
@@ -95,7 +97,7 @@ module ConnectExpressShared {
|
||||
* `kind` is one of: "error", "request", "response", "next".
|
||||
*/
|
||||
pragma[inline]
|
||||
Parameter getRouteHandlerParameter(Function routeHandler, string kind) {
|
||||
DataFlow::ParameterNode getRouteHandlerParameter(DataFlow::FunctionNode routeHandler, string kind) {
|
||||
if routeHandler.getNumParameter() = 4
|
||||
then
|
||||
// For arity 4 there is ambiguity between (err, req, res, next) and (req, res, next, param)
|
||||
@@ -115,7 +117,7 @@ module ConnectExpressShared {
|
||||
*/
|
||||
class RouteHandlerCandidate extends HTTP::RouteHandlerCandidate {
|
||||
RouteHandlerCandidate() {
|
||||
matchesSignature(astNode, _) and
|
||||
matchesSignature(this, _) and
|
||||
not (
|
||||
// heuristic: not a class method (the server invokes this with a function call)
|
||||
astNode = any(MethodDefinition def).getBody()
|
||||
|
||||
@@ -76,7 +76,7 @@ private predicate canHaveSensitiveCookie(DataFlow::Node node) {
|
||||
HeuristicNames::nameIndicatesSensitiveData([s, getCookieName(s)], _)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof SensitiveExpr
|
||||
node instanceof SensitiveNode
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,30 +271,27 @@ private module ExpressCookies {
|
||||
/**
|
||||
* A cookie set using `response.cookie` from `express` module (https://expressjs.com/en/api.html#res.cookie).
|
||||
*/
|
||||
private class InsecureExpressCookieResponse extends CookieWrites::CookieWrite,
|
||||
DataFlow::MethodCallNode {
|
||||
InsecureExpressCookieResponse() { this.asExpr() instanceof Express::SetCookie }
|
||||
|
||||
private class InsecureExpressCookieResponse extends CookieWrites::CookieWrite instanceof Express::SetCookie {
|
||||
override predicate isSecure() {
|
||||
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
|
||||
// The default is `false`.
|
||||
exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
|
||||
exists(DataFlow::Node value | value = super.getOptionArgument(2, CookieWrites::secure()) |
|
||||
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
|
||||
override predicate isSensitive() { canHaveSensitiveCookie(super.getArgument(0)) }
|
||||
|
||||
override predicate isHttpOnly() {
|
||||
// A cookie is httpOnly if there are cookie options with the `httpOnly` flag set to `true`.
|
||||
// The default is `false`.
|
||||
exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::httpOnly()) |
|
||||
exists(DataFlow::Node value | value = super.getOptionArgument(2, CookieWrites::httpOnly()) |
|
||||
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
|
||||
)
|
||||
}
|
||||
|
||||
override string getSameSite() {
|
||||
result = getSameSiteValue(this.getOptionArgument(2, "sameSite"))
|
||||
result = getSameSiteValue(super.getOptionArgument(2, "sameSite"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,10 +369,10 @@ private class HttpCookieWrite extends CookieWrites::CookieWrite {
|
||||
|
||||
HttpCookieWrite() {
|
||||
exists(HTTP::CookieDefinition setCookie |
|
||||
this.asExpr() = setCookie.getHeaderArgument() and
|
||||
this = setCookie.getHeaderArgument() and
|
||||
not this instanceof DataFlow::ArrayCreationNode
|
||||
or
|
||||
this = setCookie.getHeaderArgument().flow().(DataFlow::ArrayCreationNode).getAnElement()
|
||||
this = setCookie.getHeaderArgument().(DataFlow::ArrayCreationNode).getAnElement()
|
||||
) and
|
||||
header =
|
||||
[
|
||||
|
||||
@@ -6,10 +6,27 @@
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `CredentialsNode` instead.
|
||||
* An expression whose value is used to supply credentials such
|
||||
* as a user name, a password, or a key.
|
||||
*/
|
||||
abstract class CredentialsExpr extends Expr {
|
||||
deprecated class CredentialsExpr extends Expr {
|
||||
CredentialsNode node;
|
||||
|
||||
CredentialsExpr() { node.asExpr() = this }
|
||||
|
||||
/**
|
||||
* Gets a description of the kind of credential this expression is used as,
|
||||
* such as `"user name"`, `"password"`, `"key"`.
|
||||
*/
|
||||
deprecated string getCredentialsKind() { result = node.getCredentialsKind() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression whose value is used to supply credentials such
|
||||
* as a user name, a password, or a key.
|
||||
*/
|
||||
abstract class CredentialsNode extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a description of the kind of credential this expression is used as,
|
||||
* such as `"user name"`, `"password"`, `"key"`.
|
||||
@@ -17,12 +34,10 @@ abstract class CredentialsExpr extends Expr {
|
||||
abstract string getCredentialsKind();
|
||||
}
|
||||
|
||||
private class CredentialsFromModel extends CredentialsExpr {
|
||||
private class CredentialsFromModel extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
CredentialsFromModel() {
|
||||
this = ModelOutput::getASinkNode("credentials[" + kind + "]").asSink().asExpr()
|
||||
}
|
||||
CredentialsFromModel() { this = ModelOutput::getASinkNode("credentials[" + kind + "]").asSink() }
|
||||
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ import semmle.javascript.security.CryptoAlgorithms
|
||||
/**
|
||||
* An application of a cryptographic algorithm.
|
||||
*/
|
||||
abstract class CryptographicOperation extends Expr {
|
||||
abstract class CryptographicOperation extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the input the algorithm is used on, e.g. the plain text input to be encrypted.
|
||||
*/
|
||||
abstract Expr getInput();
|
||||
abstract DataFlow::Node getInput();
|
||||
|
||||
/**
|
||||
* Gets the applied algorithm.
|
||||
@@ -46,11 +46,9 @@ abstract class CryptographicKeyCreation extends DataFlow::Node {
|
||||
}
|
||||
|
||||
/**
|
||||
* A key used in a cryptographic algorithm, viewed as a `CredentialsExpr`.
|
||||
* A key used in a cryptographic algorithm, viewed as a `CredentialsNode`.
|
||||
*/
|
||||
class CryptographicKeyCredentialsExpr extends CredentialsExpr {
|
||||
CryptographicKeyCredentialsExpr() { this = any(CryptographicKey k).asExpr() }
|
||||
|
||||
class CryptographicKeyCredentialsExpr extends CredentialsNode instanceof CryptographicKey {
|
||||
override string getCredentialsKind() { result = "key" }
|
||||
}
|
||||
|
||||
@@ -58,8 +56,8 @@ class CryptographicKeyCredentialsExpr extends CredentialsExpr {
|
||||
* A model of the asmCrypto library.
|
||||
*/
|
||||
private module AsmCrypto {
|
||||
private class Apply extends CryptographicOperation {
|
||||
Expr input;
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
CryptographicAlgorithm algorithm; // non-functional
|
||||
|
||||
Apply() {
|
||||
@@ -73,17 +71,15 @@ private module AsmCrypto {
|
||||
* ```
|
||||
*/
|
||||
|
||||
exists(DataFlow::CallNode mce | this = mce.asExpr() |
|
||||
exists(DataFlow::SourceNode asmCrypto, string algorithmName |
|
||||
asmCrypto = DataFlow::globalVarRef("asmCrypto") and
|
||||
algorithm.matchesName(algorithmName) and
|
||||
mce = asmCrypto.getAPropertyRead(algorithmName).getAMemberCall(_) and
|
||||
input = mce.getAnArgument().asExpr()
|
||||
)
|
||||
this = asmCrypto.getAPropertyRead(algorithmName).getAMemberCall(_) and
|
||||
input = this.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = input }
|
||||
override DataFlow::Node getInput() { result = input }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
@@ -97,9 +93,8 @@ private module BrowserIdCrypto {
|
||||
Key() { this = any(Apply apply).getKey() }
|
||||
}
|
||||
|
||||
private class Apply extends CryptographicOperation {
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::MethodCallNode {
|
||||
CryptographicAlgorithm algorithm; // non-functional
|
||||
MethodCallExpr mce;
|
||||
|
||||
Apply() {
|
||||
/*
|
||||
@@ -118,7 +113,6 @@ private module BrowserIdCrypto {
|
||||
* ```
|
||||
*/
|
||||
|
||||
this = mce and
|
||||
exists(
|
||||
DataFlow::SourceNode mod, DataFlow::Node algorithmNameNode, DataFlow::CallNode keygen,
|
||||
DataFlow::FunctionNode callback
|
||||
@@ -128,15 +122,15 @@ private module BrowserIdCrypto {
|
||||
algorithmNameNode = keygen.getOptionArgument(0, "algorithm") and
|
||||
algorithm.matchesName(algorithmNameNode.getStringValue()) and
|
||||
callback = keygen.getCallback(1) and
|
||||
this = mod.getAMemberCall("sign").asExpr()
|
||||
this = mod.getAMemberCall("sign")
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = mce.getArgument(0) }
|
||||
override DataFlow::Node getInput() { result = super.getArgument(0) }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
|
||||
DataFlow::Node getKey() { result.asExpr() = mce.getArgument(1) }
|
||||
DataFlow::Node getKey() { result = super.getArgument(1) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,14 +211,12 @@ private module NodeJSCrypto {
|
||||
override predicate isSymmetricKey() { none() }
|
||||
}
|
||||
|
||||
private class Apply extends CryptographicOperation, MethodCallExpr {
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::MethodCallNode {
|
||||
InstantiatedAlgorithm instantiation;
|
||||
|
||||
Apply() {
|
||||
this = instantiation.getAMethodCall(any(string m | m = "update" or m = "write")).asExpr()
|
||||
}
|
||||
Apply() { this = instantiation.getAMethodCall(any(string m | m = "update" or m = "write")) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0) }
|
||||
override DataFlow::Node getInput() { result = super.getArgument(0) }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = instantiation.getAlgorithm() }
|
||||
}
|
||||
@@ -260,7 +252,7 @@ private module CryptoJS {
|
||||
/**
|
||||
* Matches `CryptoJS.<algorithmName>` and `require("crypto-js/<algorithmName>")`
|
||||
*/
|
||||
private DataFlow::SourceNode getAlgorithmExpr(CryptographicAlgorithm algorithm) {
|
||||
private DataFlow::SourceNode getAlgorithmNode(CryptographicAlgorithm algorithm) {
|
||||
exists(string algorithmName | algorithm.matchesName(algorithmName) |
|
||||
exists(DataFlow::SourceNode mod | mod = DataFlow::moduleImport("crypto-js") |
|
||||
result = mod.getAPropertyRead(algorithmName) or
|
||||
@@ -274,7 +266,9 @@ private module CryptoJS {
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::CallNode getEncryptionApplication(Expr input, CryptographicAlgorithm algorithm) {
|
||||
private DataFlow::CallNode getEncryptionApplication(
|
||||
DataFlow::Node input, CryptographicAlgorithm algorithm
|
||||
) {
|
||||
/*
|
||||
* ```
|
||||
* var CryptoJS = require("crypto-js");
|
||||
@@ -288,11 +282,13 @@ private module CryptoJS {
|
||||
* Also matches where `CryptoJS.<algorithmName>` has been replaced by `require("crypto-js/<algorithmName>")`
|
||||
*/
|
||||
|
||||
result = getAlgorithmExpr(algorithm).getAMemberCall("encrypt") and
|
||||
input = result.getArgument(0).asExpr()
|
||||
result = getAlgorithmNode(algorithm).getAMemberCall("encrypt") and
|
||||
input = result.getArgument(0)
|
||||
}
|
||||
|
||||
private DataFlow::CallNode getDirectApplication(Expr input, CryptographicAlgorithm algorithm) {
|
||||
private DataFlow::CallNode getDirectApplication(
|
||||
DataFlow::Node input, CryptographicAlgorithm algorithm
|
||||
) {
|
||||
/*
|
||||
* ```
|
||||
* var CryptoJS = require("crypto-js");
|
||||
@@ -307,20 +303,20 @@ private module CryptoJS {
|
||||
* Also matches where `CryptoJS.<algorithmName>` has been replaced by `require("crypto-js/<algorithmName>")`
|
||||
*/
|
||||
|
||||
result = getAlgorithmExpr(algorithm).getACall() and
|
||||
input = result.getArgument(0).asExpr()
|
||||
result = getAlgorithmNode(algorithm).getACall() and
|
||||
input = result.getArgument(0)
|
||||
}
|
||||
|
||||
private class Apply extends CryptographicOperation {
|
||||
Expr input;
|
||||
DataFlow::Node input;
|
||||
CryptographicAlgorithm algorithm; // non-functional
|
||||
|
||||
Apply() {
|
||||
this = getEncryptionApplication(input, algorithm).asExpr() or
|
||||
this = getDirectApplication(input, algorithm).asExpr()
|
||||
this = getEncryptionApplication(input, algorithm) or
|
||||
this = getDirectApplication(input, algorithm)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = input }
|
||||
override DataFlow::Node getInput() { result = input }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
@@ -328,7 +324,7 @@ private module CryptoJS {
|
||||
private class Key extends CryptographicKey {
|
||||
Key() {
|
||||
exists(DataFlow::SourceNode e, CryptographicAlgorithm algorithm |
|
||||
e = getAlgorithmExpr(algorithm)
|
||||
e = getAlgorithmNode(algorithm)
|
||||
|
|
||||
exists(string name |
|
||||
name = "encrypt" or
|
||||
@@ -351,7 +347,7 @@ private module CryptoJS {
|
||||
CreateKey() {
|
||||
// var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 });
|
||||
this =
|
||||
getAlgorithmExpr(any(CryptographicAlgorithm algo | algo.getName() = algorithm)).getACall() and
|
||||
getAlgorithmNode(any(CryptographicAlgorithm algo | algo.getName() = algorithm)).getACall() and
|
||||
optionArg = 2
|
||||
or
|
||||
// var key = CryptoJS.algo.PBKDF2.create({ keySize: 8 });
|
||||
@@ -378,8 +374,8 @@ private module CryptoJS {
|
||||
* A model of the TweetNaCl library.
|
||||
*/
|
||||
private module TweetNaCl {
|
||||
private class Apply extends CryptographicOperation instanceof MethodCallExpr {
|
||||
Expr input;
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
CryptographicAlgorithm algorithm;
|
||||
|
||||
Apply() {
|
||||
@@ -400,12 +396,12 @@ private module TweetNaCl {
|
||||
name = "sign" and algorithm.matchesName("ed25519")
|
||||
|
|
||||
(mod = DataFlow::moduleImport("nacl") or mod = DataFlow::moduleImport("nacl-fast")) and
|
||||
this = mod.getAMemberCall(name).asExpr() and
|
||||
this = mod.getAMemberCall(name) and
|
||||
super.getArgument(0) = input
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = input }
|
||||
override DataFlow::Node getInput() { result = input }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
@@ -421,7 +417,7 @@ private module HashJs {
|
||||
* - `require("hash.js/lib/hash/<algorithmName>")`()
|
||||
* - `require("hash.js/lib/hash/sha/<sha-algorithm-suffix>")`()
|
||||
*/
|
||||
private DataFlow::CallNode getAlgorithmExpr(CryptographicAlgorithm algorithm) {
|
||||
private DataFlow::CallNode getAlgorithmNode(CryptographicAlgorithm algorithm) {
|
||||
exists(string algorithmName | algorithm.matchesName(algorithmName) |
|
||||
result = DataFlow::moduleMember("hash.js", algorithmName).getACall()
|
||||
or
|
||||
@@ -438,8 +434,8 @@ private module HashJs {
|
||||
)
|
||||
}
|
||||
|
||||
private class Apply extends CryptographicOperation instanceof MethodCallExpr {
|
||||
Expr input;
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
CryptographicAlgorithm algorithm; // non-functional
|
||||
|
||||
Apply() {
|
||||
@@ -456,11 +452,11 @@ private module HashJs {
|
||||
* Also matches where `hash.<algorithmName>()` has been replaced by a more specific require a la `require("hash.js/lib/hash/sha/512")`
|
||||
*/
|
||||
|
||||
this = getAlgorithmExpr(algorithm).getAMemberCall("update").asExpr() and
|
||||
this = getAlgorithmNode(algorithm).getAMemberCall("update") and
|
||||
input = super.getArgument(0)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = input }
|
||||
override DataFlow::Node getInput() { result = input }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
@@ -492,7 +488,7 @@ private module Forge {
|
||||
// `require('forge').cipher.createCipher("3DES-CBC").update("secret", "key");`
|
||||
(createName = "createCipher" or createName = "createDecipher") and
|
||||
this = mod.getAPropertyRead("cipher").getAMemberCall(createName) and
|
||||
this.getArgument(0).asExpr().mayHaveStringValue(cipherName) and
|
||||
this.getArgument(0).mayHaveStringValue(cipherName) and
|
||||
cipherName = cipherPrefix + "-" + cipherSuffix and
|
||||
cipherSuffix = ["CBC", "CFB", "CTR", "ECB", "GCM", "OFB"] and
|
||||
algorithmName = cipherPrefix and
|
||||
@@ -531,19 +527,19 @@ private module Forge {
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
|
||||
private class Apply extends CryptographicOperation instanceof MethodCallExpr {
|
||||
Expr input;
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
CryptographicAlgorithm algorithm; // non-functional
|
||||
|
||||
Apply() {
|
||||
exists(Cipher cipher |
|
||||
this = cipher.getAMemberCall("update").asExpr() and
|
||||
this = cipher.getAMemberCall("update") and
|
||||
super.getArgument(0) = input and
|
||||
algorithm = cipher.getAlgorithm()
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = input }
|
||||
override DataFlow::Node getInput() { result = input }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
@@ -590,8 +586,8 @@ private module Forge {
|
||||
* A model of the md5 library.
|
||||
*/
|
||||
private module Md5 {
|
||||
private class Apply extends CryptographicOperation instanceof CallExpr {
|
||||
Expr input;
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
CryptographicAlgorithm algorithm;
|
||||
|
||||
Apply() {
|
||||
@@ -599,12 +595,12 @@ private module Md5 {
|
||||
exists(DataFlow::SourceNode mod |
|
||||
mod = DataFlow::moduleImport("md5") and
|
||||
algorithm.matchesName("MD5") and
|
||||
this = mod.getACall().asExpr() and
|
||||
this = mod.getACall() and
|
||||
super.getArgument(0) = input
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = input }
|
||||
override DataFlow::Node getInput() { result = input }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
@@ -614,8 +610,8 @@ private module Md5 {
|
||||
* A model of the bcrypt, bcryptjs, bcrypt-nodejs libraries.
|
||||
*/
|
||||
private module Bcrypt {
|
||||
private class Apply extends CryptographicOperation instanceof MethodCallExpr {
|
||||
Expr input;
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
CryptographicAlgorithm algorithm;
|
||||
|
||||
Apply() {
|
||||
@@ -632,12 +628,12 @@ private module Bcrypt {
|
||||
methodName = "hashSync"
|
||||
) and
|
||||
mod = DataFlow::moduleImport(moduleName) and
|
||||
this = mod.getAMemberCall(methodName).asExpr() and
|
||||
this = mod.getAMemberCall(methodName) and
|
||||
super.getArgument(0) = input
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = input }
|
||||
override DataFlow::Node getInput() { result = input }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
@@ -647,23 +643,23 @@ private module Bcrypt {
|
||||
* A model of the hasha library.
|
||||
*/
|
||||
private module Hasha {
|
||||
private class Apply extends CryptographicOperation instanceof CallExpr {
|
||||
Expr input;
|
||||
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
CryptographicAlgorithm algorithm;
|
||||
|
||||
Apply() {
|
||||
// `require('hasha')('unicorn', { algorithm: "md5" });`
|
||||
exists(DataFlow::SourceNode mod, string algorithmName, Expr algorithmNameNode |
|
||||
exists(DataFlow::SourceNode mod, string algorithmName, DataFlow::Node algorithmNameNode |
|
||||
mod = DataFlow::moduleImport("hasha") and
|
||||
this = mod.getACall().asExpr() and
|
||||
this = mod.getACall() and
|
||||
super.getArgument(0) = input and
|
||||
algorithm.matchesName(algorithmName) and
|
||||
super.hasOptionArgument(1, "algorithm", algorithmNameNode) and
|
||||
super.getOptionArgument(1, "algorithm") = algorithmNameNode and
|
||||
algorithmNameNode.mayHaveStringValue(algorithmName)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = input }
|
||||
override DataFlow::Node getInput() { result = input }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@ module DigitalOcean {
|
||||
/**
|
||||
* An expression that is used for authentication at DigitalOcean: `digitalocean.client(<token>)`.
|
||||
*/
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(CallExpr mce |
|
||||
mce = DataFlow::moduleMember("digitalocean", "client").getACall().asExpr()
|
||||
exists(DataFlow::CallNode mce |
|
||||
mce = DataFlow::moduleMember("digitalocean", "client").getACall()
|
||||
|
|
||||
this = mce.getArgument(0) and kind = "token"
|
||||
)
|
||||
|
||||
@@ -40,31 +40,27 @@ module Express {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` may refer to the given `router` object.
|
||||
*/
|
||||
private predicate isRouter(Expr e, RouterDefinition router) { router.flowsTo(e) }
|
||||
/** Holds if `e` may refer to the given `router` object. */
|
||||
private predicate isRouter(DataFlow::Node e, RouterDefinition router) { router.ref().flowsTo(e) }
|
||||
|
||||
/**
|
||||
* Holds if `e` may refer to a router object.
|
||||
*/
|
||||
private predicate isRouter(Expr e) {
|
||||
private predicate isRouter(DataFlow::Node e) {
|
||||
isRouter(e, _)
|
||||
or
|
||||
e.getType().hasUnderlyingType("express", "Router")
|
||||
e.asExpr().getType().hasUnderlyingType("express", "Router")
|
||||
or
|
||||
// created by `webpack-dev-server`
|
||||
WebpackDevServer::webpackDevServerApp().flowsToExpr(e)
|
||||
WebpackDevServer::webpackDevServerApp().flowsTo(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RouterDefinition.ref()` or `RouteSetup` instead.
|
||||
* An expression that refers to a route.
|
||||
*/
|
||||
class RouteExpr extends MethodCallExpr {
|
||||
RouteExpr() { isRouter(this) }
|
||||
|
||||
/** Gets the router from which this route was created, if it is known. */
|
||||
RouterDefinition getRouter() { isRouter(this, result) }
|
||||
deprecated class RouteExpr extends MethodCallExpr {
|
||||
RouteExpr() { isRouter(this.flow()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,13 +79,13 @@ module Express {
|
||||
private class RouterRange extends Routing::Router::Range {
|
||||
RouterDefinition def;
|
||||
|
||||
RouterRange() { this = def.flow() }
|
||||
RouterRange() { this = def }
|
||||
|
||||
override DataFlow::SourceNode getAReference() { result = def.ref() }
|
||||
}
|
||||
|
||||
private class RoutingTreeSetup extends Routing::RouteSetup::MethodCall {
|
||||
RoutingTreeSetup() { this.asExpr() instanceof RouteSetup }
|
||||
RoutingTreeSetup() { this instanceof RouteSetup }
|
||||
|
||||
override string getRelativePath() {
|
||||
not this.getMethodName() = "param" and // do not treat parameter name as a path
|
||||
@@ -140,7 +136,7 @@ module Express {
|
||||
/**
|
||||
* A call to an Express router method that sets up a route.
|
||||
*/
|
||||
class RouteSetup extends HTTP::Servers::StandardRouteSetup, MethodCallExpr {
|
||||
class RouteSetup extends HTTP::Servers::StandardRouteSetup, DataFlow::MethodCallNode {
|
||||
RouteSetup() {
|
||||
isRouter(this.getReceiver()) and
|
||||
this.getMethodName() = routeSetupMethodName()
|
||||
@@ -156,12 +152,23 @@ module Express {
|
||||
predicate isUseCall() { this.getMethodName() = "use" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getRouteHandlerNode` instead.
|
||||
* Gets the `n`th handler registered by this setup, with 0 being the first.
|
||||
*
|
||||
* This differs from `getARouteHandler` in that the argument expression is
|
||||
* returned, not its dataflow source.
|
||||
*/
|
||||
Expr getRouteHandlerExpr(int index) {
|
||||
deprecated Expr getRouteHandlerExpr(int index) {
|
||||
result = this.getRouteHandlerNode(index).asExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `n`th handler registered by this setup, with 0 being the first.
|
||||
*
|
||||
* This differs from `getARouteHandler` in that the argument expression is
|
||||
* returned, not its dataflow source.
|
||||
*/
|
||||
DataFlow::Node getRouteHandlerNode(int index) {
|
||||
// The first argument is a URI pattern if it is a string. If it could possibly be
|
||||
// a function, we consider it to be a route handler, otherwise a URI pattern.
|
||||
exists(AnalyzedNode firstArg | firstArg = this.getArgument(0).analyze() |
|
||||
@@ -173,21 +180,39 @@ module Express {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an argument that represents a route handler being registered. */
|
||||
Expr getARouteHandlerExpr() { result = this.getRouteHandlerExpr(_) }
|
||||
/**
|
||||
* DEPRECATED: Use `getARouteHandlerNode` instead.
|
||||
* Gets an argument that represents a route handler being registered.
|
||||
*/
|
||||
deprecated Expr getARouteHandlerExpr() { result = this.getRouteHandlerExpr(_) }
|
||||
|
||||
/** Gets the last argument representing a route handler being registered. */
|
||||
Expr getLastRouteHandlerExpr() {
|
||||
/**
|
||||
* Gets an argument that represents a route handler being registered.
|
||||
*/
|
||||
DataFlow::Node getARouteHandlerNode() { result = this.getRouteHandlerNode(_) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLastRouteHandlerExpr` instead.
|
||||
* Gets the last argument representing a route handler being registered.
|
||||
*/
|
||||
deprecated Expr getLastRouteHandlerExpr() {
|
||||
result = max(int i | | this.getRouteHandlerExpr(i) order by i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last argument representing a route handler being registered.
|
||||
*/
|
||||
DataFlow::Node getLastRouteHandlerNode() {
|
||||
result = max(int i | | this.getRouteHandlerNode(i) order by i)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() {
|
||||
result = this.getARouteHandler(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = this.getARouteHandlerExpr().flow().getALocalSource()
|
||||
result = this.getARouteHandlerNode().getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2, DataFlow::SourceNode succ |
|
||||
succ = this.getARouteHandler(t2)
|
||||
@@ -199,7 +224,9 @@ module Express {
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getServer() { result.(Application).getARouteHandler() = this.getARouteHandler() }
|
||||
override DataFlow::Node getServer() {
|
||||
result.(Application).getARouteHandler() = this.getARouteHandler()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTTP request type this is registered for, if any.
|
||||
@@ -233,16 +260,16 @@ module Express {
|
||||
/**
|
||||
* A call that sets up a Passport router that includes the request object.
|
||||
*/
|
||||
private class PassportRouteSetup extends HTTP::Servers::StandardRouteSetup, CallExpr {
|
||||
private class PassportRouteSetup extends HTTP::Servers::StandardRouteSetup, DataFlow::CallNode {
|
||||
DataFlow::ModuleImportNode importNode;
|
||||
DataFlow::FunctionNode callback;
|
||||
|
||||
// looks for this pattern: passport.use(new Strategy({passReqToCallback: true}, callback))
|
||||
PassportRouteSetup() {
|
||||
importNode = DataFlow::moduleImport("passport") and
|
||||
this = importNode.getAMemberCall("use").asExpr() and
|
||||
this = importNode.getAMemberCall("use") and
|
||||
exists(DataFlow::NewNode strategy |
|
||||
strategy.flowsToExpr(this.getArgument(0)) and
|
||||
strategy.flowsTo(this.getArgument(0)) and
|
||||
strategy.getNumArgument() = 2 and
|
||||
// new Strategy({passReqToCallback: true}, ...)
|
||||
strategy.getOptionArgument(0, "passReqToCallback").mayHaveBooleanValue(true) and
|
||||
@@ -250,7 +277,7 @@ module Express {
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getServer() { result = importNode.asExpr() }
|
||||
override DataFlow::Node getServer() { result = importNode }
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() { result = callback }
|
||||
}
|
||||
@@ -259,17 +286,61 @@ module Express {
|
||||
* The callback given to passport in PassportRouteSetup.
|
||||
*/
|
||||
private class PassportRouteHandler extends RouteHandler, HTTP::Servers::StandardRouteHandler,
|
||||
DataFlow::ValueNode {
|
||||
override Function astNode;
|
||||
|
||||
DataFlow::FunctionNode {
|
||||
PassportRouteHandler() { this = any(PassportRouteSetup setup).getARouteHandler() }
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and
|
||||
result = astNode.getParameter(0)
|
||||
result = this.getParameter(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RouteHandlerNode` instead.
|
||||
* An expression used as an Express route handler, such as `submitHandler` below:
|
||||
* ```
|
||||
* app.post('/submit', submitHandler)
|
||||
* ```
|
||||
*
|
||||
* Unlike `RouterHandler`, this is the argument passed to a setup, as opposed to
|
||||
* a function that flows into such an argument.
|
||||
*/
|
||||
deprecated class RouteHandlerExpr extends Expr {
|
||||
RouteHandlerNode node;
|
||||
|
||||
RouteHandlerExpr() { this.flow() = node }
|
||||
|
||||
/** Gets the setup call that registers this route handler. */
|
||||
deprecated RouteSetup getSetup() { result = node.getSetup() }
|
||||
|
||||
/** Gets the function body of this handler, if it is defined locally. */
|
||||
deprecated RouteHandler getBody() { result = node.getBody() }
|
||||
|
||||
/** Holds if this is not followed by more handlers. */
|
||||
deprecated predicate isLastHandler() { node.isLastHandler() }
|
||||
|
||||
/** Gets a route handler that immediately precedes this in the route stack. */
|
||||
deprecated Express::RouteHandlerExpr getPreviousMiddleware() {
|
||||
result = node.getPreviousMiddleware().asExpr()
|
||||
}
|
||||
|
||||
/** Gets a route handler that may follow immediately after this one in its route stack. */
|
||||
deprecated Express::RouteHandlerExpr getNextMiddleware() {
|
||||
result = node.getNextMiddleware().asExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a route handler that precedes this one (not necessarily immediately), may handle
|
||||
* same request method, and matches on the same path or a prefix.
|
||||
*/
|
||||
deprecated Express::RouteHandlerExpr getAMatchingAncestor() {
|
||||
result = node.getAMatchingAncestor().asExpr()
|
||||
}
|
||||
|
||||
/** Gets the router being registered as a sub-router here, if any. */
|
||||
deprecated RouterDefinition getAsSubRouter() { result = node.getAsSubRouter() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression used as an Express route handler, such as `submitHandler` below:
|
||||
* ```
|
||||
@@ -279,11 +350,11 @@ module Express {
|
||||
* Unlike `RouterHandler`, this is the argument passed to a setup, as opposed to
|
||||
* a function that flows into such an argument.
|
||||
*/
|
||||
class RouteHandlerExpr extends Expr {
|
||||
class RouteHandlerNode extends DataFlow::Node {
|
||||
RouteSetup setup;
|
||||
int index;
|
||||
|
||||
RouteHandlerExpr() { this = setup.getRouteHandlerExpr(index) }
|
||||
RouteHandlerNode() { this = setup.getRouteHandlerNode(index) }
|
||||
|
||||
/**
|
||||
* Gets the setup call that registers this route handler.
|
||||
@@ -294,7 +365,7 @@ module Express {
|
||||
* Gets the function body of this handler, if it is defined locally.
|
||||
*/
|
||||
RouteHandler getBody() {
|
||||
exists(DataFlow::SourceNode source | source = this.flow().getALocalSource() |
|
||||
exists(DataFlow::SourceNode source | source = this.getALocalSource() |
|
||||
result = source
|
||||
or
|
||||
DataFlow::functionOneWayForwardingStep(result.(DataFlow::SourceNode).getALocalUse(), source)
|
||||
@@ -306,7 +377,7 @@ module Express {
|
||||
*/
|
||||
predicate isLastHandler() {
|
||||
not setup.isUseCall() and
|
||||
not exists(setup.getRouteHandlerExpr(index + 1))
|
||||
not exists(setup.getRouteHandlerNode(index + 1))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,10 +402,11 @@ module Express {
|
||||
* In this case, the previous from `foo` is `auth` although they do not act on the
|
||||
* same requests.
|
||||
*/
|
||||
Express::RouteHandlerExpr getPreviousMiddleware() {
|
||||
index = 0 and result = setup.getRouter().getMiddlewareStackAt(setup.getAPredecessor())
|
||||
Express::RouteHandlerNode getPreviousMiddleware() {
|
||||
index = 0 and
|
||||
result = setup.getRouter().getMiddlewareStackAt(setup.asExpr().getAPredecessor())
|
||||
or
|
||||
index > 0 and result = setup.getRouteHandlerExpr(index - 1)
|
||||
index > 0 and result = setup.getRouteHandlerNode(index - 1)
|
||||
or
|
||||
// Outside the router's original container, use the flow-insensitive model of its middleware stack.
|
||||
// Its state is not tracked to CFG nodes outside its original container.
|
||||
@@ -348,7 +420,7 @@ module Express {
|
||||
/**
|
||||
* Gets a route handler that may follow immediately after this one in its route stack.
|
||||
*/
|
||||
Express::RouteHandlerExpr getNextMiddleware() { result.getPreviousMiddleware() = this }
|
||||
Express::RouteHandlerNode getNextMiddleware() { result.getPreviousMiddleware() = this }
|
||||
|
||||
/**
|
||||
* Gets a route handler that precedes this one (not necessarily immediately), may handle
|
||||
@@ -361,7 +433,7 @@ module Express {
|
||||
* router installs a route handler `r1` on a path that matches the path of a route handler
|
||||
* `r2` installed on a subrouter, `r1` will not be recognized as an ancestor of `r2`.
|
||||
*/
|
||||
Express::RouteHandlerExpr getAMatchingAncestor() {
|
||||
Express::RouteHandlerNode getAMatchingAncestor() {
|
||||
result = this.getPreviousMiddleware+() and
|
||||
exists(RouteSetup resSetup | resSetup = result.getSetup() |
|
||||
// check whether request methods are compatible
|
||||
@@ -378,7 +450,7 @@ module Express {
|
||||
or
|
||||
// if this is a sub-router, any previously installed middleware for the same
|
||||
// request method will necessarily match
|
||||
exists(RouteHandlerExpr outer |
|
||||
exists(RouteHandlerNode outer |
|
||||
setup.getRouter() = outer.getAsSubRouter() and
|
||||
outer.getSetup().handlesSameRequestMethodAs(setup) and
|
||||
result = outer.getAMatchingAncestor()
|
||||
@@ -404,46 +476,51 @@ module Express {
|
||||
*
|
||||
* `kind` is one of: "error", "request", "response", "next", or "parameter".
|
||||
*/
|
||||
abstract Parameter getRouteHandlerParameter(string kind);
|
||||
abstract DataFlow::ParameterNode getRouteHandlerParameter(string kind);
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
Parameter getRequestParameter() { result = this.getRouteHandlerParameter("request") }
|
||||
DataFlow::ParameterNode getRequestParameter() {
|
||||
result = this.getRouteHandlerParameter("request")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the response object.
|
||||
*/
|
||||
Parameter getResponseParameter() { result = this.getRouteHandlerParameter("response") }
|
||||
DataFlow::ParameterNode getResponseParameter() {
|
||||
result = this.getRouteHandlerParameter("response")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a request body access of this handler.
|
||||
*/
|
||||
Expr getARequestBodyAccess() { result.(PropAccess).accesses(this.getARequestExpr(), "body") }
|
||||
DataFlow::PropRead getARequestBodyAccess() { result.accesses(this.getARequestNode(), "body") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An Express route handler installed by a route setup.
|
||||
*/
|
||||
class StandardRouteHandler extends RouteHandler, HTTP::Servers::StandardRouteHandler,
|
||||
DataFlow::ValueNode {
|
||||
override Function astNode;
|
||||
DataFlow::FunctionNode {
|
||||
RouteSetup routeSetup;
|
||||
|
||||
StandardRouteHandler() { this = routeSetup.getARouteHandler() }
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
if routeSetup.isParameterHandler()
|
||||
then result = getRouteParameterHandlerParameter(astNode, kind)
|
||||
else result = getRouteHandlerParameter(astNode, kind)
|
||||
then result = getRouteParameterHandlerParameter(this, kind)
|
||||
else result = getRouteHandlerParameter(this, kind)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is a chainable method call on the response object of `handler`.
|
||||
*/
|
||||
private predicate isChainableResponseMethodCall(RouteHandler handler, MethodCallExpr call) {
|
||||
exists(string name | call.calls(handler.getAResponseExpr(), name) |
|
||||
private predicate isChainableResponseMethodCall(
|
||||
RouteHandler handler, DataFlow::MethodCallNode call
|
||||
) {
|
||||
exists(string name | call.calls(handler.getAResponseNode(), name) |
|
||||
name =
|
||||
[
|
||||
"append", "attachment", "location", "send", "sendStatus", "set", "status", "type", "vary",
|
||||
@@ -463,9 +540,9 @@ module Express {
|
||||
RouteHandler rh;
|
||||
|
||||
ExplicitResponseSource() {
|
||||
this = DataFlow::parameterNode(rh.getResponseParameter())
|
||||
this = rh.getResponseParameter()
|
||||
or
|
||||
isChainableResponseMethodCall(rh, this.asExpr())
|
||||
isChainableResponseMethodCall(rh, this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,7 +570,7 @@ module Express {
|
||||
private class ExplicitRequestSource extends RequestSource {
|
||||
RouteHandler rh;
|
||||
|
||||
ExplicitRequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) }
|
||||
ExplicitRequestSource() { this = rh.getRequestParameter() }
|
||||
|
||||
/**
|
||||
* Gets the route handler that handles this request.
|
||||
@@ -511,16 +588,32 @@ module Express {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ResponseNode` instead.
|
||||
* An Express response expression.
|
||||
*/
|
||||
class ResponseExpr extends NodeJSLib::ResponseExpr {
|
||||
deprecated class ResponseExpr extends NodeJSLib::ResponseExpr {
|
||||
ResponseExpr() { this.flow() instanceof ResponseNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* An Express response expression.
|
||||
*/
|
||||
class ResponseNode extends NodeJSLib::ResponseNode {
|
||||
override ResponseSource src;
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RequestNode` instead.
|
||||
* An Express request expression.
|
||||
*/
|
||||
deprecated class RequestExpr extends NodeJSLib::RequestExpr {
|
||||
RequestExpr() { this.flow() instanceof RequestNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* An Express request expression.
|
||||
*/
|
||||
class RequestExpr extends NodeJSLib::RequestExpr {
|
||||
class RequestNode extends NodeJSLib::RequestNode {
|
||||
override RequestSource src;
|
||||
}
|
||||
|
||||
@@ -544,7 +637,7 @@ module Express {
|
||||
|
||||
ParamHandlerInputAccess() {
|
||||
exists(RouteSetup setup | rh = setup.getARouteHandler() |
|
||||
this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter"))
|
||||
this = rh.getRouteHandlerParameter("parameter")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -674,35 +767,35 @@ module Express {
|
||||
/**
|
||||
* Holds if `e` is an HTTP request object.
|
||||
*/
|
||||
predicate isRequest(Expr e) { any(RouteHandler rh).getARequestExpr() = e }
|
||||
predicate isRequest(DataFlow::Node e) { any(RouteHandler rh).getARequestNode() = e }
|
||||
|
||||
/**
|
||||
* Holds if `e` is an HTTP response object.
|
||||
*/
|
||||
predicate isResponse(Expr e) { any(RouteHandler rh).getAResponseExpr() = e }
|
||||
predicate isResponse(DataFlow::Node e) { any(RouteHandler rh).getAResponseNode() = e }
|
||||
|
||||
/**
|
||||
* An access to the HTTP request body.
|
||||
*/
|
||||
class RequestBodyAccess extends Expr {
|
||||
class RequestBodyAccess extends DataFlow::Node {
|
||||
RequestBodyAccess() { any(RouteHandler h).getARequestBodyAccess() = this }
|
||||
}
|
||||
|
||||
abstract private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition {
|
||||
HeaderDefinition() { isResponse(astNode.getReceiver()) }
|
||||
HeaderDefinition() { isResponse(this.getReceiver()) }
|
||||
|
||||
override RouteHandler getRouteHandler() { astNode.getReceiver() = result.getAResponseExpr() }
|
||||
override RouteHandler getRouteHandler() { this.getReceiver() = result.getAResponseNode() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of the `redirect` method of an HTTP response object.
|
||||
*/
|
||||
private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr {
|
||||
private class RedirectInvocation extends HTTP::RedirectInvocation, DataFlow::MethodCallNode {
|
||||
ResponseSource response;
|
||||
|
||||
RedirectInvocation() { this = response.ref().getAMethodCall("redirect").asExpr() }
|
||||
RedirectInvocation() { this = response.ref().getAMethodCall("redirect") }
|
||||
|
||||
override Expr getUrlArgument() { result = this.getLastArgument() }
|
||||
override DataFlow::Node getUrlArgument() { result = this.getLastArgument() }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
|
||||
}
|
||||
@@ -713,8 +806,8 @@ module Express {
|
||||
*/
|
||||
private class SetOneHeader extends HeaderDefinition {
|
||||
SetOneHeader() {
|
||||
astNode.getMethodName() = any(string n | n = "set" or n = "header") and
|
||||
astNode.getNumArgument() = 2
|
||||
this.getMethodName() = any(string n | n = "set" or n = "header") and
|
||||
this.getNumArgument() = 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,18 +828,18 @@ module Express {
|
||||
*/
|
||||
private DataFlow::SourceNode getAHeaderSource() { result.flowsTo(this.getArgument(0)) }
|
||||
|
||||
override predicate definesExplicitly(string headerName, Expr headerValue) {
|
||||
override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) {
|
||||
exists(string header |
|
||||
this.getAHeaderSource().hasPropertyWrite(header, DataFlow::valueNode(headerValue)) and
|
||||
this.getAHeaderSource().hasPropertyWrite(header, headerValue) and
|
||||
headerName = header.toLowerCase()
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
|
||||
|
||||
override Expr getNameExpr() {
|
||||
override DataFlow::Node getNameNode() {
|
||||
exists(DataFlow::PropWrite write | this.getAHeaderSource().getAPropertyWrite() = write |
|
||||
result = write.getPropertyNameExpr()
|
||||
result = write.getPropertyNameExpr().flow()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -755,7 +848,7 @@ module Express {
|
||||
* An invocation of the `append` method on an HTTP response object.
|
||||
*/
|
||||
private class AppendHeader extends HeaderDefinition {
|
||||
AppendHeader() { astNode.getMethodName() = "append" }
|
||||
AppendHeader() { this.getMethodName() = "append" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -764,7 +857,7 @@ module Express {
|
||||
private class ResponseSendArgument extends HTTP::ResponseSendArgument {
|
||||
ResponseSource response;
|
||||
|
||||
ResponseSendArgument() { this = response.ref().getAMethodCall("send").getArgument(0).asExpr() }
|
||||
ResponseSendArgument() { this = response.ref().getAMethodCall("send").getArgument(0) }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
|
||||
}
|
||||
@@ -772,14 +865,14 @@ module Express {
|
||||
/**
|
||||
* An invocation of the `cookie` method on an HTTP response object.
|
||||
*/
|
||||
class SetCookie extends HTTP::CookieDefinition, MethodCallExpr {
|
||||
class SetCookie extends HTTP::CookieDefinition, DataFlow::MethodCallNode {
|
||||
ResponseSource response;
|
||||
|
||||
SetCookie() { this = response.ref().getAMethodCall("cookie").asExpr() }
|
||||
SetCookie() { this = response.ref().getAMethodCall("cookie") }
|
||||
|
||||
override Expr getNameArgument() { result = this.getArgument(0) }
|
||||
override DataFlow::Node getNameArgument() { result = this.getArgument(0) }
|
||||
|
||||
override Expr getValueArgument() { result = this.getArgument(1) }
|
||||
override DataFlow::Node getValueArgument() { result = this.getArgument(1) }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
|
||||
}
|
||||
@@ -792,7 +885,7 @@ module Express {
|
||||
TemplateObjectInput obj;
|
||||
|
||||
TemplateInput() {
|
||||
obj.getALocalSource().(DataFlow::ObjectLiteralNode).hasPropertyWrite(_, this.flow())
|
||||
obj.getALocalSource().(DataFlow::ObjectLiteralNode).hasPropertyWrite(_, this)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = obj.getRouteHandler() }
|
||||
@@ -821,7 +914,7 @@ module Express {
|
||||
* An Express server application.
|
||||
*/
|
||||
private class Application extends HTTP::ServerDefinition {
|
||||
Application() { this = appCreation().asExpr() }
|
||||
Application() { this = appCreation() }
|
||||
|
||||
/**
|
||||
* Gets a route handler of the application, regardless of nesting.
|
||||
@@ -831,15 +924,13 @@ module Express {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Express router.
|
||||
*/
|
||||
class RouterDefinition extends InvokeExpr {
|
||||
RouterDefinition() { this = routerCreation().asExpr() }
|
||||
/** An Express router. */
|
||||
class RouterDefinition extends DataFlow::Node instanceof DataFlow::InvokeNode {
|
||||
RouterDefinition() { this = routerCreation() }
|
||||
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::exprNode(this)
|
||||
result = this
|
||||
or
|
||||
exists(string name | result = this.ref(t.continue()).getAMethodCall(name) |
|
||||
name = "route" or
|
||||
@@ -852,22 +943,17 @@ module Express {
|
||||
/** Gets a data flow node referring to this router. */
|
||||
DataFlow::SourceNode ref() { result = this.ref(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* Holds if `sink` may refer to this router.
|
||||
*/
|
||||
predicate flowsTo(Expr sink) { this.ref().flowsToExpr(sink) }
|
||||
|
||||
/**
|
||||
* Gets a `RouteSetup` that was used for setting up a route on this router.
|
||||
*/
|
||||
private RouteSetup getARouteSetup() { this.flowsTo(result.getReceiver()) }
|
||||
private RouteSetup getARouteSetup() { this.ref().flowsTo(result.getReceiver()) }
|
||||
|
||||
/**
|
||||
* Gets a sub-router registered on this router.
|
||||
*
|
||||
* Example: `router2` for `router1.use(router2)` or `router1.use("/route2", router2)`
|
||||
*/
|
||||
RouterDefinition getASubRouter() { result.flowsTo(this.getARouteSetup().getAnArgument()) }
|
||||
RouterDefinition getASubRouter() { result.ref().flowsTo(this.getARouteSetup().getAnArgument()) }
|
||||
|
||||
/**
|
||||
* Gets a route handler registered on this router.
|
||||
@@ -875,7 +961,7 @@ module Express {
|
||||
* Example: `fun` for `router1.use(fun)` or `router.use("/route", fun)`
|
||||
*/
|
||||
HTTP::RouteHandler getARouteHandler() {
|
||||
result.(DataFlow::SourceNode).flowsToExpr(this.getARouteSetup().getAnArgument())
|
||||
result.(DataFlow::SourceNode).flowsTo(this.getARouteSetup().getAnArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -893,25 +979,25 @@ module Express {
|
||||
*
|
||||
* If `node` is not in the same container where `router` was defined, the predicate has no result.
|
||||
*/
|
||||
Express::RouteHandlerExpr getMiddlewareStackAt(ControlFlowNode node) {
|
||||
Express::RouteHandlerNode getMiddlewareStackAt(ControlFlowNode node) {
|
||||
if
|
||||
exists(Express::RouteSetup setup | node = setup and setup.getRouter() = this |
|
||||
exists(Express::RouteSetup setup | node = setup.asExpr() and setup.getRouter() = this |
|
||||
setup.isUseCall()
|
||||
)
|
||||
then result = node.(Express::RouteSetup).getLastRouteHandlerExpr()
|
||||
then result = node.(AST::ValueNode).flow().(Express::RouteSetup).getLastRouteHandlerNode()
|
||||
else result = this.getMiddlewareStackAt(node.getAPredecessor())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the final middleware registered on this router.
|
||||
*/
|
||||
Express::RouteHandlerExpr getMiddlewareStack() {
|
||||
Express::RouteHandlerNode getMiddlewareStack() {
|
||||
result = this.getMiddlewareStackAt(this.getContainer().getExit())
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that is passed as `expressBasicAuth({ users: { <user>: <password> }})`. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
@@ -922,9 +1008,9 @@ module Express {
|
||||
usersSrc.flowsTo(call.getOptionArgument(0, "users")) and
|
||||
usersSrc.flowsTo(pwn.getBase())
|
||||
|
|
||||
this = pwn.getPropertyNameExpr() and kind = "user name"
|
||||
this = pwn.getPropertyNameExpr().flow() and kind = "user name"
|
||||
or
|
||||
this = pwn.getRhs().asExpr() and kind = "password"
|
||||
this = pwn.getRhs() and kind = "password"
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -937,7 +1023,7 @@ module Express {
|
||||
DataFlow::MethodCallNode {
|
||||
ResponseSendFileAsFileSystemAccess() {
|
||||
exists(string name | name = "sendFile" or name = "sendfile" |
|
||||
this.calls(any(ResponseExpr res).flow(), name)
|
||||
this.calls(any(ResponseNode res), name)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -963,10 +1049,10 @@ module Express {
|
||||
|
||||
TrackedRouteHandlerCandidateWithSetup() { this = routeSetup.getARouteHandler() }
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
if routeSetup.isParameterHandler()
|
||||
then result = getRouteParameterHandlerParameter(astNode, kind)
|
||||
else result = getRouteHandlerParameter(astNode, kind)
|
||||
then result = getRouteParameterHandlerParameter(this, kind)
|
||||
else result = getRouteHandlerParameter(this, kind)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,7 @@ module Fastify {
|
||||
* A standard way to create a Fastify server.
|
||||
*/
|
||||
class StandardServerDefinition extends ServerDefinition {
|
||||
StandardServerDefinition() {
|
||||
this = DataFlow::moduleImport("fastify").getAnInvocation().asExpr()
|
||||
}
|
||||
StandardServerDefinition() { this = DataFlow::moduleImport("fastify").getAnInvocation() }
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a fastify server. */
|
||||
@@ -134,12 +132,12 @@ module Fastify {
|
||||
/**
|
||||
* A call to a Fastify method that sets up a route.
|
||||
*/
|
||||
class RouteSetup extends MethodCallExpr, HTTP::Servers::StandardRouteSetup {
|
||||
class RouteSetup extends DataFlow::MethodCallNode, HTTP::Servers::StandardRouteSetup {
|
||||
ServerDefinition server;
|
||||
string methodName;
|
||||
|
||||
RouteSetup() {
|
||||
this = server(server.flow()).getAMethodCall(methodName).asExpr() and
|
||||
this = server(server).getAMethodCall(methodName) and
|
||||
methodName = ["route", "get", "head", "post", "put", "delete", "options", "patch"]
|
||||
}
|
||||
|
||||
@@ -149,25 +147,30 @@ module Fastify {
|
||||
|
||||
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = this.getARouteHandlerExpr().getALocalSource()
|
||||
result = this.getARouteHandlerNode().getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = this.getARouteHandler(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
override Expr getServer() { result = server }
|
||||
override DataFlow::SourceNode getServer() { result = server }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getARouteHandlerNode` instead.
|
||||
* Gets an argument that represents a route handler being registered.
|
||||
*/
|
||||
deprecated DataFlow::Node getARouteHandlerExpr() { result = this.getARouteHandlerNode() }
|
||||
|
||||
/** Gets an argument that represents a route handler being registered. */
|
||||
DataFlow::Node getARouteHandlerExpr() {
|
||||
DataFlow::Node getARouteHandlerNode() {
|
||||
if methodName = "route"
|
||||
then
|
||||
result = this.flow().(DataFlow::MethodCallNode).getOptionArgument(0, getNthHandlerName(_))
|
||||
else result = this.getLastArgument().flow()
|
||||
then result = this.getOptionArgument(0, getNthHandlerName(_))
|
||||
else result = this.getLastArgument()
|
||||
}
|
||||
}
|
||||
|
||||
private class ShorthandRoutingTreeSetup extends Routing::RouteSetup::MethodCall {
|
||||
ShorthandRoutingTreeSetup() {
|
||||
this.asExpr() instanceof RouteSetup and
|
||||
this instanceof RouteSetup and
|
||||
not this.getMethodName() = "route"
|
||||
}
|
||||
|
||||
@@ -185,7 +188,7 @@ module Fastify {
|
||||
|
||||
private class FullRoutingTreeSetup extends Routing::RouteSetup::MethodCall {
|
||||
FullRoutingTreeSetup() {
|
||||
this.asExpr() instanceof RouteSetup and
|
||||
this instanceof RouteSetup and
|
||||
this.getMethodName() = "route"
|
||||
}
|
||||
|
||||
@@ -287,13 +290,7 @@ module Fastify {
|
||||
*/
|
||||
private predicate usesFastifyPlugin(RouteHandler rh, DataFlow::SourceNode plugin) {
|
||||
exists(RouteSetup setup |
|
||||
plugin
|
||||
.flowsTo(setup
|
||||
.getServer()
|
||||
.flow()
|
||||
.(DataFlow::SourceNode)
|
||||
.getAMethodCall("register")
|
||||
.getArgument(0)) and // only matches the plugins that apply to all routes
|
||||
plugin.flowsTo(setup.getServer().getAMethodCall("register").getArgument(0)) and // only matches the plugins that apply to all routes
|
||||
rh = setup.getARouteHandler()
|
||||
)
|
||||
}
|
||||
@@ -303,13 +300,7 @@ module Fastify {
|
||||
*/
|
||||
private predicate usesMiddleware(RouteHandler rh, DataFlow::SourceNode middleware) {
|
||||
exists(RouteSetup setup |
|
||||
middleware
|
||||
.flowsTo(setup
|
||||
.getServer()
|
||||
.flow()
|
||||
.(DataFlow::SourceNode)
|
||||
.getAMethodCall("use")
|
||||
.getArgument(0)) and // only matches the middlewares that apply to all routes
|
||||
middleware.flowsTo(setup.getServer().getAMethodCall("use").getArgument(0)) and // only matches the middlewares that apply to all routes
|
||||
rh = setup.getARouteHandler()
|
||||
)
|
||||
}
|
||||
@@ -340,9 +331,9 @@ module Fastify {
|
||||
RouteHandler rh;
|
||||
|
||||
ResponseSendArgument() {
|
||||
this = rh.getAResponseSource().ref().getAMethodCall("send").getArgument(0).asExpr()
|
||||
this = rh.getAResponseSource().ref().getAMethodCall("send").getArgument(0)
|
||||
or
|
||||
this = rh.(DataFlow::FunctionNode).getAReturn().asExpr()
|
||||
this = rh.(DataFlow::FunctionNode).getAReturn()
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
@@ -351,14 +342,12 @@ module Fastify {
|
||||
/**
|
||||
* An invocation of the `redirect` method of an HTTP response object.
|
||||
*/
|
||||
private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr {
|
||||
private class RedirectInvocation extends HTTP::RedirectInvocation, DataFlow::MethodCallNode {
|
||||
RouteHandler rh;
|
||||
|
||||
RedirectInvocation() {
|
||||
this = rh.getAResponseSource().ref().getAMethodCall("redirect").asExpr()
|
||||
}
|
||||
RedirectInvocation() { this = rh.getAResponseSource().ref().getAMethodCall("redirect") }
|
||||
|
||||
override Expr getUrlArgument() { result = this.getLastArgument() }
|
||||
override DataFlow::Node getUrlArgument() { result = this.getLastArgument() }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
@@ -394,18 +383,18 @@ module Fastify {
|
||||
*/
|
||||
private DataFlow::SourceNode getAHeaderSource() { result.flowsTo(this.getArgument(0)) }
|
||||
|
||||
override predicate definesExplicitly(string headerName, Expr headerValue) {
|
||||
override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) {
|
||||
exists(string header |
|
||||
this.getAHeaderSource().hasPropertyWrite(header, headerValue.flow()) and
|
||||
this.getAHeaderSource().hasPropertyWrite(header, headerValue) and
|
||||
headerName = header.toLowerCase()
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
override Expr getNameExpr() {
|
||||
override DataFlow::Node getNameNode() {
|
||||
exists(DataFlow::PropWrite write | this.getAHeaderSource().getAPropertyWrite() = write |
|
||||
result = write.getPropertyNameExpr()
|
||||
result = write.getPropertyNameExpr().flow()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +114,13 @@ module Firebase {
|
||||
class QueryListenCall extends DataFlow::MethodCallNode {
|
||||
QueryListenCall() {
|
||||
this = query().getAMethodCall() and
|
||||
(getMethodName() = "on" or getMethodName() = "once")
|
||||
(this.getMethodName() = "on" or this.getMethodName() = "once")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument in which the callback is passed.
|
||||
*/
|
||||
DataFlow::Node getCallbackNode() { result = getArgument(1) }
|
||||
DataFlow::Node getCallbackNode() { result = this.getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,50 +183,46 @@ module Firebase {
|
||||
class RefBuilderListenCall extends DataFlow::MethodCallNode {
|
||||
RefBuilderListenCall() {
|
||||
this = ref().getAMethodCall() and
|
||||
getMethodName() = "on" + any(string s)
|
||||
this.getMethodName() = "on" + any(string s)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data flow node holding the listener callback.
|
||||
*/
|
||||
DataFlow::Node getCallbackNode() { result = getArgument(0) }
|
||||
DataFlow::Node getCallbackNode() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a Firebase method that sets up a route.
|
||||
*/
|
||||
private class RouteSetup extends HTTP::Servers::StandardRouteSetup, CallExpr {
|
||||
RouteSetup() {
|
||||
this = namespace().getAPropertyRead("https").getAMemberCall("onRequest").asExpr()
|
||||
}
|
||||
private class RouteSetup extends HTTP::Servers::StandardRouteSetup, DataFlow::CallNode {
|
||||
RouteSetup() { this = namespace().getAPropertyRead("https").getAMemberCall("onRequest") }
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() {
|
||||
result = getARouteHandler(DataFlow::TypeBackTracker::end())
|
||||
result = this.getARouteHandler(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = getArgument(0).flow().getALocalSource()
|
||||
result = this.getArgument(0).getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getARouteHandler(t2).backtrack(t2, t))
|
||||
exists(DataFlow::TypeBackTracker t2 | result = this.getARouteHandler(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
override Expr getServer() { none() }
|
||||
override DataFlow::Node getServer() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function used as a route handler.
|
||||
*/
|
||||
private class RouteHandler extends Express::RouteHandler, HTTP::Servers::StandardRouteHandler,
|
||||
DataFlow::ValueNode {
|
||||
override Function astNode;
|
||||
|
||||
DataFlow::FunctionNode {
|
||||
RouteHandler() { this = any(RouteSetup setup).getARouteHandler() }
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and result = astNode.getParameter(0)
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and result = this.getParameter(0)
|
||||
or
|
||||
kind = "response" and result = astNode.getParameter(1)
|
||||
kind = "response" and result = this.getParameter(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ module HTTP {
|
||||
/**
|
||||
* A function invocation that causes a redirect response to be sent.
|
||||
*/
|
||||
abstract class RedirectInvocation extends InvokeExpr {
|
||||
abstract class RedirectInvocation extends DataFlow::CallNode {
|
||||
/** Gets the argument specifying the URL to redirect to. */
|
||||
abstract Expr getUrlArgument();
|
||||
abstract DataFlow::Node getUrlArgument();
|
||||
|
||||
/** Gets the route handler this redirect occurs in. */
|
||||
abstract RouteHandler getRouteHandler();
|
||||
@@ -56,24 +56,34 @@ module HTTP {
|
||||
* An expression that sets HTTP response headers explicitly.
|
||||
*/
|
||||
abstract class ExplicitHeaderDefinition extends HeaderDefinition {
|
||||
override string getAHeaderName() { this.definesExplicitly(result, _) }
|
||||
override string getAHeaderName() { this.definesHeaderValue(result, _) }
|
||||
|
||||
override predicate defines(string headerName, string headerValue) {
|
||||
exists(Expr e |
|
||||
this.definesExplicitly(headerName, e) and
|
||||
exists(DataFlow::Node e |
|
||||
this.definesHeaderValue(headerName, e) and
|
||||
headerValue = e.getStringValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `definesHeaderValue` instead.
|
||||
* Holds if the header with (lower-case) name `headerName` is set to the value of `headerValue`.
|
||||
*/
|
||||
abstract predicate definesExplicitly(string headerName, Expr headerValue);
|
||||
deprecated predicate definesExplicitly(string headerName, Expr headerValue) {
|
||||
this.definesHeaderValue(headerName, headerValue.flow())
|
||||
}
|
||||
|
||||
/** Holds if the header with (lower-case) name `headerName` is set to the value of `headerValue`. */
|
||||
abstract predicate definesHeaderValue(string headerName, DataFlow::Node headerValue);
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getNameNode()` instead.
|
||||
* Returns the expression used to compute the header name.
|
||||
*/
|
||||
abstract Expr getNameExpr();
|
||||
deprecated Expr getNameExpr() { result = this.getNameNode().asExpr() }
|
||||
|
||||
/** Returns the expression used to compute the header name. */
|
||||
abstract DataFlow::Node getNameNode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,7 +117,7 @@ module HTTP {
|
||||
/**
|
||||
* An expression whose value is sent as (part of) the body of an HTTP response.
|
||||
*/
|
||||
abstract class ResponseBody extends Expr {
|
||||
abstract class ResponseBody extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the route handler that sends this expression.
|
||||
*/
|
||||
@@ -123,21 +133,21 @@ module HTTP {
|
||||
/**
|
||||
* An expression that sets a cookie in an HTTP response.
|
||||
*/
|
||||
abstract class CookieDefinition extends Expr {
|
||||
abstract class CookieDefinition extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the argument, if any, specifying the raw cookie header.
|
||||
*/
|
||||
Expr getHeaderArgument() { none() }
|
||||
DataFlow::Node getHeaderArgument() { none() }
|
||||
|
||||
/**
|
||||
* Gets the argument, if any, specifying the cookie name.
|
||||
*/
|
||||
Expr getNameArgument() { none() }
|
||||
DataFlow::Node getNameArgument() { none() }
|
||||
|
||||
/**
|
||||
* Gets the argument, if any, specifying the cookie value.
|
||||
*/
|
||||
Expr getValueArgument() { none() }
|
||||
DataFlow::Node getValueArgument() { none() }
|
||||
|
||||
/** Gets the route handler that sets this cookie. */
|
||||
abstract RouteHandler getRouteHandler();
|
||||
@@ -150,12 +160,12 @@ module HTTP {
|
||||
HeaderDefinition header;
|
||||
|
||||
SetCookieHeader() {
|
||||
this = header.asExpr() and
|
||||
this = header and
|
||||
header.getAHeaderName() = "set-cookie"
|
||||
}
|
||||
|
||||
override Expr getHeaderArgument() {
|
||||
header.(ExplicitHeaderDefinition).definesExplicitly("set-cookie", result)
|
||||
override DataFlow::Node getHeaderArgument() {
|
||||
header.(ExplicitHeaderDefinition).definesHeaderValue("set-cookie", result)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = header.getRouteHandler() }
|
||||
@@ -164,7 +174,7 @@ module HTTP {
|
||||
/**
|
||||
* An expression that creates a new server.
|
||||
*/
|
||||
abstract class ServerDefinition extends Expr {
|
||||
abstract class ServerDefinition extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a route handler of the server.
|
||||
*/
|
||||
@@ -198,16 +208,30 @@ module HTTP {
|
||||
final Servers::ResponseSource getAResponseSource() { result.getRouteHandler() = this }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getARequestNode()` instead.
|
||||
* Gets an expression that contains a request object handled
|
||||
* by this handler.
|
||||
*/
|
||||
RequestExpr getARequestExpr() { result.getRouteHandler() = this }
|
||||
deprecated RequestExpr getARequestExpr() { result.flow() = this.getARequestNode() }
|
||||
|
||||
/**
|
||||
* Gets an expression that contains a request object handled
|
||||
* by this handler.
|
||||
*/
|
||||
RequestNode getARequestNode() { result.getRouteHandler() = this }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getAResponseNode()` instead.
|
||||
* Gets an expression that contains a response object provided
|
||||
* by this handler.
|
||||
*/
|
||||
deprecated ResponseExpr getAResponseExpr() { result.flow() = this.getAResponseNode() }
|
||||
|
||||
/**
|
||||
* Gets an expression that contains a response object provided
|
||||
* by this handler.
|
||||
*/
|
||||
ResponseExpr getAResponseExpr() { result.getRouteHandler() = this }
|
||||
ResponseNode getAResponseNode() { result.getRouteHandler() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,26 +256,42 @@ module HTTP {
|
||||
/**
|
||||
* An expression that sets up a route on a server.
|
||||
*/
|
||||
abstract class RouteSetup extends Expr { }
|
||||
abstract class RouteSetup extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* An expression that may contain a request object.
|
||||
*/
|
||||
abstract class RequestExpr extends Expr {
|
||||
/**
|
||||
* Gets the route handler that handles this request.
|
||||
*/
|
||||
/** A dataflow node that may contain a request object. */
|
||||
abstract class RequestNode extends DataFlow::Node {
|
||||
/** Gets the route handler that handles this request. */
|
||||
abstract RouteHandler getRouteHandler();
|
||||
}
|
||||
|
||||
/** An dataflow node that may contain a response object. */
|
||||
abstract class ResponseNode extends DataFlow::Node {
|
||||
/** Gets the route handler that handles this request. */
|
||||
abstract RouteHandler getRouteHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that may contain a response object.
|
||||
* DEPRECATED: Use `RequestNode` instead.
|
||||
* An expression that may contain a request object.
|
||||
*/
|
||||
abstract class ResponseExpr extends Expr {
|
||||
deprecated class RequestExpr extends Expr {
|
||||
RequestExpr() { this.flow() instanceof ResponseNode }
|
||||
|
||||
/**
|
||||
* Gets the route handler that handles this request.
|
||||
*/
|
||||
abstract RouteHandler getRouteHandler();
|
||||
RouteHandler getRouteHandler() { result = this.flow().(ResponseNode).getRouteHandler() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ResponseNode` instead.
|
||||
* An expression that may contain a response object.
|
||||
*/
|
||||
deprecated class ResponseExpr extends Expr {
|
||||
/**
|
||||
* Gets the route handler that handles this request.
|
||||
*/
|
||||
RouteHandler getRouteHandler() { result = this.flow().(ResponseNode).getRouteHandler() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,15 +307,19 @@ module HTTP {
|
||||
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::exprNode(this)
|
||||
result = this.getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = this.ref(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to this server. */
|
||||
DataFlow::SourceNode ref() { result = this.ref(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ref().flowsToExpr()` instead.
|
||||
* Holds if `sink` may refer to this server definition.
|
||||
*/
|
||||
predicate flowsTo(Expr sink) { this.ref(DataFlow::TypeTracker::end()).flowsToExpr(sink) }
|
||||
deprecated predicate flowsTo(Expr sink) { this.ref().flowsToExpr(sink) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,7 +334,7 @@ module HTTP {
|
||||
/**
|
||||
* Gets the server this route handler is registered on.
|
||||
*/
|
||||
Expr getServer() {
|
||||
DataFlow::Node getServer() {
|
||||
exists(StandardRouteSetup setup | setup.getARouteHandler() = this |
|
||||
result = setup.getServer()
|
||||
)
|
||||
@@ -350,10 +394,10 @@ module HTTP {
|
||||
/**
|
||||
* A request expression arising from a request source.
|
||||
*/
|
||||
class StandardRequestExpr extends RequestExpr {
|
||||
class StandardRequestNode extends RequestNode {
|
||||
RequestSource src;
|
||||
|
||||
StandardRequestExpr() { src.ref().flowsTo(DataFlow::valueNode(this)) }
|
||||
StandardRequestNode() { src.ref().flowsTo(this) }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = src.getRouteHandler() }
|
||||
}
|
||||
@@ -361,26 +405,49 @@ module HTTP {
|
||||
/**
|
||||
* A response expression arising from a response source.
|
||||
*/
|
||||
class StandardResponseExpr extends ResponseExpr {
|
||||
class StandardResponseNode extends ResponseNode {
|
||||
ResponseSource src;
|
||||
|
||||
StandardResponseExpr() { src.ref().flowsTo(DataFlow::valueNode(this)) }
|
||||
StandardResponseNode() { src.ref().flowsTo(this) }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = src.getRouteHandler() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A standard header definition.
|
||||
* A request expression arising from a request source.
|
||||
*/
|
||||
abstract class StandardHeaderDefinition extends ExplicitHeaderDefinition, DataFlow::ValueNode {
|
||||
override MethodCallExpr astNode;
|
||||
deprecated class StandardRequestExpr extends RequestExpr {
|
||||
RequestSource src;
|
||||
|
||||
override predicate definesExplicitly(string headerName, Expr headerValue) {
|
||||
headerName = this.getNameExpr().getStringValue().toLowerCase() and
|
||||
headerValue = astNode.getArgument(1)
|
||||
StandardRequestExpr() { src.ref().flowsToExpr(this) }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = src.getRouteHandler() }
|
||||
}
|
||||
|
||||
override Expr getNameExpr() { result = astNode.getArgument(0) }
|
||||
/**
|
||||
* A response expression arising from a response source.
|
||||
*/
|
||||
deprecated class StandardResponseExpr extends ResponseExpr {
|
||||
ResponseSource src;
|
||||
|
||||
StandardResponseExpr() { src.ref().flowsToExpr(this) }
|
||||
|
||||
override RouteHandler getRouteHandler() {
|
||||
result = this.flow().(StandardResponseNode).getRouteHandler()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A standard header definition.
|
||||
*/
|
||||
abstract class StandardHeaderDefinition extends ExplicitHeaderDefinition,
|
||||
DataFlow::MethodCallNode {
|
||||
override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) {
|
||||
headerName = this.getNameNode().getStringValue().toLowerCase() and
|
||||
headerValue = this.getArgument(1)
|
||||
}
|
||||
|
||||
override DataFlow::Node getNameNode() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,7 +463,7 @@ module HTTP {
|
||||
/**
|
||||
* Gets the server on which this route setup sets up routes.
|
||||
*/
|
||||
abstract Expr getServer();
|
||||
abstract DataFlow::Node getServer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,39 +9,34 @@ module Hapi {
|
||||
/**
|
||||
* An expression that creates a new Hapi server.
|
||||
*/
|
||||
class ServerDefinition extends HTTP::Servers::StandardServerDefinition, NewExpr {
|
||||
class ServerDefinition extends HTTP::Servers::StandardServerDefinition, DataFlow::NewNode {
|
||||
ServerDefinition() {
|
||||
// `server = new Hapi.Server()`
|
||||
this = DataFlow::moduleMember("hapi", "Server").getAnInstantiation().asExpr()
|
||||
this = DataFlow::moduleMember("hapi", "Server").getAnInstantiation()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hapi route handler.
|
||||
*/
|
||||
class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode {
|
||||
Function function;
|
||||
|
||||
RouteHandler() {
|
||||
function = astNode and
|
||||
exists(RouteSetup setup | this = setup.getARouteHandler())
|
||||
}
|
||||
class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
RouteHandler() { exists(RouteSetup setup | this = setup.getARouteHandler()) }
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
Parameter getRequestParameter() { result = function.getParameter(0) }
|
||||
DataFlow::ParameterNode getRequestParameter() { result = this.getParameter(0) }
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the "request toolkit",
|
||||
* usually named `h`.
|
||||
*/
|
||||
Parameter getRequestToolkitParameter() { result = function.getParameter(1) }
|
||||
DataFlow::ParameterNode getRequestToolkitParameter() { result = this.getParameter(1) }
|
||||
|
||||
/**
|
||||
* Gets a source node referring to the request toolkit parameter, usually named `h`.
|
||||
*/
|
||||
DataFlow::SourceNode getRequestToolkit() { result = getRequestToolkitParameter().flow() }
|
||||
DataFlow::SourceNode getRequestToolkit() { result = this.getRequestToolkitParameter() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,9 +44,9 @@ module Hapi {
|
||||
* of a request object.
|
||||
*/
|
||||
private class ResponseSource extends HTTP::Servers::ResponseSource {
|
||||
RequestExpr req;
|
||||
RequestNode req;
|
||||
|
||||
ResponseSource() { asExpr().(PropAccess).accesses(req, "response") }
|
||||
ResponseSource() { this.(DataFlow::PropRead).accesses(req, "response") }
|
||||
|
||||
/**
|
||||
* Gets the route handler that provides this response.
|
||||
@@ -66,7 +61,7 @@ module Hapi {
|
||||
private class RequestSource extends HTTP::Servers::RequestSource {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) }
|
||||
RequestSource() { this = rh.getRequestParameter() }
|
||||
|
||||
/**
|
||||
* Gets the route handler that handles this request.
|
||||
@@ -75,16 +70,32 @@ module Hapi {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ResponseNode` instead.
|
||||
* A Hapi response expression.
|
||||
*/
|
||||
class ResponseExpr extends HTTP::Servers::StandardResponseExpr {
|
||||
deprecated class ResponseExpr extends HTTP::Servers::StandardResponseExpr {
|
||||
ResponseExpr() { this.flow() instanceof ResponseNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hapi response node.
|
||||
*/
|
||||
class ResponseNode extends HTTP::Servers::StandardResponseNode {
|
||||
override ResponseSource src;
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RequestNode` instead.
|
||||
* An Hapi request expression.
|
||||
*/
|
||||
class RequestExpr extends HTTP::Servers::StandardRequestExpr {
|
||||
deprecated class RequestExpr extends HTTP::Servers::StandardRequestExpr {
|
||||
RequestExpr() { this.flow() instanceof RequestNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hapi request node.
|
||||
*/
|
||||
class RequestNode extends HTTP::Servers::StandardRequestNode {
|
||||
override RequestSource src;
|
||||
}
|
||||
|
||||
@@ -96,38 +107,38 @@ module Hapi {
|
||||
string kind;
|
||||
|
||||
RequestInputAccess() {
|
||||
exists(Expr request | request = rh.getARequestExpr() |
|
||||
exists(DataFlow::Node request | request = rh.getARequestNode() |
|
||||
kind = "body" and
|
||||
(
|
||||
// `request.rawPayload`
|
||||
this.asExpr().(PropAccess).accesses(request, "rawPayload")
|
||||
this.(DataFlow::PropRead).accesses(request, "rawPayload")
|
||||
or
|
||||
exists(PropAccess payload |
|
||||
exists(DataFlow::PropRead payload |
|
||||
// `request.payload.name`
|
||||
payload.accesses(request, "payload") and
|
||||
this.asExpr().(PropAccess).accesses(payload, _)
|
||||
this.(DataFlow::PropRead).accesses(payload, _)
|
||||
)
|
||||
)
|
||||
or
|
||||
kind = "parameter" and
|
||||
exists(PropAccess query |
|
||||
exists(DataFlow::PropRead query |
|
||||
// `request.query.name`
|
||||
query.accesses(request, "query") and
|
||||
this.asExpr().(PropAccess).accesses(query, _)
|
||||
this.(DataFlow::PropRead).accesses(query, _)
|
||||
)
|
||||
or
|
||||
exists(PropAccess url |
|
||||
exists(DataFlow::PropRead url |
|
||||
// `request.url.path`
|
||||
kind = "url" and
|
||||
url.accesses(request, "url") and
|
||||
this.asExpr().(PropAccess).accesses(url, "path")
|
||||
this.(DataFlow::PropRead).accesses(url, "path")
|
||||
)
|
||||
or
|
||||
exists(PropAccess state |
|
||||
exists(DataFlow::PropRead state |
|
||||
// `request.state.<name>`
|
||||
kind = "cookie" and
|
||||
state.accesses(request, "state") and
|
||||
this.asExpr().(PropAccess).accesses(state, _)
|
||||
this.(DataFlow::PropRead).accesses(state, _)
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -149,11 +160,11 @@ module Hapi {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestHeaderAccess() {
|
||||
exists(Expr request | request = rh.getARequestExpr() |
|
||||
exists(PropAccess headers |
|
||||
exists(DataFlow::Node request | request = rh.getARequestNode() |
|
||||
exists(DataFlow::PropRead headers |
|
||||
// `request.headers.<name>`
|
||||
headers.accesses(request, "headers") and
|
||||
this.asExpr().(PropAccess).accesses(headers, _)
|
||||
this.(DataFlow::PropRead).accesses(headers, _)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -171,11 +182,11 @@ module Hapi {
|
||||
* An HTTP header defined in a Hapi server.
|
||||
*/
|
||||
private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition {
|
||||
ResponseExpr res;
|
||||
ResponseNode res;
|
||||
|
||||
HeaderDefinition() {
|
||||
// request.response.header('Cache-Control', 'no-cache')
|
||||
astNode.calls(res, "header")
|
||||
this.calls(res, "header")
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = res.getRouteHandler() }
|
||||
@@ -184,40 +195,40 @@ module Hapi {
|
||||
/**
|
||||
* A call to a Hapi method that sets up a route.
|
||||
*/
|
||||
class RouteSetup extends MethodCallExpr, HTTP::Servers::StandardRouteSetup {
|
||||
class RouteSetup extends DataFlow::MethodCallNode, HTTP::Servers::StandardRouteSetup {
|
||||
ServerDefinition server;
|
||||
Expr handler;
|
||||
DataFlow::Node handler;
|
||||
|
||||
RouteSetup() {
|
||||
server.flowsTo(getReceiver()) and
|
||||
server.ref().getAMethodCall() = this and
|
||||
(
|
||||
// server.route({ handler: fun })
|
||||
getMethodName() = "route" and
|
||||
hasOptionArgument(0, "handler", handler)
|
||||
this.getMethodName() = "route" and
|
||||
this.getOptionArgument(0, "handler") = handler
|
||||
or
|
||||
// server.ext('/', fun)
|
||||
getMethodName() = "ext" and
|
||||
handler = getArgument(1)
|
||||
this.getMethodName() = "ext" and
|
||||
handler = this.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() {
|
||||
result = getARouteHandler(DataFlow::TypeBackTracker::end())
|
||||
result = this.getARouteHandler(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = getRouteHandler().getALocalSource()
|
||||
result = this.getRouteHandler().getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getARouteHandler(t2).backtrack(t2, t))
|
||||
exists(DataFlow::TypeBackTracker t2 | result = this.getARouteHandler(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private DataFlow::Node getRouteHandler() { result = handler.flow() }
|
||||
private DataFlow::Node getRouteHandler() { result = handler }
|
||||
|
||||
Expr getRouteHandlerExpr() { result = handler }
|
||||
deprecated Expr getRouteHandlerExpr() { result = handler.asExpr() }
|
||||
|
||||
override Expr getServer() { result = server }
|
||||
override DataFlow::Node getServer() { result = server }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,9 +268,9 @@ module Hapi {
|
||||
|
||||
override DataFlow::SourceNode getOutput() { none() }
|
||||
|
||||
override DataFlow::Node getTemplateFileNode() { result = getArgument(0) }
|
||||
override DataFlow::Node getTemplateFileNode() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getTemplateParamsNode() { result = getArgument(1) }
|
||||
override DataFlow::Node getTemplateParamsNode() { result = this.getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,7 +279,7 @@ module Hapi {
|
||||
private class HandlerReturn extends HTTP::ResponseSendArgument {
|
||||
RouteHandler handler;
|
||||
|
||||
HandlerReturn() { this = handler.(DataFlow::FunctionNode).getAReturn().asExpr() }
|
||||
HandlerReturn() { this = handler.(DataFlow::FunctionNode).getAReturn() }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = handler }
|
||||
}
|
||||
|
||||
@@ -83,16 +83,12 @@ private module HttpProxy {
|
||||
)
|
||||
}
|
||||
|
||||
override Parameter getRequestParameter() {
|
||||
exists(int req | routeHandlingEventHandler(event, req, _) |
|
||||
result = getFunction().getParameter(req)
|
||||
)
|
||||
override DataFlow::ParameterNode getRequestParameter() {
|
||||
exists(int req | routeHandlingEventHandler(event, req, _) | result = getParameter(req))
|
||||
}
|
||||
|
||||
override Parameter getResponseParameter() {
|
||||
exists(int res | routeHandlingEventHandler(event, _, res) |
|
||||
result = getFunction().getParameter(res)
|
||||
)
|
||||
override DataFlow::ParameterNode getResponseParameter() {
|
||||
exists(int res | routeHandlingEventHandler(event, _, res) | result = getParameter(res))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +40,10 @@ private module JsonWebToken {
|
||||
}
|
||||
|
||||
/**
|
||||
* The private key for a JWT as a `CredentialsExpr`.
|
||||
* The private key for a JWT as a `CredentialsNode`.
|
||||
*/
|
||||
private class JwtKey extends CredentialsExpr {
|
||||
JwtKey() {
|
||||
this = DataFlow::moduleMember("jsonwebtoken", "sign").getACall().getArgument(1).asExpr()
|
||||
}
|
||||
private class JwtKey extends CredentialsNode {
|
||||
JwtKey() { this = DataFlow::moduleMember("jsonwebtoken", "sign").getACall().getArgument(1) }
|
||||
|
||||
override string getCredentialsKind() { result = "key" }
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ module Knex {
|
||||
|
||||
/** A SQL string passed to a raw Knex method. */
|
||||
private class RawKnexSqlString extends SQL::SqlString {
|
||||
RawKnexSqlString() { this = any(RawKnexCall call).getArgument(0).asExpr() }
|
||||
RawKnexSqlString() { this = any(RawKnexCall call).getArgument(0) }
|
||||
}
|
||||
|
||||
/** A call that triggers a SQL query submission by calling then/stream/asCallback. */
|
||||
|
||||
@@ -9,10 +9,10 @@ module Koa {
|
||||
/**
|
||||
* An expression that creates a new Koa application.
|
||||
*/
|
||||
class AppDefinition extends HTTP::Servers::StandardServerDefinition, InvokeExpr {
|
||||
class AppDefinition extends HTTP::Servers::StandardServerDefinition, DataFlow::InvokeNode {
|
||||
AppDefinition() {
|
||||
// `app = new Koa()` / `app = Koa()`
|
||||
this = DataFlow::moduleImport("koa").getAnInvocation().asExpr()
|
||||
this = DataFlow::moduleImport("koa").getAnInvocation()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ module Koa {
|
||||
|
||||
HeaderDefinition() {
|
||||
// ctx.set('Cache-Control', 'no-cache');
|
||||
astNode.calls(rh.getAResponseOrContextExpr(), "set")
|
||||
this.calls(rh.getAResponseOrContextNode(), "set")
|
||||
or
|
||||
// ctx.response.header('Cache-Control', 'no-cache')
|
||||
astNode.calls(rh.getAResponseExpr(), "header")
|
||||
this.calls(rh.getAResponseNode(), "header")
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
@@ -40,10 +40,17 @@ module Koa {
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the context object.
|
||||
*/
|
||||
Parameter getContextParameter() {
|
||||
result = this.getAFunctionValue().getFunction().getParameter(0)
|
||||
DataFlow::ParameterNode getContextParameter() {
|
||||
result = this.getAFunctionValue().getParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getAContextNode` instead.
|
||||
* Gets an expression that contains the "context" object of
|
||||
* a route handler invocation.
|
||||
*/
|
||||
deprecated Expr getAContextExpr() { result = this.getAContextNode().asExpr() }
|
||||
|
||||
/**
|
||||
* Gets an expression that contains the "context" object of
|
||||
* a route handler invocation.
|
||||
@@ -52,22 +59,38 @@ module Koa {
|
||||
* `this` or `ctx`, given as the first and only argument to the
|
||||
* route handler.
|
||||
*/
|
||||
Expr getAContextExpr() { result.(ContextExpr).getRouteHandler() = this }
|
||||
DataFlow::Node getAContextNode() { result.(ContextNode).getRouteHandler() = this }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getAResponseOrContextNode` instead.
|
||||
* Gets an expression that contains the context or response
|
||||
* object of a route handler invocation.
|
||||
*/
|
||||
deprecated Expr getAResponseOrContextExpr() {
|
||||
result = this.getAResponseOrContextNode().asExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that contains the context or response
|
||||
* object of a route handler invocation.
|
||||
*/
|
||||
Expr getAResponseOrContextExpr() {
|
||||
result = this.getAResponseExpr() or result = this.getAContextExpr()
|
||||
DataFlow::Node getAResponseOrContextNode() {
|
||||
result = this.getAResponseNode() or result = this.getAContextNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getARequestOrContextNode` instead.
|
||||
* Gets an expression that contains the context or request
|
||||
* object of a route handler invocation.
|
||||
*/
|
||||
deprecated Expr getARequestOrContextExpr() { result = this.getARequestOrContextNode().asExpr() }
|
||||
|
||||
/**
|
||||
* Gets an expression that contains the context or request
|
||||
* object of a route handler invocation.
|
||||
*/
|
||||
Expr getARequestOrContextExpr() {
|
||||
result = this.getARequestExpr() or result = this.getAContextExpr()
|
||||
DataFlow::Node getARequestOrContextNode() {
|
||||
result = this.getARequestNode() or result = this.getAContextNode()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +131,7 @@ module Koa {
|
||||
RouteHandler rh;
|
||||
|
||||
ContextSource() {
|
||||
this = DataFlow::parameterNode(rh.getContextParameter())
|
||||
this = rh.getContextParameter()
|
||||
or
|
||||
this.(DataFlow::ThisNode).getBinder() = rh
|
||||
}
|
||||
@@ -118,8 +141,6 @@ module Koa {
|
||||
*/
|
||||
RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
predicate flowsTo(DataFlow::Node nd) { this.ref().flowsTo(nd) }
|
||||
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = this
|
||||
@@ -206,10 +227,10 @@ module Koa {
|
||||
* A Koa request source, that is, an access to the `request` property
|
||||
* of a context object.
|
||||
*/
|
||||
private class RequestSource extends HTTP::Servers::RequestSource {
|
||||
ContextExpr ctx;
|
||||
private class RequestSource extends HTTP::Servers::RequestSource instanceof DataFlow::PropRead {
|
||||
ContextNode ctx;
|
||||
|
||||
RequestSource() { this.asExpr().(PropAccess).accesses(ctx, "request") }
|
||||
RequestSource() { super.accesses(ctx, "request") }
|
||||
|
||||
/**
|
||||
* Gets the route handler that provides this response.
|
||||
@@ -241,10 +262,10 @@ module Koa {
|
||||
* A Koa response source, that is, an access to the `response` property
|
||||
* of a context object.
|
||||
*/
|
||||
private class ResponseSource extends HTTP::Servers::ResponseSource {
|
||||
ContextExpr ctx;
|
||||
private class ResponseSource extends HTTP::Servers::ResponseSource instanceof DataFlow::PropRead {
|
||||
ContextNode ctx;
|
||||
|
||||
ResponseSource() { this.asExpr().(PropAccess).accesses(ctx, "response") }
|
||||
ResponseSource() { super.accesses(ctx, "response") }
|
||||
|
||||
/**
|
||||
* Gets the route handler that provides this response.
|
||||
@@ -253,12 +274,25 @@ module Koa {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ContextNode` instead.
|
||||
* An expression that may hold a Koa context object.
|
||||
*/
|
||||
class ContextExpr extends Expr {
|
||||
deprecated class ContextExpr extends Expr {
|
||||
ContextNode node;
|
||||
|
||||
ContextExpr() { node.asExpr() = this }
|
||||
|
||||
/** Gets the route handler that provides this response. */
|
||||
deprecated RouteHandler getRouteHandler() { result = node.getRouteHandler() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that may hold a Koa context object.
|
||||
*/
|
||||
class ContextNode extends DataFlow::Node {
|
||||
ContextSource src;
|
||||
|
||||
ContextExpr() { src.flowsTo(DataFlow::valueNode(this)) }
|
||||
ContextNode() { src.ref().flowsTo(this) }
|
||||
|
||||
/**
|
||||
* Gets the route handler that provides this response.
|
||||
@@ -267,16 +301,32 @@ module Koa {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RequestNode` instead.
|
||||
* An expression that may hold a Koa request object.
|
||||
*/
|
||||
class RequestExpr extends HTTP::Servers::StandardRequestExpr {
|
||||
deprecated class RequestExpr extends HTTP::Servers::StandardRequestExpr {
|
||||
RequestExpr() { this.flow() instanceof RequestNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that may hold a Koa request object.
|
||||
*/
|
||||
class RequestNode extends HTTP::Servers::StandardRequestNode {
|
||||
override RequestSource src;
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ResponseNode` instead.
|
||||
* An expression that may hold a Koa response object.
|
||||
*/
|
||||
deprecated class ResponseExpr extends HTTP::Servers::StandardResponseExpr {
|
||||
ResponseExpr() { this.flow() instanceof ResponseNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that may hold a Koa response object.
|
||||
*/
|
||||
class ResponseExpr extends HTTP::Servers::StandardResponseExpr {
|
||||
class ResponseNode extends HTTP::Servers::StandardResponseNode {
|
||||
override ResponseSource src;
|
||||
}
|
||||
|
||||
@@ -294,11 +344,11 @@ module Koa {
|
||||
kind = "parameter" and
|
||||
this = rh.getARequestParameterAccess()
|
||||
or
|
||||
exists(Expr e | rh.getARequestOrContextExpr() = e |
|
||||
exists(DataFlow::Node e | rh.getARequestOrContextNode() = e |
|
||||
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
|
||||
exists(string propName |
|
||||
kind = "url" and
|
||||
this.asExpr().(PropAccess).accesses(e, propName)
|
||||
this.(DataFlow::PropRead).accesses(e, propName)
|
||||
|
|
||||
propName = "url"
|
||||
or
|
||||
@@ -309,19 +359,19 @@ module Koa {
|
||||
or
|
||||
// params, when handler is registered by `koa-router` or similar.
|
||||
kind = "parameter" and
|
||||
this.asExpr().(PropAccess).accesses(e, "params")
|
||||
this.(DataFlow::PropRead).accesses(e, "params")
|
||||
or
|
||||
// `ctx.request.body`
|
||||
e instanceof RequestExpr and
|
||||
e instanceof RequestNode and
|
||||
kind = "body" and
|
||||
this.asExpr().(PropAccess).accesses(e, "body")
|
||||
this.(DataFlow::PropRead).accesses(e, "body")
|
||||
or
|
||||
// `ctx.cookies.get(<name>)`
|
||||
exists(PropAccess cookies |
|
||||
e instanceof ContextExpr and
|
||||
exists(DataFlow::PropRead cookies |
|
||||
e instanceof ContextNode and
|
||||
kind = "cookie" and
|
||||
cookies.accesses(e, "cookies") and
|
||||
this = cookies.flow().(DataFlow::SourceNode).getAMethodCall("get")
|
||||
this = cookies.getAMethodCall("get")
|
||||
)
|
||||
or
|
||||
exists(RequestHeaderAccess access | access = this |
|
||||
@@ -340,9 +390,9 @@ module Koa {
|
||||
|
||||
private DataFlow::Node getAQueryParameterAccess(RouteHandler rh) {
|
||||
// `ctx.query.name` or `ctx.request.query.name`
|
||||
exists(PropAccess q |
|
||||
q.accesses(rh.getARequestOrContextExpr(), "query") and
|
||||
result = q.flow().(DataFlow::SourceNode).getAPropertyRead()
|
||||
exists(DataFlow::PropRead q |
|
||||
q.accesses(rh.getARequestOrContextNode(), "query") and
|
||||
result = q.getAPropertyRead()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -353,18 +403,18 @@ module Koa {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestHeaderAccess() {
|
||||
exists(Expr e | e = rh.getARequestOrContextExpr() |
|
||||
exists(string propName, PropAccess headers |
|
||||
exists(DataFlow::Node e | e = rh.getARequestOrContextNode() |
|
||||
exists(string propName, DataFlow::PropRead headers |
|
||||
// `ctx.request.header.<name>`, `ctx.request.headers.<name>`
|
||||
headers.accesses(e, propName) and
|
||||
this = headers.flow().(DataFlow::SourceNode).getAPropertyRead()
|
||||
this = headers.getAPropertyRead()
|
||||
|
|
||||
propName = "header" or
|
||||
propName = "headers"
|
||||
)
|
||||
or
|
||||
// `ctx.request.get(<name>)`
|
||||
this.asExpr().(MethodCallExpr).calls(e, "get")
|
||||
this.(DataFlow::MethodCallNode).calls(e, "get")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -385,24 +435,23 @@ module Koa {
|
||||
/**
|
||||
* A call to a Koa method that sets up a route.
|
||||
*/
|
||||
class RouteSetup extends HTTP::Servers::StandardRouteSetup, MethodCallExpr {
|
||||
class RouteSetup extends HTTP::Servers::StandardRouteSetup, DataFlow::MethodCallNode {
|
||||
AppDefinition server;
|
||||
|
||||
RouteSetup() {
|
||||
// app.use(fun)
|
||||
server.flowsTo(this.getReceiver()) and
|
||||
this.getMethodName() = "use"
|
||||
server.ref().getAMethodCall("use") = this
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() {
|
||||
// `StandardRouteHandler` uses this predicate in it's charpred, so making this predicate return a `RouteHandler` would give an empty recursion.
|
||||
result.flowsToExpr(this.getArgument(0))
|
||||
result.flowsTo(this.getArgument(0))
|
||||
or
|
||||
// For the route-handlers that does not depend on this predicate in their charpred.
|
||||
result.(RouteHandler).getARouteHandlerRegistrationObject().flowsToExpr(this.getArgument(0))
|
||||
result.(RouteHandler).getARouteHandlerRegistrationObject().flowsTo(this.getArgument(0))
|
||||
}
|
||||
|
||||
override Expr getServer() { result = server }
|
||||
override DataFlow::Node getServer() { result = server }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,10 +461,7 @@ module Koa {
|
||||
RouteHandler rh;
|
||||
|
||||
ResponseSendArgument() {
|
||||
exists(DataFlow::PropWrite pwn |
|
||||
pwn.writes(DataFlow::valueNode(rh.getAResponseOrContextExpr()), "body",
|
||||
DataFlow::valueNode(this))
|
||||
)
|
||||
exists(DataFlow::PropWrite pwn | pwn.writes(rh.getAResponseOrContextNode(), "body", this))
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
@@ -424,12 +470,12 @@ module Koa {
|
||||
/**
|
||||
* An invocation of the `redirect` method of an HTTP response object.
|
||||
*/
|
||||
private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr {
|
||||
private class RedirectInvocation extends HTTP::RedirectInvocation instanceof DataFlow::MethodCallNode {
|
||||
RouteHandler rh;
|
||||
|
||||
RedirectInvocation() { this.(MethodCallExpr).calls(rh.getAResponseOrContextExpr(), "redirect") }
|
||||
RedirectInvocation() { super.calls(rh.getAResponseOrContextNode(), "redirect") }
|
||||
|
||||
override Expr getUrlArgument() { result = this.getArgument(0) }
|
||||
override DataFlow::Node getUrlArgument() { result = this.getArgument(0) }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ private module LiveServer {
|
||||
* An expression that imports the live-server package, seen as a server-definition.
|
||||
*/
|
||||
class ServerDefinition extends HTTP::Servers::StandardServerDefinition {
|
||||
ServerDefinition() { this = DataFlow::moduleImport("live-server").asExpr() }
|
||||
ServerDefinition() { this = DataFlow::moduleImport("live-server") }
|
||||
|
||||
API::Node getImportNode() { result.asSource().asExpr() = this }
|
||||
API::Node getImportNode() { result.asSource() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,26 +22,22 @@ private module LiveServer {
|
||||
class RouteHandler extends Connect::RouteHandler, DataFlow::FunctionNode {
|
||||
RouteHandler() { this = any(RouteSetup setup).getARouteHandler() }
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
result = ConnectExpressShared::getRouteHandlerParameter(astNode, kind)
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
result = ConnectExpressShared::getRouteHandlerParameter(this, kind)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The call to `require("live-server").start()`, seen as a route setup.
|
||||
*/
|
||||
class RouteSetup extends MethodCallExpr, HTTP::Servers::StandardRouteSetup {
|
||||
class RouteSetup extends HTTP::Servers::StandardRouteSetup instanceof API::CallNode {
|
||||
ServerDefinition server;
|
||||
API::CallNode call;
|
||||
|
||||
RouteSetup() {
|
||||
call = server.getImportNode().getMember("start").getACall() and
|
||||
this = call.asExpr()
|
||||
}
|
||||
RouteSetup() { this = server.getImportNode().getMember("start").getACall() }
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() {
|
||||
exists(DataFlow::SourceNode middleware |
|
||||
middleware = call.getParameter(0).getMember("middleware").getAValueReachingSink()
|
||||
middleware = super.getParameter(0).getMember("middleware").getAValueReachingSink()
|
||||
|
|
||||
result = middleware.getAMemberCall(["push", "unshift"]).getArgument(0).getAFunctionValue()
|
||||
or
|
||||
@@ -49,6 +45,6 @@ private module LiveServer {
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getServer() { result = server }
|
||||
override DataFlow::Node getServer() { result = server }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,8 +349,8 @@ private module Pino {
|
||||
or
|
||||
// `pino` is installed as the "log" property on the request object in `Express` and similar libraries.
|
||||
// in `Hapi` the property is "logger".
|
||||
exists(HTTP::RequestExpr req, API::Node reqNode |
|
||||
reqNode.asSource() = req.flow().getALocalSource() and
|
||||
exists(HTTP::RequestNode req, API::Node reqNode |
|
||||
reqNode.asSource() = req.getALocalSource() and
|
||||
result = reqNode.getMember(["log", "logger"])
|
||||
)
|
||||
}
|
||||
|
||||
@@ -62,11 +62,19 @@ private module Micro {
|
||||
override HTTP::RouteHandler getRouteHandler() { result = h }
|
||||
}
|
||||
|
||||
class MicroRequestExpr extends NodeJSLib::RequestExpr {
|
||||
deprecated class MicroRequestExpr extends NodeJSLib::RequestExpr {
|
||||
override MicroRequestSource src;
|
||||
}
|
||||
|
||||
class MicroReseponseExpr extends NodeJSLib::ResponseExpr {
|
||||
class MicroRequestNode extends NodeJSLib::RequestNode {
|
||||
override MicroRequestSource src;
|
||||
}
|
||||
|
||||
deprecated class MicroReseponseExpr extends NodeJSLib::ResponseExpr {
|
||||
override MicroResponseSource src;
|
||||
}
|
||||
|
||||
class MicroResponseNode extends NodeJSLib::ResponseNode {
|
||||
override MicroResponseSource src;
|
||||
}
|
||||
|
||||
@@ -104,7 +112,7 @@ private module Micro {
|
||||
|
||||
MicroSendArgument() {
|
||||
send = moduleMember("micro", ["send", "sendError"]).getACall() and
|
||||
this = send.getLastArgument().asExpr()
|
||||
this = send.getLastArgument()
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() {
|
||||
|
||||
@@ -56,7 +56,7 @@ module NestJS {
|
||||
*/
|
||||
predicate isReturnValueReflected() {
|
||||
getAFunctionDecorator(this) = nestjs().getMember(["Get", "Post"]).getACall() and
|
||||
not hasRedirectDecorator() and
|
||||
not this.hasRedirectDecorator() and
|
||||
not getAFunctionDecorator(this) = nestjs().getMember("Render").getACall()
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ module NestJS {
|
||||
NestJSRequestInput() {
|
||||
decoratorName =
|
||||
["Query", "Param", "Headers", "Body", "HostParam", "UploadedFile", "UploadedFiles"] and
|
||||
decorator = getADecorator() and
|
||||
decorator = this.getADecorator() and
|
||||
decorator = nestjs().getMember(decoratorName).getACall()
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ module NestJS {
|
||||
|
||||
/** Gets a pipe applied to this parameter, not including global pipes. */
|
||||
DataFlow::Node getAPipe() {
|
||||
result = getNestRouteHandler().getAPipe()
|
||||
result = this.getNestRouteHandler().getAPipe()
|
||||
or
|
||||
result = decorator.getArgument(1)
|
||||
or
|
||||
@@ -132,7 +132,7 @@ module NestJS {
|
||||
hasSanitizingPipe(this, false)
|
||||
or
|
||||
hasSanitizingPipe(this, true) and
|
||||
isSanitizingType(getParameter().getType().unfold())
|
||||
isSanitizingType(this.getParameter().getType().unfold())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,14 +240,14 @@ module NestJS {
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::FunctionNode getTransformFunction() { result = getInstanceMethod("transform") }
|
||||
DataFlow::FunctionNode getTransformFunction() { result = this.getInstanceMethod("transform") }
|
||||
|
||||
DataFlow::ParameterNode getInputData() { result = getTransformFunction().getParameter(0) }
|
||||
DataFlow::ParameterNode getInputData() { result = this.getTransformFunction().getParameter(0) }
|
||||
|
||||
DataFlow::Node getOutputData() { result = getTransformFunction().getReturnNode() }
|
||||
DataFlow::Node getOutputData() { result = this.getTransformFunction().getReturnNode() }
|
||||
|
||||
NestJSRequestInput getAnAffectedParameter() {
|
||||
[getAnInstanceReference(), getAClassReference()].flowsTo(result.getAPipe())
|
||||
[this.getAnInstanceReference(), this.getAClassReference()].flowsTo(result.getAPipe())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,16 +297,16 @@ module NestJS {
|
||||
private class NestJSRequestInputAsRequestInputAccess extends NestJSRequestInput,
|
||||
HTTP::RequestInputAccess {
|
||||
NestJSRequestInputAsRequestInputAccess() {
|
||||
not isSanitizedByPipe() and
|
||||
not this.isSanitizedByPipe() and
|
||||
not this = any(CustomPipeClass cls).getAnAffectedParameter()
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = getNestRouteHandler() }
|
||||
override HTTP::RouteHandler getRouteHandler() { result = this.getNestRouteHandler() }
|
||||
|
||||
override string getKind() { result = getInputKind() }
|
||||
override string getKind() { result = this.getInputKind() }
|
||||
|
||||
override predicate isUserControlledObject() {
|
||||
not exists(getAPipe()) and // value is not transformed by a pipe
|
||||
not exists(this.getAPipe()) and // value is not transformed by a pipe
|
||||
(
|
||||
decorator.getNumArgument() = 0
|
||||
or
|
||||
@@ -349,10 +349,10 @@ module NestJS {
|
||||
|
||||
ReturnValueAsResponseSend() {
|
||||
handler.isReturnValueReflected() and
|
||||
this = handler.getAReturn().asExpr() and
|
||||
this = handler.getAReturn() and
|
||||
// Only returned strings are sinks
|
||||
not exists(Type type |
|
||||
type = getType() and
|
||||
type = this.asExpr().getType() and
|
||||
not isStringType(type.unfold())
|
||||
)
|
||||
}
|
||||
@@ -389,15 +389,15 @@ module NestJS {
|
||||
CustomParameterDecorator() { this = nestjs().getMember("createParamDecorator").getACall() }
|
||||
|
||||
/** Gets the `context` parameter. */
|
||||
API::Node getExecutionContext() { result = getParameter(0).getParameter(1) }
|
||||
API::Node getExecutionContext() { result = this.getParameter(0).getParameter(1) }
|
||||
|
||||
/** Gets a parameter with this decorator applied. */
|
||||
DataFlow::ParameterNode getADecoratedParameter() {
|
||||
result.getADecorator() = getReturn().getReturn().getAValueReachableFromSource()
|
||||
result.getADecorator() = this.getReturn().getReturn().getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
/** Gets a value returned by the decorator's callback, which becomes the value of the decorated parameter. */
|
||||
DataFlow::Node getResult() { result = getParameter(0).getReturn().asSink() }
|
||||
DataFlow::Node getResult() { result = this.getParameter(0).getReturn().asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,11 +35,10 @@ module NextJS {
|
||||
*/
|
||||
Module getAModuleWithFallbackPaths() {
|
||||
result = getAPagesModule() and
|
||||
exists(DataFlow::FunctionNode staticPaths, Expr fallback |
|
||||
exists(DataFlow::FunctionNode staticPaths, DataFlow::Node fallback |
|
||||
staticPaths = result.getAnExportedValue("getStaticPaths").getAFunctionValue() and
|
||||
fallback =
|
||||
staticPaths.getAReturn().getALocalSource().getAPropertyWrite("fallback").getRhs().asExpr() and
|
||||
not fallback.(BooleanLiteral).getValue() = "false"
|
||||
fallback = staticPaths.getAReturn().getALocalSource().getAPropertyWrite("fallback").getRhs() and
|
||||
not fallback.mayHaveBooleanValue(false)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -230,10 +229,10 @@ module NextJS {
|
||||
)
|
||||
}
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and result = this.getFunction().getParameter(0)
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and result = this.getParameter(0)
|
||||
or
|
||||
kind = "response" and result = this.getFunction().getParameter(1)
|
||||
kind = "response" and result = this.getParameter(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import javascript
|
||||
|
||||
/** Provides classes for modeling NoSql query sinks. */
|
||||
module NoSql {
|
||||
/** An expression that is interpreted as a NoSql query. */
|
||||
abstract class Query extends Expr {
|
||||
/** An expression that is interpreted as a NoSQL query. */
|
||||
abstract class Query extends DataFlow::Node {
|
||||
/** Gets an expression that is interpreted as a code operator in this query. */
|
||||
DataFlow::Node getACodeOperator() { none() }
|
||||
}
|
||||
@@ -84,7 +84,7 @@ private module MongoDB {
|
||||
class Query extends NoSql::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
Query() { this = qc.getAQueryArgument() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
@@ -496,13 +496,11 @@ private module Mongoose {
|
||||
/**
|
||||
* An expression passed to `mongoose.createConnection` to supply credentials.
|
||||
*/
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string prop |
|
||||
this = createConnection().getParameter(3).getMember(prop).asSink().asExpr()
|
||||
|
|
||||
exists(string prop | this = createConnection().getParameter(3).getMember(prop).asSink() |
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
prop = "pass" and kind = "password"
|
||||
@@ -518,7 +516,7 @@ private module Mongoose {
|
||||
class MongoDBQueryPart extends NoSql::Query {
|
||||
MongooseFunction f;
|
||||
|
||||
MongoDBQueryPart() { this = f.getQueryArgument().asSink().asExpr() }
|
||||
MongoDBQueryPart() { this = f.getQueryArgument().asSink() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(f.getQueryArgument())
|
||||
@@ -625,7 +623,7 @@ private module Minimongo {
|
||||
class Query extends NoSql::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
Query() { this = qc.getAQueryArgument() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
@@ -685,7 +683,7 @@ private module MarsDB {
|
||||
class Query extends NoSql::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
Query() { this = qc.getAQueryArgument() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
@@ -770,7 +768,7 @@ private module Redis {
|
||||
RedisKeyArgument() {
|
||||
exists(string method, int argIndex |
|
||||
QuerySignatures::argumentIsAmbiguousKey(method, argIndex) and
|
||||
this = redis().getMember(method).getParameter(argIndex).asSink().asExpr()
|
||||
this = redis().getMember(method).getParameter(argIndex).asSink()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ module NodeJSLib {
|
||||
/**
|
||||
* Holds if `call` is an invocation of `http.createServer` or `https.createServer`.
|
||||
*/
|
||||
predicate isCreateServer(CallExpr call) {
|
||||
predicate isCreateServer(DataFlow::CallNode call) {
|
||||
exists(string pkg, string fn |
|
||||
pkg = "http" and fn = "createServer"
|
||||
or
|
||||
@@ -60,17 +60,39 @@ module NodeJSLib {
|
||||
or
|
||||
pkg = "http2" and fn = "createSecureServer"
|
||||
|
|
||||
call = DataFlow::moduleMember(pkg, fn).getAnInvocation().asExpr()
|
||||
call = DataFlow::moduleMember(pkg, fn).getAnInvocation()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ResponseNode` instead.
|
||||
* A Node.js HTTP response.
|
||||
*
|
||||
* A server library that provides an (enhanced) NodesJS HTTP response
|
||||
* object should implement a library specific subclass of this class.
|
||||
*/
|
||||
deprecated class ResponseExpr extends HTTP::Servers::StandardResponseExpr {
|
||||
ResponseExpr() { this.flow() instanceof ResponseNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Node.js HTTP response.
|
||||
*
|
||||
* A server library that provides an (enhanced) NodesJS HTTP response
|
||||
* object should implement a library specific subclass of this class.
|
||||
*/
|
||||
abstract class ResponseExpr extends HTTP::Servers::StandardResponseExpr { }
|
||||
abstract class ResponseNode extends HTTP::Servers::StandardResponseNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RequestNode` instead.
|
||||
* A Node.js HTTP request.
|
||||
*
|
||||
* A server library that provides an (enhanced) NodesJS HTTP request
|
||||
* object should implement a library specific subclass of this class.
|
||||
*/
|
||||
deprecated class RequestExpr extends HTTP::Servers::StandardRequestExpr {
|
||||
RequestExpr() { this.flow() instanceof RequestNode }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Node.js HTTP request.
|
||||
@@ -78,7 +100,7 @@ module NodeJSLib {
|
||||
* A server library that provides an (enhanced) NodesJS HTTP request
|
||||
* object should implement a library specific subclass of this class.
|
||||
*/
|
||||
abstract class RequestExpr extends HTTP::Servers::StandardRequestExpr { }
|
||||
abstract class RequestNode extends HTTP::Servers::StandardRequestNode { }
|
||||
|
||||
/**
|
||||
* A function used as an Node.js server route handler.
|
||||
@@ -91,12 +113,12 @@ module NodeJSLib {
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
Parameter getRequestParameter() { result = this.getFunction().getParameter(0) }
|
||||
DataFlow::ParameterNode getRequestParameter() { result = this.getParameter(0) }
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the response object.
|
||||
*/
|
||||
Parameter getResponseParameter() { result = this.getFunction().getParameter(1) }
|
||||
DataFlow::ParameterNode getResponseParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +140,7 @@ module NodeJSLib {
|
||||
private class StandardResponseSource extends ResponseSource {
|
||||
RouteHandler rh;
|
||||
|
||||
StandardResponseSource() { this = DataFlow::parameterNode(rh.getResponseParameter()) }
|
||||
StandardResponseSource() { this = rh.getResponseParameter() }
|
||||
|
||||
/**
|
||||
* Gets the route handler that provides this response.
|
||||
@@ -138,7 +160,7 @@ module NodeJSLib {
|
||||
private class StandardRequestSource extends RequestSource {
|
||||
RouteHandler rh;
|
||||
|
||||
StandardRequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) }
|
||||
StandardRequestSource() { this = rh.getRequestParameter() }
|
||||
|
||||
/**
|
||||
* Gets the route handler that handles this request.
|
||||
@@ -147,36 +169,52 @@ module NodeJSLib {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `BuiltinRouteHandlerResponseNode` instead.
|
||||
* A builtin Node.js HTTP response.
|
||||
*/
|
||||
private class BuiltinRouteHandlerResponseExpr extends ResponseExpr {
|
||||
deprecated private class BuiltinRouteHandlerResponseExpr extends ResponseExpr {
|
||||
BuiltinRouteHandlerResponseExpr() { src instanceof ResponseSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A builtin Node.js HTTP response.
|
||||
*/
|
||||
private class BuiltinRouteHandlerResponseNode extends ResponseNode {
|
||||
BuiltinRouteHandlerResponseNode() { src instanceof ResponseSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `BuiltinRouteHandlerRequestNode` instead.
|
||||
* A builtin Node.js HTTP request.
|
||||
*/
|
||||
deprecated private class BuiltinRouteHandlerRequestExpr extends RequestExpr {
|
||||
BuiltinRouteHandlerRequestExpr() { src instanceof RequestSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A builtin Node.js HTTP request.
|
||||
*/
|
||||
private class BuiltinRouteHandlerRequestExpr extends RequestExpr {
|
||||
BuiltinRouteHandlerRequestExpr() { src instanceof RequestSource }
|
||||
private class BuiltinRouteHandlerRequestNode extends RequestNode {
|
||||
BuiltinRouteHandlerRequestNode() { src instanceof RequestSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a user-controlled Node.js request input.
|
||||
*/
|
||||
private class RequestInputAccess extends HTTP::RequestInputAccess {
|
||||
RequestExpr request;
|
||||
RequestNode request;
|
||||
string kind;
|
||||
|
||||
RequestInputAccess() {
|
||||
// `req.url` / `req.body`
|
||||
kind = ["url", "body"] and
|
||||
this.asExpr().(PropAccess).accesses(request, kind)
|
||||
this.(DataFlow::PropRead).accesses(request, kind)
|
||||
or
|
||||
exists(PropAccess headers |
|
||||
exists(DataFlow::PropRead headers |
|
||||
// `req.headers.cookie`
|
||||
kind = "cookie" and
|
||||
headers.accesses(request, "headers") and
|
||||
this.asExpr().(PropAccess).accesses(headers, "cookie")
|
||||
this.(DataFlow::PropRead).accesses(headers, "cookie")
|
||||
)
|
||||
or
|
||||
exists(RequestHeaderAccess access | this = access |
|
||||
@@ -194,14 +232,14 @@ module NodeJSLib {
|
||||
* An access to an HTTP header (other than "Cookie") on an incoming Node.js request object.
|
||||
*/
|
||||
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
|
||||
RequestExpr request;
|
||||
RequestNode request;
|
||||
|
||||
RequestHeaderAccess() {
|
||||
exists(PropAccess headers, string name |
|
||||
exists(DataFlow::PropRead headers, string name |
|
||||
// `req.headers.<name>`
|
||||
name != "cookie" and
|
||||
headers.accesses(request, "headers") and
|
||||
this.asExpr().(PropAccess).accesses(headers, name)
|
||||
this.(DataFlow::PropRead).accesses(headers, name)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -213,19 +251,19 @@ module NodeJSLib {
|
||||
|
||||
override string getKind() { result = "header" }
|
||||
|
||||
RequestExpr getRequest() { result = request }
|
||||
RequestNode getRequest() { result = request }
|
||||
}
|
||||
|
||||
class RouteSetup extends CallExpr, HTTP::Servers::StandardRouteSetup {
|
||||
class RouteSetup extends DataFlow::CallNode, HTTP::Servers::StandardRouteSetup {
|
||||
ServerDefinition server;
|
||||
Expr handler;
|
||||
DataFlow::Node handler;
|
||||
|
||||
RouteSetup() {
|
||||
server.flowsTo(this) and
|
||||
server.ref() = this and
|
||||
handler = this.getLastArgument()
|
||||
or
|
||||
server.flowsTo(this.getReceiver()) and
|
||||
this.(MethodCallExpr).getMethodName().regexpMatch("on(ce)?") and
|
||||
server.ref().getAMethodCall() = this and
|
||||
this.getCalleeName().regexpMatch("on(ce)?") and
|
||||
this.getArgument(0).getStringValue() = "request" and
|
||||
handler = this.getArgument(1)
|
||||
}
|
||||
@@ -236,7 +274,7 @@ module NodeJSLib {
|
||||
|
||||
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = handler.flow().getALocalSource()
|
||||
result = handler.getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2, DataFlow::SourceNode succ |
|
||||
succ = this.getARouteHandler(t2)
|
||||
@@ -248,18 +286,24 @@ module NodeJSLib {
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getServer() { result = server }
|
||||
override DataFlow::Node getServer() { result = server }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getRouteHandlerNode` instead.
|
||||
* Gets the expression for the handler registered by this setup.
|
||||
*/
|
||||
deprecated Expr getRouteHandlerExpr() { result = handler.asExpr() }
|
||||
|
||||
/**
|
||||
* Gets the expression for the handler registered by this setup.
|
||||
*/
|
||||
Expr getRouteHandlerExpr() { result = handler }
|
||||
DataFlow::Node getRouteHandlerNode() { result = handler }
|
||||
}
|
||||
|
||||
abstract private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition {
|
||||
ResponseExpr r;
|
||||
ResponseNode r;
|
||||
|
||||
HeaderDefinition() { astNode.getReceiver() = r }
|
||||
HeaderDefinition() { this.getReceiver() = r }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = r.getRouteHandler() }
|
||||
}
|
||||
@@ -268,7 +312,7 @@ module NodeJSLib {
|
||||
* A call to the `setHeader` method of an HTTP response.
|
||||
*/
|
||||
private class SetHeader extends HeaderDefinition {
|
||||
SetHeader() { astNode.getMethodName() = "setHeader" }
|
||||
SetHeader() { this.getMethodName() = "setHeader" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,15 +320,15 @@ module NodeJSLib {
|
||||
*/
|
||||
private class WriteHead extends HeaderDefinition {
|
||||
WriteHead() {
|
||||
astNode.getMethodName() = "writeHead" and
|
||||
astNode.getNumArgument() >= 1
|
||||
this.getMethodName() = "writeHead" and
|
||||
this.getNumArgument() >= 1
|
||||
}
|
||||
|
||||
override predicate definesExplicitly(string headerName, Expr headerValue) {
|
||||
astNode.getNumArgument() > 1 and
|
||||
override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) {
|
||||
this.getNumArgument() > 1 and
|
||||
exists(DataFlow::SourceNode headers, string header |
|
||||
headers.flowsToExpr(astNode.getLastArgument()) and
|
||||
headers.hasPropertyWrite(header, DataFlow::valueNode(headerValue)) and
|
||||
headers.flowsTo(this.getLastArgument()) and
|
||||
headers.hasPropertyWrite(header, headerValue) and
|
||||
headerName = header.toLowerCase()
|
||||
)
|
||||
}
|
||||
@@ -363,9 +407,9 @@ module NodeJSLib {
|
||||
HTTP::RouteHandler rh;
|
||||
|
||||
ResponseSendArgument() {
|
||||
exists(MethodCallExpr mce, string m | m = "write" or m = "end" |
|
||||
mce.calls(any(ResponseExpr e | e.getRouteHandler() = rh), m) and
|
||||
this = mce.getArgument(0) and
|
||||
exists(DataFlow::MethodCallNode mcn, string m | m = "write" or m = "end" |
|
||||
mcn.calls(any(ResponseNode e | e.getRouteHandler() = rh), m) and
|
||||
this = mcn.getArgument(0) and
|
||||
// don't mistake callback functions as data
|
||||
not this.analyze().getAValue() instanceof AbstractFunction
|
||||
)
|
||||
@@ -382,11 +426,10 @@ module NodeJSLib {
|
||||
}
|
||||
|
||||
/** An expression that is passed as `http.request({ auth: <expr> }, ...)`. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
Credentials() {
|
||||
exists(string http | http = "http" or http = "https" |
|
||||
this =
|
||||
DataFlow::moduleMember(http, "request").getACall().getOptionArgument(0, "auth").asExpr()
|
||||
this = DataFlow::moduleMember(http, "request").getACall().getOptionArgument(0, "auth")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -994,11 +1037,9 @@ module NodeJSLib {
|
||||
/**
|
||||
* A data flow node that is the username passed to the login callback provided by an HTTP or HTTPS request made by a Node.js process, for example `username` in `http.request(url).on('login', (res, cb) => {cb(username, password)})`.
|
||||
*/
|
||||
private class ClientRequestLoginUsername extends CredentialsExpr {
|
||||
private class ClientRequestLoginUsername extends CredentialsNode {
|
||||
ClientRequestLoginUsername() {
|
||||
exists(ClientRequestLoginCallback callback |
|
||||
this = callback.getACall().getArgument(0).asExpr()
|
||||
)
|
||||
exists(ClientRequestLoginCallback callback | this = callback.getACall().getArgument(0))
|
||||
}
|
||||
|
||||
override string getCredentialsKind() { result = "Node.js http(s) client login username" }
|
||||
@@ -1007,11 +1048,9 @@ module NodeJSLib {
|
||||
/**
|
||||
* A data flow node that is the password passed to the login callback provided by an HTTP or HTTPS request made by a Node.js process, for example `password` in `http.request(url).on('login', (res, cb) => {cb(username, password)})`.
|
||||
*/
|
||||
private class ClientRequestLoginPassword extends CredentialsExpr {
|
||||
private class ClientRequestLoginPassword extends CredentialsNode {
|
||||
ClientRequestLoginPassword() {
|
||||
exists(ClientRequestLoginCallback callback |
|
||||
this = callback.getACall().getArgument(1).asExpr()
|
||||
)
|
||||
exists(ClientRequestLoginCallback callback | this = callback.getACall().getArgument(1))
|
||||
}
|
||||
|
||||
override string getCredentialsKind() { result = "Node.js http(s) client login password" }
|
||||
|
||||
@@ -31,13 +31,13 @@ module PkgCloud {
|
||||
/**
|
||||
* An expression that is used for authentication through pkgcloud.
|
||||
*/
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string propertyName, DataFlow::InvokeNode invk, int i |
|
||||
takesConfigurationObject(invk, i) and
|
||||
this = invk.getOptionArgument(0, propertyName).asExpr()
|
||||
this = invk.getOptionArgument(0, propertyName)
|
||||
|
|
||||
/*
|
||||
* Catch-all support for the following providers:
|
||||
|
||||
@@ -6,7 +6,7 @@ import javascript
|
||||
|
||||
module Request {
|
||||
/** A credentials expression that is used for authentication. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
@@ -20,9 +20,9 @@ module Request {
|
||||
action = mod.getAMemberCall(any(HTTP::RequestMethodName n).toLowerCase())
|
||||
)
|
||||
|
|
||||
exists(MethodCallExpr auth, int argIndex |
|
||||
exists(DataFlow::MethodCallNode auth, int argIndex |
|
||||
// request.get(url).auth('username', 'password', _, 'token');
|
||||
auth = action.getAMemberCall("auth").asExpr() and
|
||||
auth = action.getAMemberCall("auth") and
|
||||
this = auth.getArgument(argIndex)
|
||||
|
|
||||
argIndex = 0 and kind = "user name"
|
||||
@@ -35,7 +35,7 @@ module Request {
|
||||
exists(DataFlow::ObjectLiteralNode auth, string propertyName |
|
||||
// request.get(url, { auth: {user: 'username', pass: 'password', bearer: 'token'}})
|
||||
auth.flowsTo(action.getOptionArgument(1, "auth")) and
|
||||
auth.hasPropertyWrite(propertyName, DataFlow::valueNode(this))
|
||||
auth.hasPropertyWrite(propertyName, this)
|
||||
|
|
||||
(propertyName = "user" or propertyName = "username") and
|
||||
kind = "user name"
|
||||
|
||||
@@ -9,10 +9,10 @@ module Restify {
|
||||
/**
|
||||
* An expression that creates a new Restify server.
|
||||
*/
|
||||
class ServerDefinition extends HTTP::Servers::StandardServerDefinition, CallExpr {
|
||||
class ServerDefinition extends HTTP::Servers::StandardServerDefinition, DataFlow::CallNode {
|
||||
ServerDefinition() {
|
||||
// `server = restify.createServer()`
|
||||
this = DataFlow::moduleMember("restify", "createServer").getACall().asExpr()
|
||||
this = DataFlow::moduleMember("restify", "createServer").getACall()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,38 +69,54 @@ module Restify {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ResponseNode` instead.
|
||||
* A Node.js HTTP response provided by Restify.
|
||||
*/
|
||||
class ResponseExpr extends NodeJSLib::ResponseExpr {
|
||||
deprecated class ResponseExpr extends NodeJSLib::ResponseExpr {
|
||||
ResponseExpr() { src instanceof ResponseSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Node.js HTTP response provided by Restify.
|
||||
*/
|
||||
class ResponseNode extends NodeJSLib::ResponseNode {
|
||||
ResponseNode() { src instanceof ResponseSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RequestNode` instead.
|
||||
* A Node.js HTTP request provided by Restify.
|
||||
*/
|
||||
deprecated class RequestExpr extends NodeJSLib::RequestExpr {
|
||||
RequestExpr() { src instanceof RequestSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Node.js HTTP request provided by Restify.
|
||||
*/
|
||||
class RequestExpr extends NodeJSLib::RequestExpr {
|
||||
RequestExpr() { src instanceof RequestSource }
|
||||
class RequestNode extends NodeJSLib::RequestNode {
|
||||
RequestNode() { src instanceof RequestSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a user-controlled Restify request input.
|
||||
*/
|
||||
private class RequestInputAccess extends HTTP::RequestInputAccess {
|
||||
RequestExpr request;
|
||||
RequestNode request;
|
||||
string kind;
|
||||
|
||||
RequestInputAccess() {
|
||||
exists(MethodCallExpr query |
|
||||
exists(DataFlow::MethodCallNode query |
|
||||
// `request.getQuery().<name>`
|
||||
kind = "parameter" and
|
||||
query.calls(request, "getQuery") and
|
||||
this.asExpr().(PropAccess).accesses(query, _)
|
||||
this.(DataFlow::PropRead).accesses(query, _)
|
||||
)
|
||||
or
|
||||
exists(string methodName |
|
||||
// `request.href()` or `request.getPath()`
|
||||
kind = "url" and
|
||||
this.asExpr().(MethodCallExpr).calls(request, methodName)
|
||||
this.(DataFlow::MethodCallNode).calls(request, methodName)
|
||||
|
|
||||
methodName = "href" or
|
||||
methodName = "getPath"
|
||||
@@ -108,13 +124,12 @@ module Restify {
|
||||
or
|
||||
// `request.getContentType()`, `request.userAgent()`, `request.trailer(...)`, `request.header(...)`
|
||||
kind = "header" and
|
||||
this.asExpr()
|
||||
.(MethodCallExpr)
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.calls(request, ["getContentType", "userAgent", "trailer", "header"])
|
||||
or
|
||||
// `req.cookies
|
||||
kind = "cookie" and
|
||||
this.asExpr().(PropAccess).accesses(request, "cookies")
|
||||
this.(DataFlow::PropRead).accesses(request, "cookies")
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = request.getRouteHandler() }
|
||||
@@ -128,28 +143,27 @@ module Restify {
|
||||
private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition {
|
||||
HeaderDefinition() {
|
||||
// response.header('Cache-Control', 'no-cache')
|
||||
astNode.getReceiver() instanceof ResponseExpr and
|
||||
astNode.getMethodName() = "header"
|
||||
this.getReceiver() instanceof ResponseNode and
|
||||
this.getMethodName() = "header"
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { astNode.getReceiver() = result.getAResponseExpr() }
|
||||
override RouteHandler getRouteHandler() { this.getReceiver() = result.getAResponseNode() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a Restify method that sets up a route.
|
||||
*/
|
||||
class RouteSetup extends MethodCallExpr, HTTP::Servers::StandardRouteSetup {
|
||||
class RouteSetup extends DataFlow::MethodCallNode, HTTP::Servers::StandardRouteSetup {
|
||||
ServerDefinition server;
|
||||
|
||||
RouteSetup() {
|
||||
// server.get('/', fun)
|
||||
// server.head('/', fun)
|
||||
server.flowsTo(getReceiver()) and
|
||||
getMethodName() = any(HTTP::RequestMethodName m).toLowerCase()
|
||||
server.ref().getAMethodCall(any(HTTP::RequestMethodName m).toLowerCase()) = this
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getARouteHandler() { result.flowsToExpr(getArgument(1)) }
|
||||
override DataFlow::SourceNode getARouteHandler() { result.flowsTo(this.getArgument(1)) }
|
||||
|
||||
override Expr getServer() { result = server }
|
||||
override DataFlow::Node getServer() { result = server }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,26 +5,26 @@
|
||||
import javascript
|
||||
|
||||
module SQL {
|
||||
/** A string-valued expression that is interpreted as a SQL command. */
|
||||
abstract class SqlString extends Expr { }
|
||||
/** A string-valued dataflow node that is interpreted as a SQL command. */
|
||||
abstract class SqlString extends DataFlow::Node { }
|
||||
|
||||
private class SqlStringFromModel extends SqlString {
|
||||
SqlStringFromModel() { this = ModelOutput::getASinkNode("sql-injection").asSink().asExpr() }
|
||||
SqlStringFromModel() { this = ModelOutput::getASinkNode("sql-injection").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that sanitizes a string to make it safe to embed into
|
||||
* An dataflow node that sanitizes a string to make it safe to embed into
|
||||
* a SQL command.
|
||||
*/
|
||||
abstract class SqlSanitizer extends Expr {
|
||||
Expr input;
|
||||
Expr output;
|
||||
abstract class SqlSanitizer extends DataFlow::Node {
|
||||
DataFlow::Node input;
|
||||
DataFlow::Node output;
|
||||
|
||||
/** Gets the input expression being sanitized. */
|
||||
Expr getInput() { result = input }
|
||||
DataFlow::Node getInput() { result = input }
|
||||
|
||||
/** Gets the output expression containing the sanitized value. */
|
||||
Expr getOutput() { result = output }
|
||||
DataFlow::Node getOutput() { result = output }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,26 +90,26 @@ private module MySql {
|
||||
|
||||
/** An expression that is passed to the `query` method and hence interpreted as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
QueryString() { this = any(QueryCall qc).getAQueryArgument() }
|
||||
}
|
||||
|
||||
/** A call to the `escape` or `escapeId` method that performs SQL sanitization. */
|
||||
class EscapingSanitizer extends SQL::SqlSanitizer, MethodCallExpr {
|
||||
class EscapingSanitizer extends SQL::SqlSanitizer instanceof API::CallNode {
|
||||
EscapingSanitizer() {
|
||||
this = [mysql(), pool(), connection()].getMember(["escape", "escapeId"]).getACall().asExpr() and
|
||||
this = [mysql(), pool(), connection()].getMember(["escape", "escapeId"]).getACall() and
|
||||
input = this.getArgument(0) and
|
||||
output = this
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that is passed as user name or password to `mysql.createConnection`. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(API::Node callee, string prop |
|
||||
callee in [createConnection(), createPool()] and
|
||||
this = callee.getParameter(0).getMember(prop).asSink().asExpr() and
|
||||
this = callee.getParameter(0).getMember(prop).asSink() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
@@ -198,21 +198,21 @@ private module Postgres {
|
||||
/** An expression that is passed to the `query` method and hence interpreted as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
this = any(QueryCall qc).getAQueryArgument().asExpr()
|
||||
this = any(QueryCall qc).getAQueryArgument()
|
||||
or
|
||||
this = API::moduleImport("pg-cursor").getParameter(0).asSink().asExpr()
|
||||
this = API::moduleImport("pg-cursor").getParameter(0).asSink()
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that is passed as user name or password when creating a client or a pool. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string prop |
|
||||
this = [newClient(), newPool()].getParameter(0).getMember(prop).asSink().asExpr()
|
||||
this = [newClient(), newPool()].getParameter(0).getMember(prop).asSink()
|
||||
or
|
||||
this = pgPromise().getParameter(0).getMember(prop).asSink().asExpr()
|
||||
this = pgPromise().getParameter(0).getMember(prop).asSink()
|
||||
|
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
@@ -349,7 +349,7 @@ private module Postgres {
|
||||
|
||||
/** An expression that is interpreted as SQL by `pg-promise`. */
|
||||
class PgPromiseQueryString extends SQL::SqlString {
|
||||
PgPromiseQueryString() { this = any(PgPromiseQueryCall qc).getAQueryArgument().asExpr() }
|
||||
PgPromiseQueryString() { this = any(PgPromiseQueryCall qc).getAQueryArgument() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ private module Sqlite {
|
||||
|
||||
/** An expression that is passed to the `query` method and hence interpreted as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
QueryString() { this = any(QueryCall qc).getAQueryArgument() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ private module MsSql {
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
exists(DatabaseAccess dba | dba instanceof QueryTemplateExpr or dba instanceof QueryCall |
|
||||
this = dba.getAQueryArgument().asExpr()
|
||||
this = dba.getAQueryArgument()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -478,14 +478,14 @@ private module MsSql {
|
||||
/** An element of a query template, which is automatically sanitized. */
|
||||
class QueryTemplateSanitizer extends SQL::SqlSanitizer {
|
||||
QueryTemplateSanitizer() {
|
||||
this = any(QueryTemplateExpr qte).getAQueryArgument().asExpr() and
|
||||
this = any(QueryTemplateExpr qte).getAQueryArgument() and
|
||||
input = this and
|
||||
output = this
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that is passed as user name or password when creating a client or a pool. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
class Credentials extends CredentialsNode {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
@@ -495,7 +495,7 @@ private module MsSql {
|
||||
or
|
||||
callee = mssql().getMember("ConnectionPool")
|
||||
) and
|
||||
this = callee.getParameter(0).getMember(prop).asSink().asExpr() and
|
||||
this = callee.getParameter(0).getMember(prop).asSink() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
|
||||
@@ -29,8 +29,8 @@ private class PromotedExpressCandidate extends Express::RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler {
|
||||
PromotedExpressCandidate() { this instanceof ConnectExpressShared::RouteHandlerCandidate }
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
result = ConnectExpressShared::getRouteHandlerParameter(getAstNode(), kind)
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
result = ConnectExpressShared::getRouteHandlerParameter(this, kind)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ private class PromotedConnectCandidate extends Connect::RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler {
|
||||
PromotedConnectCandidate() { this instanceof ConnectExpressShared::RouteHandlerCandidate }
|
||||
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
result = ConnectExpressShared::getRouteHandlerParameter(getAstNode(), kind)
|
||||
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
||||
result = ConnectExpressShared::getRouteHandlerParameter(this, kind)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,25 @@ import javascript
|
||||
import semmle.javascript.security.internal.SensitiveDataHeuristics
|
||||
private import HeuristicNames
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SensitiveNode` instead.
|
||||
* An expression that might contain sensitive data.
|
||||
*/
|
||||
deprecated class SensitiveExpr extends Expr {
|
||||
SensitiveNode node;
|
||||
|
||||
SensitiveExpr() { node.asExpr() = this }
|
||||
|
||||
/** Gets a human-readable description of this expression for use in alert messages. */
|
||||
deprecated string describe() { result = node.describe() }
|
||||
|
||||
/** Gets a classification of the kind of sensitive data this expression might contain. */
|
||||
deprecated SensitiveDataClassification getClassification() { result = node.getClassification() }
|
||||
}
|
||||
|
||||
/** An expression that might contain sensitive data. */
|
||||
cached
|
||||
abstract class SensitiveExpr extends Expr {
|
||||
abstract class SensitiveNode extends DataFlow::Node {
|
||||
/** Gets a human-readable description of this expression for use in alert messages. */
|
||||
cached
|
||||
abstract string describe();
|
||||
@@ -26,33 +42,33 @@ abstract class SensitiveExpr extends Expr {
|
||||
}
|
||||
|
||||
/** A function call that might produce sensitive data. */
|
||||
class SensitiveCall extends SensitiveExpr, InvokeExpr {
|
||||
class SensitiveCall extends SensitiveNode instanceof DataFlow::InvokeNode {
|
||||
SensitiveDataClassification classification;
|
||||
|
||||
SensitiveCall() {
|
||||
classification = this.getCalleeName().(SensitiveDataFunctionName).getClassification()
|
||||
classification = super.getCalleeName().(SensitiveDataFunctionName).getClassification()
|
||||
or
|
||||
// This is particularly to pick up methods with an argument like "password", which
|
||||
// may indicate a lookup.
|
||||
exists(string s | this.getAnArgument().mayHaveStringValue(s) |
|
||||
exists(string s | super.getAnArgument().mayHaveStringValue(s) |
|
||||
nameIndicatesSensitiveData(s, classification)
|
||||
)
|
||||
}
|
||||
|
||||
override string describe() { result = "a call to " + this.getCalleeName() }
|
||||
override string describe() { result = "a call to " + super.getCalleeName() }
|
||||
|
||||
override SensitiveDataClassification getClassification() { result = classification }
|
||||
}
|
||||
|
||||
/** An access to a variable or property that might contain sensitive data. */
|
||||
abstract class SensitiveVariableAccess extends SensitiveExpr {
|
||||
abstract class SensitiveVariableAccess extends SensitiveNode {
|
||||
string name;
|
||||
|
||||
SensitiveVariableAccess() {
|
||||
this.(VarAccess).getName() = name
|
||||
this.asExpr().(VarAccess).getName() = name
|
||||
or
|
||||
exists(DataFlow::PropRead pr |
|
||||
this = pr.asExpr() and
|
||||
this = pr and
|
||||
pr.getPropertyName() = name
|
||||
)
|
||||
}
|
||||
@@ -173,10 +189,8 @@ class ProtectCall extends DataFlow::CallNode {
|
||||
}
|
||||
|
||||
/** An expression that might contain a clear-text password. */
|
||||
class CleartextPasswordExpr extends SensitiveExpr {
|
||||
CleartextPasswordExpr() {
|
||||
this.(SensitiveExpr).getClassification() = SensitiveDataClassification::password()
|
||||
}
|
||||
class CleartextPasswordExpr extends SensitiveNode {
|
||||
CleartextPasswordExpr() { this.getClassification() = SensitiveDataClassification::password() }
|
||||
|
||||
override string describe() { none() }
|
||||
|
||||
|
||||
@@ -30,10 +30,8 @@ module BrokenCryptoAlgorithm {
|
||||
* A sensitive expression, viewed as a data flow source for sensitive information
|
||||
* in broken or weak cryptographic algorithms.
|
||||
*/
|
||||
class SensitiveExprSource extends Source, DataFlow::ValueNode {
|
||||
override SensitiveExpr astNode;
|
||||
|
||||
override string describe() { result = astNode.describe() }
|
||||
class SensitiveExprSource extends Source instanceof SensitiveNode {
|
||||
override string describe() { result = SensitiveNode.super.describe() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +41,7 @@ module BrokenCryptoAlgorithm {
|
||||
WeakCryptographicOperationSink() {
|
||||
exists(CryptographicOperation application |
|
||||
application.getAlgorithm().isWeak() and
|
||||
this.asExpr() = application.getInput()
|
||||
this = application.getInput()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +30,13 @@ module CleartextStorage {
|
||||
* A sensitive expression, viewed as a data flow source for cleartext storage
|
||||
* of sensitive information.
|
||||
*/
|
||||
class SensitiveExprSource extends Source, DataFlow::ValueNode {
|
||||
override SensitiveExpr astNode;
|
||||
|
||||
class SensitiveExprSource extends Source instanceof SensitiveNode {
|
||||
SensitiveExprSource() {
|
||||
// storing user names or account names in plaintext isn't usually a problem
|
||||
astNode.getClassification() != SensitiveDataClassification::id()
|
||||
super.getClassification() != SensitiveDataClassification::id()
|
||||
}
|
||||
|
||||
override string describe() { result = astNode.describe() }
|
||||
override string describe() { result = SensitiveNode.super.describe() }
|
||||
}
|
||||
|
||||
/** A call to any function whose name suggests that it encodes or encrypts its arguments. */
|
||||
@@ -52,8 +50,8 @@ module CleartextStorage {
|
||||
class CookieStorageSink extends Sink {
|
||||
CookieStorageSink() {
|
||||
exists(HTTP::CookieDefinition cookieDef |
|
||||
this.asExpr() = cookieDef.getValueArgument() or
|
||||
this.asExpr() = cookieDef.getHeaderArgument()
|
||||
this = cookieDef.getValueArgument() or
|
||||
this = cookieDef.getHeaderArgument()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -61,16 +59,12 @@ module CleartextStorage {
|
||||
/**
|
||||
* An expression set as a value of localStorage or sessionStorage.
|
||||
*/
|
||||
class WebStorageSink extends Sink {
|
||||
WebStorageSink() { this.asExpr() instanceof WebStorageWrite }
|
||||
}
|
||||
class WebStorageSink extends Sink instanceof WebStorageWrite { }
|
||||
|
||||
/**
|
||||
* An expression stored by AngularJS.
|
||||
*/
|
||||
class AngularJSStorageSink extends Sink {
|
||||
AngularJSStorageSink() {
|
||||
any(AngularJS::AngularJSCall call).storesArgumentGlobally(this.asExpr())
|
||||
}
|
||||
AngularJSStorageSink() { any(AngularJS::AngularJSCallNode call).storesArgumentGlobally(this) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,23 +57,23 @@ module ClientSideUrlRedirect {
|
||||
* when `base` is the current URL.
|
||||
*/
|
||||
predicate untrustedUrlSubstring(DataFlow::Node base, DataFlow::Node substring) {
|
||||
exists(MethodCallExpr mce, string methodName |
|
||||
mce = substring.asExpr() and mce.calls(base.asExpr(), methodName)
|
||||
exists(DataFlow::MethodCallNode mcn, string methodName |
|
||||
mcn = substring and mcn.calls(base, methodName)
|
||||
|
|
||||
methodName = "split" and
|
||||
// exclude all splits where only the prefix is accessed, which is safe for url-redirects.
|
||||
not exists(PropAccess pacc | mce = pacc.getBase() | pacc.getPropertyName() = "0")
|
||||
not exists(DataFlow::PropRead pacc | mcn = pacc.getBase() | pacc.getPropertyName() = "0")
|
||||
or
|
||||
methodName = StringOps::substringMethodName() and
|
||||
// exclude `location.href.substring(0, ...)` and similar, which can
|
||||
// never refer to the query string
|
||||
not mce.getArgument(0).(NumberLiteral).getIntValue() = 0
|
||||
not mcn.getArgument(0).getIntValue() = 0
|
||||
)
|
||||
or
|
||||
exists(MethodCallExpr mce |
|
||||
substring.asExpr() = mce and
|
||||
mce = any(DataFlow::RegExpCreationNode re).getAMethodCall("exec").asExpr() and
|
||||
base.asExpr() = mce.getArgument(0)
|
||||
exists(DataFlow::MethodCallNode mcn |
|
||||
substring = mcn and
|
||||
mcn = any(DataFlow::RegExpCreationNode re).getAMethodCall("exec") and
|
||||
base = mcn.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -104,7 +104,9 @@ module ClientSideUrlRedirect {
|
||||
xss = true
|
||||
or
|
||||
// An assignment to `location`
|
||||
exists(Assignment assgn | isLocation(assgn.getTarget()) and astNode = assgn.getRhs()) and
|
||||
exists(Assignment assgn |
|
||||
isLocationNode(assgn.getTarget().flow()) and astNode = assgn.getRhs()
|
||||
) and
|
||||
xss = true
|
||||
or
|
||||
// An assignment to `location.href`, `location.protocol` or `location.hostname`
|
||||
@@ -119,7 +121,7 @@ module ClientSideUrlRedirect {
|
||||
// A redirection using the AngularJS `$location` service
|
||||
exists(AngularJS::ServiceReference service |
|
||||
service.getName() = "$location" and
|
||||
this.asExpr() = service.getAMethodCall("url").getArgument(0)
|
||||
this = service.getAMethodCall("url").getArgument(0)
|
||||
) and
|
||||
xss = false
|
||||
}
|
||||
@@ -177,7 +179,7 @@ module ClientSideUrlRedirect {
|
||||
)
|
||||
or
|
||||
// e.g. node.setAttribute("href", sink)
|
||||
any(DomMethodCallExpr call).interpretsArgumentsAsUrl(this.asExpr())
|
||||
any(DomMethodCallNode call).interpretsArgumentsAsUrl(this)
|
||||
}
|
||||
|
||||
override predicate isXssSink() { any() }
|
||||
@@ -189,9 +191,9 @@ module ClientSideUrlRedirect {
|
||||
*/
|
||||
class AttributeWriteUrlSink extends ScriptUrlSink, DataFlow::ValueNode {
|
||||
AttributeWriteUrlSink() {
|
||||
exists(DomPropWriteNode pw |
|
||||
exists(DomPropertyWrite pw |
|
||||
pw.interpretsValueAsJavaScriptUrl() and
|
||||
this = DataFlow::valueNode(pw.getRhs())
|
||||
this = pw.getRhs()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ module CodeInjection {
|
||||
*/
|
||||
class AngularJSExpressionSink extends Sink, DataFlow::ValueNode {
|
||||
AngularJSExpressionSink() {
|
||||
any(AngularJS::AngularJSCall call).interpretsArgumentAsCode(this.asExpr())
|
||||
any(AngularJS::AngularJSCallNode call).interpretsArgumentAsCode(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,12 +46,12 @@ module CorsMisconfigurationForCredentials {
|
||||
CorsOriginHeaderWithAssociatedCredentialHeader() {
|
||||
exists(
|
||||
HTTP::RouteHandler routeHandler, HTTP::ExplicitHeaderDefinition origin,
|
||||
Expr credentialsValue
|
||||
DataFlow::Node credentialsValue
|
||||
|
|
||||
routeHandler.getAResponseHeader(_) = origin and
|
||||
routeHandler.getAResponseHeader(_) = credentials and
|
||||
origin.definesExplicitly("access-control-allow-origin", this.asExpr()) and
|
||||
credentials.definesExplicitly("access-control-allow-credentials", credentialsValue)
|
||||
origin.definesHeaderValue("access-control-allow-origin", this) and
|
||||
credentials.definesHeaderValue("access-control-allow-credentials", credentialsValue)
|
||||
|
|
||||
credentialsValue.mayHaveBooleanValue(true) or
|
||||
credentialsValue.mayHaveStringValue("true")
|
||||
|
||||
@@ -21,14 +21,28 @@ class DomGlobalVariable extends GlobalVariable {
|
||||
/** DEPRECATED: Alias for DomGlobalVariable */
|
||||
deprecated class DOMGlobalVariable = DomGlobalVariable;
|
||||
|
||||
/** Holds if `e` could hold a value that comes from the DOM. */
|
||||
predicate isDomValue(Expr e) { DOM::domValueRef().flowsToExpr(e) }
|
||||
/**
|
||||
* DEPRECATED: Use `isDomNode` instead.
|
||||
* Holds if `e` could hold a value that comes from the DOM.
|
||||
*/
|
||||
deprecated predicate isDomValue(Expr e) { isDomNode(e.flow()) }
|
||||
|
||||
/**
|
||||
* Holds if `e` could hold a value that comes from the DOM.
|
||||
*/
|
||||
predicate isDomNode(DataFlow::Node e) { DOM::domValueRef().flowsTo(e) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isLocationNode` instead.
|
||||
* Holds if `e` could refer to the `location` property of a DOM node.
|
||||
*/
|
||||
deprecated predicate isLocation(Expr e) { isLocationNode(e.flow()) }
|
||||
|
||||
/** Holds if `e` could refer to the `location` property of a DOM node. */
|
||||
predicate isLocation(Expr e) {
|
||||
e = DOM::domValueRef().getAPropertyReference("location").asExpr()
|
||||
predicate isLocationNode(DataFlow::Node e) {
|
||||
e = DOM::domValueRef().getAPropertyReference("location")
|
||||
or
|
||||
e.accessesGlobal("location")
|
||||
e = DataFlow::globalVarRef("location")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,15 +67,52 @@ deprecated predicate isDocumentUrl(Expr e) { e.flow() = DOM::locationSource() }
|
||||
deprecated predicate isDocumentURL = isDocumentUrl/1;
|
||||
|
||||
/**
|
||||
* DEPRECATED. In most cases, a sanitizer based on this predicate can be removed, as
|
||||
* taint tracking no longer step through the properties of the location object by default.
|
||||
*
|
||||
* Holds if `pacc` accesses a part of `document.location` that is
|
||||
* not considered user-controlled, that is, anything except
|
||||
* `href`, `hash` and `search`.
|
||||
*/
|
||||
deprecated predicate isSafeLocationProperty(PropAccess pacc) {
|
||||
exists(string prop | pacc = DOM::locationRef().getAPropertyRead(prop).asExpr() |
|
||||
prop != "href" and prop != "hash" and prop != "search"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `DomMethodCallNode` instead.
|
||||
* A call to a DOM method.
|
||||
*/
|
||||
class DomMethodCallExpr extends MethodCallExpr {
|
||||
DomMethodCallExpr() { isDomValue(this.getReceiver()) }
|
||||
deprecated class DomMethodCallExpr extends MethodCallExpr {
|
||||
DomMethodCallNode node;
|
||||
|
||||
DomMethodCallExpr() { this.flow() = node }
|
||||
|
||||
/** Holds if `arg` is an argument that is interpreted as HTML. */
|
||||
deprecated predicate interpretsArgumentsAsHtml(Expr arg) {
|
||||
node.interpretsArgumentsAsHtml(arg.flow())
|
||||
}
|
||||
|
||||
/** Holds if `arg` is an argument that is used as an URL. */
|
||||
deprecated predicate interpretsArgumentsAsURL(Expr arg) {
|
||||
node.interpretsArgumentsAsURL(arg.flow())
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for interpretsArgumentsAsHtml */
|
||||
deprecated predicate interpretsArgumentsAsHTML(Expr arg) { this.interpretsArgumentsAsHtml(arg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a DOM method.
|
||||
*/
|
||||
class DomMethodCallNode extends DataFlow::MethodCallNode {
|
||||
DomMethodCallNode() { isDomNode(this.getReceiver()) }
|
||||
|
||||
/**
|
||||
* Holds if `arg` is an argument that is interpreted as HTML.
|
||||
*/
|
||||
predicate interpretsArgumentsAsHtml(Expr arg) {
|
||||
predicate interpretsArgumentsAsHtml(DataFlow::Node arg) {
|
||||
exists(int argPos, string name |
|
||||
arg = this.getArgument(argPos) and
|
||||
name = this.getMethodName()
|
||||
@@ -86,7 +137,7 @@ class DomMethodCallExpr extends MethodCallExpr {
|
||||
/**
|
||||
* Holds if `arg` is an argument that is used as an URL.
|
||||
*/
|
||||
predicate interpretsArgumentsAsUrl(Expr arg) {
|
||||
predicate interpretsArgumentsAsUrl(DataFlow::Node arg) {
|
||||
exists(int argPos, string name |
|
||||
arg = this.getArgument(argPos) and
|
||||
name = this.getMethodName()
|
||||
@@ -104,56 +155,84 @@ class DomMethodCallExpr extends MethodCallExpr {
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for interpretsArgumentsAsUrl */
|
||||
deprecated predicate interpretsArgumentsAsURL(Expr arg) { this.interpretsArgumentsAsUrl(arg) }
|
||||
deprecated predicate interpretsArgumentsAsURL(DataFlow::Node arg) {
|
||||
this.interpretsArgumentsAsUrl(arg)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for interpretsArgumentsAsHtml */
|
||||
deprecated predicate interpretsArgumentsAsHTML(Expr arg) { this.interpretsArgumentsAsHtml(arg) }
|
||||
deprecated predicate interpretsArgumentsAsHTML(DataFlow::Node arg) {
|
||||
this.interpretsArgumentsAsHtml(arg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `DomPropertyWrite` instead.
|
||||
* An assignment to a property of a DOM object.
|
||||
*/
|
||||
class DomPropWriteNode extends Assignment {
|
||||
PropAccess lhs;
|
||||
deprecated class DomPropWriteNode extends Assignment {
|
||||
DomPropertyWrite node;
|
||||
|
||||
DomPropWriteNode() {
|
||||
lhs = this.getLhs() and
|
||||
isDomValue(lhs.getBase())
|
||||
}
|
||||
DomPropWriteNode() { this.flow() = node }
|
||||
|
||||
/**
|
||||
* Holds if the assigned value is interpreted as HTML.
|
||||
*/
|
||||
predicate interpretsValueAsHtml() {
|
||||
lhs.getPropertyName() = "innerHTML" or
|
||||
lhs.getPropertyName() = "outerHTML"
|
||||
}
|
||||
predicate interpretsValueAsHtml() { node.interpretsValueAsHtml() }
|
||||
|
||||
/** DEPRECATED: Alias for interpretsValueAsHtml */
|
||||
deprecated predicate interpretsValueAsHTML() { this.interpretsValueAsHtml() }
|
||||
|
||||
/**
|
||||
* Holds if the assigned value is interpreted as JavaScript via javascript: protocol.
|
||||
*/
|
||||
predicate interpretsValueAsJavaScriptUrl() { node.interpretsValueAsJavaScriptUrl() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignment to a property of a DOM object.
|
||||
*/
|
||||
class DomPropertyWrite extends DataFlow::Node instanceof DataFlow::PropWrite {
|
||||
DomPropertyWrite() { isDomNode(super.getBase()) }
|
||||
|
||||
/**
|
||||
* Holds if the assigned value is interpreted as HTML.
|
||||
*/
|
||||
predicate interpretsValueAsHtml() {
|
||||
super.getPropertyName() = "innerHTML" or
|
||||
super.getPropertyName() = "outerHTML"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the assigned value is interpreted as JavaScript via javascript: protocol.
|
||||
*/
|
||||
predicate interpretsValueAsJavaScriptUrl() {
|
||||
lhs.getPropertyName() = DOM::getAPropertyNameInterpretedAsJavaScriptUrl()
|
||||
super.getPropertyName() = DOM::getAPropertyNameInterpretedAsJavaScriptUrl()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data flow node corresponding to the value being written.
|
||||
*/
|
||||
DataFlow::Node getRhs() {
|
||||
result = super.getRhs()
|
||||
or
|
||||
result = super.getWriteNode().(AssignAddExpr).getRhs().flow()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A value written to web storage, like `localStorage` or `sessionStorage`.
|
||||
*/
|
||||
class WebStorageWrite extends Expr {
|
||||
class WebStorageWrite extends DataFlow::Node {
|
||||
WebStorageWrite() {
|
||||
exists(DataFlow::SourceNode webStorage |
|
||||
webStorage = DataFlow::globalVarRef("localStorage") or
|
||||
webStorage = DataFlow::globalVarRef("sessionStorage")
|
||||
|
|
||||
// an assignment to `window.localStorage[someProp]`
|
||||
this = webStorage.getAPropertyWrite().getRhs().asExpr()
|
||||
this = webStorage.getAPropertyWrite().getRhs()
|
||||
or
|
||||
// an invocation of `window.localStorage.setItem`
|
||||
this = webStorage.getAMethodCall("setItem").getArgument(1).asExpr()
|
||||
this = webStorage.getAMethodCall("setItem").getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ module DomBasedXss {
|
||||
)
|
||||
or
|
||||
// call to an Angular method that interprets its argument as HTML
|
||||
any(AngularJS::AngularJSCall call).interpretsArgumentAsHtml(this.asExpr())
|
||||
any(AngularJS::AngularJSCallNode call).interpretsArgumentAsHtml(this)
|
||||
or
|
||||
// call to a WinJS function that interprets its argument as HTML
|
||||
exists(DataFlow::MethodCallNode mcn, string m |
|
||||
@@ -130,12 +130,12 @@ module DomBasedXss {
|
||||
class DomSink extends Sink {
|
||||
DomSink() {
|
||||
// Call to a DOM function that inserts its argument into the DOM
|
||||
any(DomMethodCallExpr call).interpretsArgumentsAsHtml(this.asExpr())
|
||||
any(DomMethodCallNode call).interpretsArgumentsAsHtml(this)
|
||||
or
|
||||
// Assignment to a dangerous DOM property
|
||||
exists(DomPropWriteNode pw |
|
||||
exists(DomPropertyWrite pw |
|
||||
pw.interpretsValueAsHtml() and
|
||||
this = DataFlow::valueNode(pw.getRhs())
|
||||
this = pw.getRhs()
|
||||
)
|
||||
or
|
||||
// `html` or `source.html` properties of React Native `WebView`
|
||||
@@ -159,7 +159,7 @@ module DomBasedXss {
|
||||
)
|
||||
or
|
||||
exists(DataFlow::MethodCallNode ccf |
|
||||
isDomValue(ccf.getReceiver().asExpr()) and
|
||||
isDomNode(ccf.getReceiver()) and
|
||||
ccf.getMethodName() = "createContextualFragment" and
|
||||
this = ccf.getArgument(0)
|
||||
)
|
||||
|
||||
@@ -165,7 +165,7 @@ module ExternalApiUsedWithUntrustedData {
|
||||
not param = base.getReceiver()
|
||||
|
|
||||
result = param and
|
||||
name = param.asSource().asExpr().(Parameter).getName()
|
||||
name = param.asSource().(DataFlow::ParameterNode).getName()
|
||||
or
|
||||
param.asSource().asExpr() instanceof DestructuringPattern and
|
||||
result = param.getMember(name)
|
||||
|
||||
@@ -33,12 +33,10 @@ module HardcodedCredentials {
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass of `Sink` that includes every `CredentialsExpr`
|
||||
* A subclass of `Sink` that includes every `CredentialsNode`
|
||||
* as a credentials sink.
|
||||
*/
|
||||
class DefaultCredentialsSink extends Sink, DataFlow::ValueNode {
|
||||
override CredentialsExpr astNode;
|
||||
|
||||
override string getKind() { result = astNode.getCredentialsKind() }
|
||||
class DefaultCredentialsSink extends Sink instanceof CredentialsNode {
|
||||
override string getKind() { result = super.getCredentialsKind() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,14 +30,12 @@ module InsufficientPasswordHash {
|
||||
* A potential clear-text password, considered as a source for password hashing
|
||||
* with insufficient computational effort.
|
||||
*/
|
||||
class CleartextPasswordSource extends Source, DataFlow::ValueNode {
|
||||
override SensitiveExpr astNode;
|
||||
|
||||
class CleartextPasswordSource extends Source instanceof SensitiveNode {
|
||||
CleartextPasswordSource() {
|
||||
astNode.getClassification() = SensitiveDataClassification::password()
|
||||
super.getClassification() = SensitiveDataClassification::password()
|
||||
}
|
||||
|
||||
override string describe() { result = astNode.describe() }
|
||||
override string describe() { result = SensitiveNode.super.describe() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +47,7 @@ module InsufficientPasswordHash {
|
||||
application.getAlgorithm().isWeak() or
|
||||
not application.getAlgorithm() instanceof PasswordHashingAlgorithm
|
||||
|
|
||||
this.asExpr() = application.getInput()
|
||||
this = application.getInput()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ class RateLimiterFlexibleRateLimiter extends DataFlow::FunctionNode {
|
||||
rateLimiterClassName.matches("RateLimiter%") and
|
||||
rateLimiterClass = API::moduleImport("rate-limiter-flexible").getMember(rateLimiterClassName) and
|
||||
rateLimiterConsume = rateLimiterClass.getInstance().getMember("consume") and
|
||||
request.getParameter() = getRouteHandlerParameter(this.getFunction(), "request") and
|
||||
request = getRouteHandlerParameter(this, "request") and
|
||||
request.getAPropertyRead().flowsTo(rateLimiterConsume.getAParameter().asSink())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,5 @@ module NosqlInjection {
|
||||
}
|
||||
|
||||
/** An expression interpreted as a NoSql query, viewed as a sink. */
|
||||
class NosqlQuerySink extends Sink, DataFlow::ValueNode {
|
||||
override NoSql::Query astNode;
|
||||
}
|
||||
class NosqlQuerySink extends Sink instanceof NoSql::Query { }
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
inlbl = TaintedObject::label() and
|
||||
outlbl = TaintedObject::label() and
|
||||
exists(NoSql::Query query, DataFlow::SourceNode queryObj |
|
||||
queryObj.flowsToExpr(query) and
|
||||
queryObj.flowsTo(query) and
|
||||
queryObj.flowsTo(trg) and
|
||||
src = queryObj.getAPropertyWrite().getRhs()
|
||||
)
|
||||
|
||||
@@ -41,9 +41,7 @@ module PostMessageStar {
|
||||
* A sensitive expression, viewed as a data flow source for cross-window communication
|
||||
* with unrestricted origin.
|
||||
*/
|
||||
class SensitiveExprSource extends Source, DataFlow::ValueNode {
|
||||
override SensitiveExpr astNode;
|
||||
}
|
||||
class SensitiveExprSource extends Source instanceof SensitiveNode { }
|
||||
|
||||
/** A call to any function whose name suggests that it encodes or encrypts its arguments. */
|
||||
class ProtectSanitizer extends Sanitizer {
|
||||
|
||||
@@ -24,10 +24,8 @@ module ReflectedXss {
|
||||
* a content type that does not (case-insensitively) contain the string "html". This
|
||||
* is to prevent us from flagging plain-text or JSON responses as vulnerable.
|
||||
*/
|
||||
class HttpResponseSink extends Sink, DataFlow::ValueNode {
|
||||
override HTTP::ResponseSendArgument astNode;
|
||||
|
||||
HttpResponseSink() { not exists(getANonHtmlHeaderDefinition(astNode)) }
|
||||
class HttpResponseSink extends Sink instanceof HTTP::ResponseSendArgument {
|
||||
HttpResponseSink() { not exists(getANonHtmlHeaderDefinition(this)) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,11 +57,11 @@ module RemotePropertyInjection {
|
||||
* header names as properties. This case is already handled by
|
||||
* `PropertyWriteSink`.
|
||||
*/
|
||||
class HeaderNameSink extends Sink, DataFlow::ValueNode {
|
||||
class HeaderNameSink extends Sink {
|
||||
HeaderNameSink() {
|
||||
exists(HTTP::ExplicitHeaderDefinition hd |
|
||||
not hd instanceof Express::SetMultipleHeaders and
|
||||
astNode = hd.getNameExpr()
|
||||
this = hd.getNameNode()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,17 +33,17 @@ module ServerSideUrlRedirect {
|
||||
/**
|
||||
* An HTTP redirect, considered as a sink for `Configuration`.
|
||||
*/
|
||||
class RedirectSink extends Sink, DataFlow::ValueNode {
|
||||
RedirectSink() { astNode = any(HTTP::RedirectInvocation redir).getUrlArgument() }
|
||||
class RedirectSink extends Sink {
|
||||
RedirectSink() { this = any(HTTP::RedirectInvocation redir).getUrlArgument() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A definition of the HTTP "Location" header, considered as a sink for
|
||||
* `Configuration`.
|
||||
*/
|
||||
class LocationHeaderSink extends Sink, DataFlow::ValueNode {
|
||||
class LocationHeaderSink extends Sink {
|
||||
LocationHeaderSink() {
|
||||
any(HTTP::ExplicitHeaderDefinition def).definesExplicitly("location", astNode)
|
||||
any(HTTP::ExplicitHeaderDefinition def).definesHeaderValue("location", this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,11 @@ module SqlInjection {
|
||||
}
|
||||
|
||||
/** An SQL expression passed to an API call that executes SQL. */
|
||||
class SqlInjectionExprSink extends Sink, DataFlow::ValueNode {
|
||||
override SQL::SqlString astNode;
|
||||
}
|
||||
class SqlInjectionExprSink extends Sink instanceof SQL::SqlString { }
|
||||
|
||||
/** An expression that sanitizes a value for the purposes of string based query injection. */
|
||||
class SanitizerExpr extends Sanitizer, DataFlow::ValueNode {
|
||||
SanitizerExpr() { astNode = any(SQL::SqlSanitizer ss).getOutput() }
|
||||
class SanitizerExpr extends Sanitizer {
|
||||
SanitizerExpr() { this = any(SQL::SqlSanitizer ss).getOutput() }
|
||||
}
|
||||
|
||||
/** An GraphQL expression passed to an API call that executes GraphQL. */
|
||||
|
||||
@@ -32,7 +32,5 @@ module StackTraceExposure {
|
||||
* An expression that can become part of an HTTP response body, viewed
|
||||
* as a data flow sink for stack trace exposure vulnerabilities.
|
||||
*/
|
||||
class DefaultSink extends Sink, DataFlow::ValueNode {
|
||||
override HTTP::ResponseBody astNode;
|
||||
}
|
||||
class DefaultSink extends Sink instanceof HTTP::ResponseBody { }
|
||||
}
|
||||
|
||||
@@ -647,12 +647,12 @@ module TaintedPath {
|
||||
/**
|
||||
* A path argument to the Express `res.render` method.
|
||||
*/
|
||||
class ExpressRenderSink extends Sink, DataFlow::ValueNode {
|
||||
class ExpressRenderSink extends Sink {
|
||||
ExpressRenderSink() {
|
||||
exists(MethodCallExpr mce |
|
||||
exists(DataFlow::MethodCallNode mce |
|
||||
Express::isResponse(mce.getReceiver()) and
|
||||
mce.getMethodName() = "render" and
|
||||
astNode = mce.getArgument(0)
|
||||
this = mce.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ module TemplateObjectInjection {
|
||||
predicate usesVulnerableTemplateEngine(Express::RouterDefinition router) {
|
||||
// option 1: `app.set("view engine", "theEngine")`.
|
||||
// Express will load the engine automatically.
|
||||
exists(MethodCallExpr call |
|
||||
router.flowsTo(call.getReceiver()) and
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
router.ref().getAMethodCall() = call and
|
||||
call.getMethodName() = "set" and
|
||||
call.getArgument(0).getStringValue() = "view engine" and
|
||||
call.getArgument(1).getStringValue() = getAVulnerableTemplateEngine()
|
||||
@@ -91,11 +91,11 @@ module TemplateObjectInjection {
|
||||
DataFlow::MethodCallNode viewEngineCall
|
||||
|
|
||||
// `app.engine("name", engine)
|
||||
router.flowsTo(registerCall.getReceiver().asExpr()) and
|
||||
router.ref().getAMethodCall() = registerCall and
|
||||
registerCall.getMethodName() = ["engine", "register"] and
|
||||
engine = registerCall.getArgument(1).getALocalSource() and
|
||||
// app.set("view engine", "name")
|
||||
router.flowsTo(viewEngineCall.getReceiver().asExpr()) and
|
||||
router.ref().getAMethodCall() = viewEngineCall and
|
||||
viewEngineCall.getMethodName() = "set" and
|
||||
viewEngineCall.getArgument(0).getStringValue() = "view engine" and
|
||||
// The name set by the `app.engine("name")` call matches `app.set("view engine", "name")`.
|
||||
|
||||
@@ -31,8 +31,8 @@ module XmlBomb {
|
||||
/**
|
||||
* An access to `document.location`, considered as a flow source for XML bomb vulnerabilities.
|
||||
*/
|
||||
class LocationAsSource extends Source, DataFlow::ValueNode {
|
||||
LocationAsSource() { isLocation(astNode) }
|
||||
class LocationAsSource extends Source {
|
||||
LocationAsSource() { isLocationNode(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,8 +31,8 @@ module Xxe {
|
||||
/**
|
||||
* An access to `document.location`, considered as a flow source for XXE vulnerabilities.
|
||||
*/
|
||||
class LocationAsSource extends Source, DataFlow::ValueNode {
|
||||
LocationAsSource() { isLocation(astNode) }
|
||||
class LocationAsSource extends Source {
|
||||
LocationAsSource() { isLocationNode(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,17 +42,19 @@ predicate isABuiltinEventName(string name) {
|
||||
* Holds if user code emits or broadcasts an event named `name`.
|
||||
*/
|
||||
predicate isAUserDefinedEventName(string name) {
|
||||
exists(string methodName, MethodCallExpr mce | methodName = "$emit" or methodName = "$broadcast" |
|
||||
mce.getArgument(0).mayHaveStringValue(name) and
|
||||
exists(string methodName, DataFlow::MethodCallNode mcn |
|
||||
methodName = "$emit" or methodName = "$broadcast"
|
||||
|
|
||||
mcn.getArgument(0).mayHaveStringValue(name) and
|
||||
(
|
||||
// dataflow based scope resolution
|
||||
mce = any(AngularJS::ScopeServiceReference scope).getAMethodCall(methodName)
|
||||
mcn = any(AngularJS::ScopeServiceReference scope).getAMethodCall(methodName)
|
||||
or
|
||||
// heuristic scope resolution: assume parameters like `$scope` or `$rootScope` are AngularJS scope objects
|
||||
exists(SimpleParameter param |
|
||||
exists(DataFlow::ParameterNode param |
|
||||
param.getName() = any(AngularJS::ScopeServiceReference scope).getName() and
|
||||
mce.getReceiver().mayReferToParameter(param) and
|
||||
mce.getMethodName() = methodName
|
||||
param.getAMethodCall() = mcn and
|
||||
mcn.getMethodName() = methodName
|
||||
)
|
||||
or
|
||||
// a call in an AngularJS expression
|
||||
@@ -64,7 +66,7 @@ predicate isAUserDefinedEventName(string name) {
|
||||
)
|
||||
}
|
||||
|
||||
from AngularJS::ScopeServiceReference scope, MethodCallExpr mce, string eventName
|
||||
from AngularJS::ScopeServiceReference scope, DataFlow::MethodCallNode mce, string eventName
|
||||
where
|
||||
mce = scope.getAMethodCall("$on") and
|
||||
mce.getArgument(0).mayHaveStringValue(eventName) and
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import javascript
|
||||
|
||||
from AngularJS::InjectableFunction f, SimpleParameter p, string msg
|
||||
from AngularJS::InjectableFunction f, DataFlow::ParameterNode p, string msg
|
||||
where
|
||||
p = f.asFunction().getAParameter() and
|
||||
(
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import javascript
|
||||
|
||||
from MethodCallExpr mce, AngularJS::BuiltinServiceReference service
|
||||
from DataFlow::MethodCallNode mce, AngularJS::BuiltinServiceReference service
|
||||
where
|
||||
service.getName() = "$sceProvider" and
|
||||
mce = service.getAMethodCall("enabled") and
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import javascript
|
||||
|
||||
from AngularJS::ServiceReference compile, SimpleParameter elem, CallExpr c
|
||||
from AngularJS::ServiceReference compile, DataFlow::ParameterNode elem, DataFlow::CallNode c
|
||||
where
|
||||
compile.getName() = "$compile" and
|
||||
elem =
|
||||
@@ -24,7 +24,7 @@ where
|
||||
.(AngularJS::LinkFunction)
|
||||
.getElementParameter() and
|
||||
c = compile.getACall() and
|
||||
c.getArgument(0).mayReferToParameter(elem) and
|
||||
elem.flowsTo(c.getArgument(0)) and
|
||||
// don't flag $compile calls that specify a `maxPriority`
|
||||
c.getNumArgument() < 3
|
||||
select c, "This call to $compile may cause double compilation of '" + elem + "'."
|
||||
|
||||
@@ -12,16 +12,17 @@
|
||||
import javascript
|
||||
import semmle.javascript.RestrictedLocations
|
||||
|
||||
predicate isRepeatedDependency(AngularJS::InjectableFunction f, string name, AstNode location) {
|
||||
predicate isRepeatedDependency(AngularJS::InjectableFunction f, string name, DataFlow::Node node) {
|
||||
exists(int i, int j |
|
||||
i < j and
|
||||
exists(f.getDependencyDeclaration(i, name)) and
|
||||
location = f.getDependencyDeclaration(j, name)
|
||||
node = f.getDependencyDeclaration(j, name)
|
||||
)
|
||||
}
|
||||
|
||||
from AngularJS::InjectableFunction f, AstNode node, string name
|
||||
from AngularJS::InjectableFunction f, DataFlow::Node node, string name
|
||||
where
|
||||
isRepeatedDependency(f, name, node) and
|
||||
not count(f.asFunction().getParameterByName(name)) > 1 // avoid duplicating reports from js/duplicate-parameter-name
|
||||
select f.asFunction().(FirstLineOf), "This function has a duplicate dependency '$@'.", node, name
|
||||
select f.asFunction().getFunction().(FirstLineOf), "This function has a duplicate dependency '$@'.",
|
||||
node, name
|
||||
|
||||
@@ -23,7 +23,7 @@ predicate isResourceUrlWhitelist(
|
||||
) {
|
||||
exists(AngularJS::ServiceReference service |
|
||||
service.getName() = "$sceDelegateProvider" and
|
||||
setupCall.asExpr() = service.getAMethodCall("resourceUrlWhitelist") and
|
||||
setupCall = service.getAMethodCall("resourceUrlWhitelist") and
|
||||
list.flowsTo(setupCall.getArgument(0))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
import javascript
|
||||
import semmle.javascript.RestrictedLocations
|
||||
|
||||
from AngularJS::InjectableFunction f, AstNode explicitInjection
|
||||
from AngularJS::InjectableFunction f, DataFlow::Node explicitInjection
|
||||
where
|
||||
count(f.getAnExplicitDependencyInjection()) > 1 and
|
||||
explicitInjection = f.getAnExplicitDependencyInjection()
|
||||
select f.asFunction().(FirstLineOf), "This function has $@ defined in multiple places.",
|
||||
explicitInjection, "dependency injections"
|
||||
select f.asFunction().getFunction().(FirstLineOf),
|
||||
"This function has $@ defined in multiple places.", explicitInjection, "dependency injections"
|
||||
|
||||
@@ -13,9 +13,9 @@ import javascript
|
||||
import Declarations.UnusedParameter
|
||||
import semmle.javascript.RestrictedLocations
|
||||
|
||||
predicate isUnusedParameter(Function f, string msg, Parameter parameter) {
|
||||
predicate isUnusedParameter(DataFlow::FunctionNode f, string msg, Parameter parameter) {
|
||||
exists(Variable pv |
|
||||
isUnused(f, parameter, pv, _) and
|
||||
isUnused(f.getFunction(), parameter, pv, _) and
|
||||
not isAnAccidentallyUnusedParameter(parameter) and // avoid double alerts
|
||||
msg = "Unused dependency " + pv.getName() + "."
|
||||
)
|
||||
|
||||
@@ -135,7 +135,7 @@ DataFlow::CallNode servesAPrivateFolder(string description) {
|
||||
*/
|
||||
Express::RouteSetup getAnExposingExpressSetup(string path) {
|
||||
result.isUseCall() and
|
||||
result.getArgument([0 .. 1]) = servesAPrivateFolder(path).getEnclosingExpr()
|
||||
result.getArgument([0 .. 1]) = servesAPrivateFolder(path)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +149,7 @@ DataFlow::CallNode getAnExposingServeSetup(string path) {
|
||||
|
||||
from DataFlow::Node node, string path
|
||||
where
|
||||
node = getAnExposingExpressSetup(path).flow()
|
||||
node = getAnExposingExpressSetup(path)
|
||||
or
|
||||
node = getAnExposingServeSetup(path)
|
||||
select node, "Serves " + path + ", which can contain private information."
|
||||
|
||||
@@ -19,7 +19,7 @@ import DataFlow::PathGraph
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
not source.getNode().asExpr() instanceof CleartextPasswordExpr // flagged by js/insufficient-password-hash
|
||||
not source.getNode() instanceof CleartextPasswordExpr // flagged by js/insufficient-password-hash
|
||||
select sink.getNode(), source, sink,
|
||||
"Sensitive data from $@ is used in a broken or weak cryptographic algorithm.", source.getNode(),
|
||||
source.getNode().(Source).describe()
|
||||
|
||||
@@ -15,13 +15,13 @@ import javascript
|
||||
|
||||
from
|
||||
Routing::RouteSetup setup, Routing::RouteHandler handler, HTTP::RequestInputAccess input,
|
||||
SensitiveExpr sensitive
|
||||
SensitiveNode sensitive
|
||||
where
|
||||
setup.getOwnHttpMethod() = "GET" and
|
||||
setup.getAChild+() = handler and
|
||||
input.getRouteHandler() = handler.getFunction() and
|
||||
input.getKind() = "parameter" and
|
||||
input.(DataFlow::SourceNode).flowsToExpr(sensitive) and
|
||||
input.(DataFlow::SourceNode).flowsTo(sensitive) and
|
||||
not sensitive.getClassification() = SensitiveDataClassification::id()
|
||||
select input, "$@ for GET requests uses query parameter as sensitive data.", handler,
|
||||
"Route handler"
|
||||
|
||||
@@ -77,7 +77,7 @@ private module StandardPoIs {
|
||||
UnpromotedRouteSetupPoI() { this = "UnpromotedRouteSetupPoI" }
|
||||
|
||||
override predicate is(Node l0) {
|
||||
l0 instanceof HTTP::RouteSetupCandidate and not l0.asExpr() instanceof HTTP::RouteSetup
|
||||
l0 instanceof HTTP::RouteSetupCandidate and not l0 instanceof HTTP::RouteSetup
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import CandidateTracking
|
||||
|
||||
from HTTP::RouteSetupCandidate setup
|
||||
where
|
||||
not setup.asExpr() instanceof HTTP::RouteSetup and
|
||||
not setup instanceof HTTP::RouteSetup and
|
||||
exists(HTTP::RouteHandlerCandidate rh |
|
||||
track(rh, DataFlow::TypeTracker::end()).flowsTo(setup.getARouteHandlerArg())
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ class RouteHandlerAndSetupPoI extends ActivePoI {
|
||||
RouteHandlerAndSetupPoI() { this = "RouteHandlerAndSetupPoI" }
|
||||
|
||||
override predicate is(Node l0, Node l1, string t1) {
|
||||
l1.asExpr().(Express::RouteSetup).getARouteHandler() = l0 and t1 = "setup"
|
||||
l1.(Express::RouteSetup).getARouteHandler() = l0 and t1 = "setup"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ class RouteSetupAndRouterAndRouteHandlerPoI extends ActivePoI {
|
||||
RouteSetupAndRouterAndRouteHandlerPoI() { this = "RouteSetupAndRouterAndRouteHandlerPoI" }
|
||||
|
||||
override predicate is(Node l0, Node l1, string t1, Node l2, string t2) {
|
||||
l0.asExpr().(Express::RouteSetup).getRouter().flow() = l1 and
|
||||
l0.(Express::RouteSetup).getRouter() = l1 and
|
||||
t1 = "router" and
|
||||
l0.asExpr().(Express::RouteSetup).getARouteHandler() = l2 and
|
||||
l0.(Express::RouteSetup).getARouteHandler() = l2 and
|
||||
t2 = "routehandler"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,4 @@ query predicate processTermination(NodeJSLib::ProcessTermination term) { any() }
|
||||
|
||||
query predicate sensitiveAction(SensitiveAction ac) { any() }
|
||||
|
||||
query predicate sensitiveExpr(SensitiveExpr e) { any() }
|
||||
query predicate sensitiveExpr(SensitiveNode e) { any() }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
from AngularJS::ScopeServiceReference s, MethodCallExpr mce
|
||||
from AngularJS::ScopeServiceReference s, DataFlow::MethodCallNode mce
|
||||
where mce = s.getAMethodCall(_)
|
||||
select mce
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import javascript
|
||||
private import AngularJS
|
||||
|
||||
from InjectableFunction f, SimpleParameter p, DataFlow::Node nd
|
||||
from InjectableFunction f, DataFlow::ParameterNode p, DataFlow::Node nd
|
||||
where nd = f.getCustomServiceDependency(p)
|
||||
select p.getName(), nd
|
||||
|
||||
@@ -1,92 +1,137 @@
|
||||
| isolate scope for directive1 | scope-access.js:4:41:4:45 | scope |
|
||||
| isolate scope for directive1 | scope-access.js:4:41:4:45 | scope |
|
||||
| isolate scope for directive1 | scope-access.js:5:17:5:21 | scope |
|
||||
| isolate scope for directive1 | scope-access.js:7:20:7:21 | {} |
|
||||
| isolate scope for directive2 | scope-access.js:12:34:12:39 | $scope |
|
||||
| isolate scope for directive2 | scope-access.js:12:34:12:39 | $scope |
|
||||
| isolate scope for directive2 | scope-access.js:13:17:13:22 | $scope |
|
||||
| isolate scope for directive2 | scope-access.js:15:20:15:21 | {} |
|
||||
| isolate scope for directive3 | scope-access.js:20:39:20:44 | $scope |
|
||||
| isolate scope for directive3 | scope-access.js:20:39:20:44 | $scope |
|
||||
| isolate scope for directive3 | scope-access.js:21:17:21:22 | $scope |
|
||||
| isolate scope for directive3 | scope-access.js:23:20:23:21 | {} |
|
||||
| isolate scope for directive4 | scope-access.js:28:45:28:45 | a |
|
||||
| isolate scope for directive4 | scope-access.js:28:45:28:45 | a |
|
||||
| isolate scope for directive4 | scope-access.js:29:17:29:17 | a |
|
||||
| isolate scope for directive4 | scope-access.js:31:20:31:21 | {} |
|
||||
| isolate scope for directive5 | scope-access.js:36:25:36:24 | this |
|
||||
| isolate scope for directive5 | scope-access.js:37:17:37:20 | this |
|
||||
| isolate scope for directive5 | scope-access.js:39:20:39:21 | {} |
|
||||
| isolate scope for directive6 | scope-access.js:45:25:45:24 | this |
|
||||
| isolate scope for directive6 | scope-access.js:46:18:46:26 | return of anonymous function |
|
||||
| isolate scope for directive6 | scope-access.js:46:23:46:26 | this |
|
||||
| isolate scope for directive6 | scope-access.js:48:20:48:21 | {} |
|
||||
| isolate scope for myCustomer | dev-guide-5.js:11:12:13:5 | { // Sc ... y\\n } |
|
||||
| isolate scope for myCustomer | dev-guide-6.js:11:12:13:5 | { // Sc ... y\\n } |
|
||||
| scope for <directive7>...</> | scope-access.js:54:34:54:39 | $scope |
|
||||
| scope for <directive7>...</> | scope-access.js:54:34:54:39 | $scope |
|
||||
| scope for <directive7>...</> | scope-access.js:55:17:55:22 | $scope |
|
||||
| scope for <div>...</> | dev-guide-1.js:4:49:4:54 | $scope |
|
||||
| scope for <div>...</> | dev-guide-1.js:4:49:4:54 | $scope |
|
||||
| scope for <div>...</> | dev-guide-1.js:4:49:4:54 | $scope |
|
||||
| scope for <div>...</> | dev-guide-1.js:5:3:5:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-1.js:7:3:7:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-1.js:7:21:7:20 | $scope |
|
||||
| scope for <div>...</> | dev-guide-1.js:8:5:8:10 | $scope |
|
||||
| scope for <div>...</> | dev-guide-1.js:8:34:8:39 | $scope |
|
||||
| scope for <div>...</> | dev-guide-2.js:4:66:4:71 | $scope |
|
||||
| scope for <div>...</> | dev-guide-2.js:4:66:4:71 | $scope |
|
||||
| scope for <div>...</> | dev-guide-2.js:5:3:5:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-2.js:8:51:8:56 | $scope |
|
||||
| scope for <div>...</> | dev-guide-2.js:8:51:8:56 | $scope |
|
||||
| scope for <div>...</> | dev-guide-2.js:9:3:9:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <div>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <div>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <div>...</> | dev-guide-3.js:5:3:5:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-3.js:6:3:6:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-3.js:6:25:6:24 | $scope |
|
||||
| scope for <div>...</> | dev-guide-3.js:7:5:7:10 | $scope |
|
||||
| scope for <div>...</> | dev-guide-4.js:4:52:4:57 | $scope |
|
||||
| scope for <div>...</> | dev-guide-4.js:4:52:4:57 | $scope |
|
||||
| scope for <div>...</> | dev-guide-4.js:5:3:5:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-4.js:10:51:10:56 | $scope |
|
||||
| scope for <div>...</> | dev-guide-4.js:10:51:10:56 | $scope |
|
||||
| scope for <div>...</> | dev-guide-4.js:11:3:11:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-5.js:4:47:4:52 | $scope |
|
||||
| scope for <div>...</> | dev-guide-5.js:4:47:4:52 | $scope |
|
||||
| scope for <div>...</> | dev-guide-5.js:4:47:4:52 | $scope |
|
||||
| scope for <div>...</> | dev-guide-5.js:4:47:4:52 | $scope |
|
||||
| scope for <div>...</> | dev-guide-5.js:5:3:5:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-5.js:5:3:5:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-5.js:6:3:6:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-5.js:6:3:6:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-6.js:4:47:4:52 | $scope |
|
||||
| scope for <div>...</> | dev-guide-6.js:4:47:4:52 | $scope |
|
||||
| scope for <div>...</> | dev-guide-6.js:4:47:4:52 | $scope |
|
||||
| scope for <div>...</> | dev-guide-6.js:4:47:4:52 | $scope |
|
||||
| scope for <div>...</> | dev-guide-6.js:5:3:5:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-6.js:5:3:5:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-6.js:6:3:6:8 | $scope |
|
||||
| scope for <div>...</> | dev-guide-6.js:6:3:6:8 | $scope |
|
||||
| scope for <elementthatusescontroller1>...</> | scope-access.js:59:52:59:57 | $scope |
|
||||
| scope for <elementthatusescontroller1>...</> | scope-access.js:59:52:59:57 | $scope |
|
||||
| scope for <elementthatusescontroller1>...</> | scope-access.js:60:9:60:14 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:5:3:5:8 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:5:3:5:8 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:6:3:6:8 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:6:3:6:8 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:6:25:6:24 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:6:25:6:24 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:7:5:7:10 | $scope |
|
||||
| scope for <li>...</> | dev-guide-3.js:7:5:7:10 | $scope |
|
||||
| scope in dev-guide-1.html | dev-guide-1.js:4:49:4:54 | $scope |
|
||||
| scope in dev-guide-1.html | dev-guide-1.js:4:49:4:54 | $scope |
|
||||
| scope in dev-guide-1.html | dev-guide-1.js:4:49:4:54 | $scope |
|
||||
| scope in dev-guide-1.html | dev-guide-1.js:5:3:5:8 | $scope |
|
||||
| scope in dev-guide-1.html | dev-guide-1.js:7:3:7:8 | $scope |
|
||||
| scope in dev-guide-1.html | dev-guide-1.js:7:21:7:20 | $scope |
|
||||
| scope in dev-guide-1.html | dev-guide-1.js:8:5:8:10 | $scope |
|
||||
| scope in dev-guide-1.html | dev-guide-1.js:8:34:8:39 | $scope |
|
||||
| scope in dev-guide-2.html | dev-guide-2.js:4:66:4:71 | $scope |
|
||||
| scope in dev-guide-2.html | dev-guide-2.js:4:66:4:71 | $scope |
|
||||
| scope in dev-guide-2.html | dev-guide-2.js:5:3:5:8 | $scope |
|
||||
| scope in dev-guide-2.html | dev-guide-2.js:8:51:8:56 | $scope |
|
||||
| scope in dev-guide-2.html | dev-guide-2.js:8:51:8:56 | $scope |
|
||||
| scope in dev-guide-2.html | dev-guide-2.js:9:3:9:8 | $scope |
|
||||
| scope in dev-guide-3.html | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope in dev-guide-3.html | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope in dev-guide-3.html | dev-guide-3.js:4:52:4:57 | $scope |
|
||||
| scope in dev-guide-3.html | dev-guide-3.js:5:3:5:8 | $scope |
|
||||
| scope in dev-guide-3.html | dev-guide-3.js:6:3:6:8 | $scope |
|
||||
| scope in dev-guide-3.html | dev-guide-3.js:6:25:6:24 | $scope |
|
||||
| scope in dev-guide-3.html | dev-guide-3.js:7:5:7:10 | $scope |
|
||||
| scope in dev-guide-4.html | dev-guide-4.js:4:52:4:57 | $scope |
|
||||
| scope in dev-guide-4.html | dev-guide-4.js:4:52:4:57 | $scope |
|
||||
| scope in dev-guide-4.html | dev-guide-4.js:5:3:5:8 | $scope |
|
||||
| scope in dev-guide-4.html | dev-guide-4.js:10:51:10:56 | $scope |
|
||||
| scope in dev-guide-4.html | dev-guide-4.js:10:51:10:56 | $scope |
|
||||
| scope in dev-guide-4.html | dev-guide-4.js:11:3:11:8 | $scope |
|
||||
| scope in dev-guide-5.html | dev-guide-5.js:4:47:4:52 | $scope |
|
||||
| scope in dev-guide-5.html | dev-guide-5.js:4:47:4:52 | $scope |
|
||||
| scope in dev-guide-5.html | dev-guide-5.js:5:3:5:8 | $scope |
|
||||
| scope in dev-guide-5.html | dev-guide-5.js:6:3:6:8 | $scope |
|
||||
| scope in dev-guide-5.html | dev-guide-6.js:4:47:4:52 | $scope |
|
||||
| scope in dev-guide-5.html | dev-guide-6.js:4:47:4:52 | $scope |
|
||||
| scope in dev-guide-5.html | dev-guide-6.js:5:3:5:8 | $scope |
|
||||
| scope in dev-guide-5.html | dev-guide-6.js:6:3:6:8 | $scope |
|
||||
| scope in dev-guide-6.html | dev-guide-5.js:4:47:4:52 | $scope |
|
||||
| scope in dev-guide-6.html | dev-guide-5.js:4:47:4:52 | $scope |
|
||||
| scope in dev-guide-6.html | dev-guide-5.js:5:3:5:8 | $scope |
|
||||
| scope in dev-guide-6.html | dev-guide-5.js:6:3:6:8 | $scope |
|
||||
| scope in dev-guide-6.html | dev-guide-6.js:4:47:4:52 | $scope |
|
||||
| scope in dev-guide-6.html | dev-guide-6.js:4:47:4:52 | $scope |
|
||||
| scope in dev-guide-6.html | dev-guide-6.js:5:3:5:8 | $scope |
|
||||
| scope in dev-guide-6.html | dev-guide-6.js:6:3:6:8 | $scope |
|
||||
| scope in scope-access.html | scope-access.js:54:34:54:39 | $scope |
|
||||
| scope in scope-access.html | scope-access.js:54:34:54:39 | $scope |
|
||||
| scope in scope-access.html | scope-access.js:55:17:55:22 | $scope |
|
||||
| scope in scope-access.html | scope-access.js:59:52:59:57 | $scope |
|
||||
| scope in scope-access.html | scope-access.js:59:52:59:57 | $scope |
|
||||
| scope in scope-access.html | scope-access.js:60:9:60:14 | $scope |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_HeaderDefinition_getNameExpr(HTTP::ExplicitHeaderDefinition hd, Expr res) {
|
||||
hd.getRouteHandler() instanceof Express::RouteHandler and res = hd.getNameExpr()
|
||||
query predicate test_HeaderDefinition_getNameExpr(
|
||||
HTTP::ExplicitHeaderDefinition hd, DataFlow::Node res
|
||||
) {
|
||||
hd.getRouteHandler() instanceof Express::RouteHandler and res = hd.getNameNode()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RequestExpr(Express::RequestExpr e, HTTP::RouteHandler res) {
|
||||
query predicate test_RequestExpr(Express::RequestNode e, HTTP::RouteHandler res) {
|
||||
res = e.getRouteHandler()
|
||||
}
|
||||
|
||||
query predicate test_RequestExprStandalone(Express::RequestExpr e) {
|
||||
query predicate test_RequestExprStandalone(Express::RequestNode e) {
|
||||
not exists(e.getRouteHandler())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ResponseExpr(Express::ResponseExpr e, HTTP::RouteHandler res) {
|
||||
query predicate test_ResponseExpr(Express::ResponseNode e, HTTP::RouteHandler res) {
|
||||
res = e.getRouteHandler()
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteExpr(Express::RouteExpr e, Express::RouterDefinition res) {
|
||||
res = e.getRouter()
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandler(Express::RouteHandler rh, Parameter res0, Parameter res1) {
|
||||
query predicate test_RouteHandler(
|
||||
Express::RouteHandler rh, DataFlow::ParameterNode res0, DataFlow::ParameterNode res1
|
||||
) {
|
||||
res0 = rh.getRequestParameter() and res1 = rh.getResponseParameter()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandlerExpr(
|
||||
Express::RouteHandlerExpr rhe, Express::RouteSetup res0, boolean isLast
|
||||
Express::RouteHandlerNode rhe, Express::RouteSetup res0, boolean isLast
|
||||
) {
|
||||
(if rhe.isLastHandler() then isLast = true else isLast = false) and
|
||||
res0 = rhe.getSetup()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandlerExpr_getAMatchingAncestor(
|
||||
Express::RouteHandlerExpr expr, Express::RouteHandlerExpr res
|
||||
Express::RouteHandlerNode expr, Express::RouteHandlerNode res
|
||||
) {
|
||||
res = expr.getAMatchingAncestor()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandlerExpr_getAsSubRouter(
|
||||
Express::RouteHandlerExpr expr, Express::RouterDefinition res
|
||||
Express::RouteHandlerNode expr, Express::RouterDefinition res
|
||||
) {
|
||||
res = expr.getAsSubRouter()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandlerExpr_getBody(
|
||||
Express::RouteHandlerExpr rhe, Express::RouteHandler res
|
||||
Express::RouteHandlerNode rhe, Express::RouteHandler res
|
||||
) {
|
||||
res = rhe.getBody()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandlerExpr_getNextMiddleware(
|
||||
Express::RouteHandlerExpr expr, Express::RouteHandlerExpr res
|
||||
Express::RouteHandlerNode expr, Express::RouteHandlerNode res
|
||||
) {
|
||||
res = expr.getNextMiddleware()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandlerExpr_getPreviousMiddleware(
|
||||
Express::RouteHandlerExpr expr, Express::RouteHandlerExpr res
|
||||
Express::RouteHandlerNode expr, Express::RouteHandlerNode res
|
||||
) {
|
||||
res = expr.getPreviousMiddleware()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandler_getARequestBodyAccess(Express::RouteHandler rh, Expr res) {
|
||||
query predicate test_RouteHandler_getARequestBodyAccess(Express::RouteHandler rh, DataFlow::Node res) {
|
||||
res = rh.getARequestBodyAccess()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandler_getARequestExpr(Express::RouteHandler rh, HTTP::RequestExpr res) {
|
||||
res = rh.getARequestExpr()
|
||||
query predicate test_RouteHandler_getARequestExpr(Express::RouteHandler rh, HTTP::RequestNode res) {
|
||||
res = rh.getARequestNode()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandler_getAResponseExpr(Express::RouteHandler rh, HTTP::ResponseExpr res) {
|
||||
res = rh.getAResponseExpr()
|
||||
query predicate test_RouteHandler_getAResponseExpr(Express::RouteHandler rh, HTTP::ResponseNode res) {
|
||||
res = rh.getAResponseNode()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteSetup(Express::RouteSetup rs, Expr res0, boolean isUseCall) {
|
||||
query predicate test_RouteSetup(Express::RouteSetup rs, DataFlow::Node res0, boolean isUseCall) {
|
||||
(if rs.isUseCall() then isUseCall = true else isUseCall = false) and
|
||||
res0 = rs.getServer()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteSetup_getARouteHandlerExpr(Express::RouteSetup r, Expr res) {
|
||||
res = r.getARouteHandlerExpr()
|
||||
query predicate test_RouteSetup_getARouteHandlerExpr(Express::RouteSetup r, DataFlow::Node res) {
|
||||
res = r.getARouteHandlerNode()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteSetup_getLastRouteHandlerExpr(Express::RouteSetup r, Expr res) {
|
||||
res = r.getLastRouteHandlerExpr()
|
||||
query predicate test_RouteSetup_getLastRouteHandlerExpr(Express::RouteSetup r, DataFlow::Node res) {
|
||||
res = r.getLastRouteHandlerNode()
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user