mirror of
https://github.com/github/codeql.git
synced 2025-12-18 09:43:15 +01:00
Merge branch 'main' into rc/3.7
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"name": "typescript-parser-wrapper",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"typescript": "4.7.2"
|
||||
"typescript": "4.8.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.json",
|
||||
|
||||
@@ -168,20 +168,9 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
|
||||
}
|
||||
}
|
||||
|
||||
// Number of conditional type expressions the visitor is currently inside.
|
||||
// We disable type extraction inside such type expressions, to avoid complications
|
||||
// with `infer` types.
|
||||
let insideConditionalTypes = 0;
|
||||
|
||||
visitAstNode(ast);
|
||||
function visitAstNode(node: AugmentedNode) {
|
||||
if (node.kind === ts.SyntaxKind.ConditionalType) {
|
||||
++insideConditionalTypes;
|
||||
}
|
||||
ts.forEachChild(node, visitAstNode);
|
||||
if (node.kind === ts.SyntaxKind.ConditionalType) {
|
||||
--insideConditionalTypes;
|
||||
}
|
||||
|
||||
// fill in line/column info
|
||||
if ("pos" in node) {
|
||||
@@ -202,7 +191,7 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
|
||||
}
|
||||
}
|
||||
|
||||
if (typeChecker != null && insideConditionalTypes === 0) {
|
||||
if (typeChecker != null) {
|
||||
if (isTypedNode(node)) {
|
||||
let contextualType = isContextuallyTypedNode(node)
|
||||
? typeChecker.getContextualType(node)
|
||||
|
||||
@@ -241,7 +241,7 @@ const astProperties: string[] = [
|
||||
"constructor",
|
||||
"declarationList",
|
||||
"declarations",
|
||||
"decorators",
|
||||
"illegalDecorators",
|
||||
"default",
|
||||
"delete",
|
||||
"dotDotDotToken",
|
||||
|
||||
@@ -947,7 +947,7 @@ export class TypeTable {
|
||||
* Returns a unique string for the given call/constructor signature.
|
||||
*/
|
||||
private getSignatureString(kind: ts.SignatureKind, signature: AugmentedSignature): string {
|
||||
let modifiers : ts.ModifiersArray = signature.getDeclaration()?.modifiers;
|
||||
let modifiers = signature.getDeclaration()?.modifiers;
|
||||
let isAbstract = modifiers && modifiers.filter(modifier => modifier.kind == ts.SyntaxKind.AbstractKeyword).length > 0
|
||||
|
||||
let parameters = signature.getParameters();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
version "12.7.11"
|
||||
resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446
|
||||
|
||||
typescript@4.7.2:
|
||||
version "4.7.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4"
|
||||
integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==
|
||||
typescript@4.8.2:
|
||||
version "4.8.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790"
|
||||
integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==
|
||||
|
||||
@@ -41,7 +41,7 @@ public class Main {
|
||||
* A version identifier that should be updated every time the extractor changes in such a way that
|
||||
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
|
||||
*/
|
||||
public static final String EXTRACTOR_VERSION = "2022-07-11";
|
||||
public static final String EXTRACTOR_VERSION = "2022-08-25";
|
||||
|
||||
public static final Pattern NEWLINE = Pattern.compile("\n");
|
||||
|
||||
|
||||
@@ -976,8 +976,9 @@ public class TypeScriptASTConverter {
|
||||
hasDeclareKeyword,
|
||||
hasAbstractKeyword);
|
||||
attachSymbolInformation(classDecl.getClassDef(), node);
|
||||
if (node.has("decorators")) {
|
||||
classDecl.addDecorators(convertChildren(node, "decorators"));
|
||||
List<Decorator> decorators = getDecorators(node);
|
||||
if (!decorators.isEmpty()) {
|
||||
classDecl.addDecorators(decorators);
|
||||
advanceUntilAfter(loc, classDecl.getDecorators());
|
||||
}
|
||||
Node exportedDecl = fixExports(loc, classDecl);
|
||||
@@ -989,6 +990,17 @@ public class TypeScriptASTConverter {
|
||||
return exportedDecl;
|
||||
}
|
||||
|
||||
List<Decorator> getDecorators(JsonObject node) throws ParseError {
|
||||
List<Decorator> result = new ArrayList<>();
|
||||
for (JsonElement elt : getChildIterable(node, "modifiers")) {
|
||||
JsonObject modifier = elt.getAsJsonObject();
|
||||
if (hasKind(modifier, "Decorator")) {
|
||||
result.add((Decorator) convertNode(modifier));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Node convertCommaListExpression(JsonObject node, SourceLocation loc) throws ParseError {
|
||||
return new SequenceExpression(loc, convertChildren(node, "elements"));
|
||||
}
|
||||
@@ -1041,7 +1053,7 @@ public class TypeScriptASTConverter {
|
||||
private List<DecoratorList> convertParameterDecorators(JsonObject function) throws ParseError {
|
||||
List<DecoratorList> decoratorLists = new ArrayList<>();
|
||||
for (JsonElement parameter : getProperParameters(function)) {
|
||||
decoratorLists.add(makeDecoratorList(parameter.getAsJsonObject().get("decorators")));
|
||||
decoratorLists.add(makeDecoratorList(parameter.getAsJsonObject().get("modifiers")));
|
||||
}
|
||||
return decoratorLists;
|
||||
}
|
||||
@@ -1139,7 +1151,7 @@ public class TypeScriptASTConverter {
|
||||
loc,
|
||||
hasModifier(node, "ConstKeyword"),
|
||||
hasModifier(node, "DeclareKeyword"),
|
||||
convertChildrenNotNull(node, "decorators"),
|
||||
convertChildrenNotNull(node, "illegalDecorators"), // as of https://github.com/microsoft/TypeScript/pull/50343/ the property is called `illegalDecorators` instead of `decorators`
|
||||
convertChild(node, "name"),
|
||||
convertChildren(node, "members"));
|
||||
attachSymbolInformation(enumDeclaration, node);
|
||||
@@ -1664,8 +1676,9 @@ public class TypeScriptASTConverter {
|
||||
FunctionExpression method = convertImplicitFunction(node, loc);
|
||||
MethodDefinition methodDefinition =
|
||||
new MethodDefinition(loc, flags, methodKind, convertChild(node, "name"), method);
|
||||
if (node.has("decorators")) {
|
||||
methodDefinition.addDecorators(convertChildren(node, "decorators"));
|
||||
List<Decorator> decorators = getDecorators(node);
|
||||
if (!decorators.isEmpty()) {
|
||||
methodDefinition.addDecorators(decorators);
|
||||
advanceUntilAfter(loc, methodDefinition.getDecorators());
|
||||
}
|
||||
return methodDefinition;
|
||||
@@ -2079,8 +2092,9 @@ public class TypeScriptASTConverter {
|
||||
convertChild(node, "name"),
|
||||
convertChild(node, "initializer"),
|
||||
convertChildAsType(node, "type"));
|
||||
if (node.has("decorators")) {
|
||||
fieldDefinition.addDecorators(convertChildren(node, "decorators"));
|
||||
List<Decorator> decorators = getDecorators(node);
|
||||
if (!decorators.isEmpty()) {
|
||||
fieldDefinition.addDecorators(decorators);
|
||||
advanceUntilAfter(loc, fieldDefinition.getDecorators());
|
||||
}
|
||||
return fieldDefinition;
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-experimental-atm-lib
|
||||
version: 0.3.2
|
||||
version: 0.3.4
|
||||
extractor: javascript
|
||||
library: true
|
||||
groups:
|
||||
|
||||
@@ -15,16 +15,16 @@ import extraction.ExtractEndpointData
|
||||
|
||||
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate, Query query) {
|
||||
query instanceof NosqlInjectionQuery and
|
||||
result = NosqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof SqlInjectionQuery and
|
||||
result = SqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof TaintedPathQuery and
|
||||
result = TaintedPathATM::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof XssQuery and
|
||||
result = XssATM::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
|
||||
@@ -14,10 +14,26 @@ import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures
|
||||
import experimental.adaptivethreatmodeling.EndpointScoring as EndpointScoring
|
||||
import experimental.adaptivethreatmodeling.EndpointTypes
|
||||
import experimental.adaptivethreatmodeling.FilteringReasons
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathATM
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
|
||||
/** DEPRECATED: Alias for NosqlInjectionAtm */
|
||||
deprecated module NosqlInjectionATM = NosqlInjectionAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
|
||||
/** DEPRECATED: Alias for SqlInjectionAtm */
|
||||
deprecated module SqlInjectionATM = SqlInjectionAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
|
||||
/** DEPRECATED: Alias for TaintedPathAtm */
|
||||
deprecated module TaintedPathATM = TaintedPathAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
|
||||
/** DEPRECATED: Alias for XssAtm */
|
||||
deprecated module XssATM = XssAtm;
|
||||
|
||||
import Labels
|
||||
import NoFeaturizationRestrictionsConfig
|
||||
import Queries
|
||||
@@ -25,13 +41,13 @@ import Queries
|
||||
/** Gets the ATM configuration object for the specified query. */
|
||||
AtmConfig getAtmCfg(Query query) {
|
||||
query instanceof NosqlInjectionQuery and
|
||||
result instanceof NosqlInjectionATM::NosqlInjectionAtmConfig
|
||||
result instanceof NosqlInjectionAtm::NosqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionATM::SqlInjectionAtmConfig
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::SqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathATM::TaintedPathAtmConfig
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::TaintedPathAtmConfig
|
||||
or
|
||||
query instanceof XssQuery and result instanceof XssATM::DomBasedXssAtmConfig
|
||||
query instanceof XssQuery and result instanceof XssAtm::DomBasedXssAtmConfig
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getAtmCfg */
|
||||
@@ -39,13 +55,13 @@ deprecated ATMConfig getATMCfg(Query query) { result = getAtmCfg(query) }
|
||||
|
||||
/** Gets the ATM data flow configuration for the specified query. */
|
||||
DataFlow::Configuration getDataFlowCfg(Query query) {
|
||||
query instanceof NosqlInjectionQuery and result instanceof NosqlInjectionATM::Configuration
|
||||
query instanceof NosqlInjectionQuery and result instanceof NosqlInjectionAtm::Configuration
|
||||
or
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionATM::Configuration
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::Configuration
|
||||
or
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathATM::Configuration
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::Configuration
|
||||
or
|
||||
query instanceof XssQuery and result instanceof XssATM::Configuration
|
||||
query instanceof XssQuery and result instanceof XssAtm::Configuration
|
||||
}
|
||||
|
||||
/** Gets a known sink for the specified query. */
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
* Maps ML-powered queries to their `EndpointType` for clearer labelling while evaluating ML model during training.
|
||||
*/
|
||||
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathATM
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssATM
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
import experimental.adaptivethreatmodeling.AdaptiveThreatModeling
|
||||
|
||||
from string queryName, AtmConfig c, EndpointType e
|
||||
where
|
||||
(
|
||||
queryName = "SqlInjection" and
|
||||
c instanceof SqlInjectionATM::SqlInjectionAtmConfig
|
||||
c instanceof SqlInjectionAtm::SqlInjectionAtmConfig
|
||||
or
|
||||
queryName = "NosqlInjection" and
|
||||
c instanceof NosqlInjectionATM::NosqlInjectionAtmConfig
|
||||
c instanceof NosqlInjectionAtm::NosqlInjectionAtmConfig
|
||||
or
|
||||
queryName = "TaintedPath" and
|
||||
c instanceof TaintedPathATM::TaintedPathAtmConfig
|
||||
c instanceof TaintedPathAtm::TaintedPathAtmConfig
|
||||
or
|
||||
queryName = "Xss" and c instanceof XssATM::DomBasedXssAtmConfig
|
||||
queryName = "Xss" and c instanceof XssAtm::DomBasedXssAtmConfig
|
||||
) and
|
||||
e = c.getASinkEndpointType()
|
||||
select queryName, e.getEncoding() as label
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: codeql/javascript-experimental-atm-queries
|
||||
language: javascript
|
||||
version: 0.3.2
|
||||
version: 0.3.4
|
||||
suites: codeql-suites
|
||||
defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls
|
||||
groups:
|
||||
|
||||
@@ -7,20 +7,20 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathATM
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures
|
||||
import experimental.adaptivethreatmodeling.StandardEndpointFilters as StandardEndpointFilters
|
||||
import extraction.NoFeaturizationRestrictionsConfig
|
||||
|
||||
query predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) {
|
||||
(
|
||||
not exists(NosqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(SqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(TaintedPathATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(XssATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
StandardEndpointFilters::isArgumentToModeledFunction(endpoint)
|
||||
) and
|
||||
EndpointFeatures::tokenFeatures(endpoint, featureName, featureValue)
|
||||
|
||||
@@ -17,31 +17,31 @@ import semmle.javascript.security.dataflow.SqlInjectionCustomizations
|
||||
import semmle.javascript.security.dataflow.TaintedPathCustomizations
|
||||
import semmle.javascript.security.dataflow.DomBasedXssCustomizations
|
||||
import experimental.adaptivethreatmodeling.StandardEndpointFilters as StandardEndpointFilters
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathATM
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
|
||||
query predicate nosqlFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof NosqlInjection::Sink and
|
||||
reason = NosqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
not reason = ["argument to modeled function", "modeled sink", "modeled database access"]
|
||||
}
|
||||
|
||||
query predicate sqlFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof SqlInjection::Sink and
|
||||
reason = SqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
query predicate taintedPathFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof TaintedPath::Sink and
|
||||
reason = TaintedPathATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
query predicate xssFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof DomBasedXss::Sink and
|
||||
reason = XssATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
|
||||
query predicate effectiveSinks(DataFlow::Node node) {
|
||||
not exists(NosqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(node))
|
||||
not exists(NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(node))
|
||||
}
|
||||
|
||||
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
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Added support for TypeScript 4.8.
|
||||
4
javascript/ql/lib/change-notes/2022-08-09-mermaid.md
Normal file
4
javascript/ql/lib/change-notes/2022-08-09-mermaid.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* A model for the `mermaid` library has been added. XSS queries can now detect flow through the `render` method of the `mermaid` library.
|
||||
5
javascript/ql/lib/change-notes/2022-09-12-uppercase.md
Normal file
5
javascript/ql/lib/change-notes/2022-09-12-uppercase.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
@@ -11,7 +11,7 @@ import javascript
|
||||
*/
|
||||
module Actions {
|
||||
/** A YAML node in a GitHub Actions workflow file. */
|
||||
private class Node extends YAMLNode {
|
||||
private class Node extends YamlNode {
|
||||
Node() {
|
||||
this.getLocation()
|
||||
.getFile()
|
||||
@@ -24,12 +24,12 @@ module Actions {
|
||||
* An Actions workflow. This is a mapping at the top level of an Actions YAML workflow file.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
|
||||
*/
|
||||
class Workflow extends Node, YAMLDocument, YAMLMapping {
|
||||
class Workflow extends Node, YamlDocument, YamlMapping {
|
||||
/** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */
|
||||
YAMLMapping getJobs() { result = this.lookup("jobs") }
|
||||
YamlMapping getJobs() { result = this.lookup("jobs") }
|
||||
|
||||
/** Gets the name of the workflow. */
|
||||
string getName() { result = this.lookup("name").(YAMLString).getValue() }
|
||||
string getName() { result = this.lookup("name").(YamlString).getValue() }
|
||||
|
||||
/** Gets the name of the workflow file. */
|
||||
string getFileName() { result = this.getFile().getBaseName() }
|
||||
@@ -45,7 +45,7 @@ module Actions {
|
||||
* An Actions On trigger within a workflow.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#on.
|
||||
*/
|
||||
class On extends YAMLNode, YAMLMappingLikeNode {
|
||||
class On extends YamlNode, YamlMappingLikeNode {
|
||||
Workflow workflow;
|
||||
|
||||
On() { workflow.lookup("on") = this }
|
||||
@@ -58,7 +58,7 @@ module Actions {
|
||||
* An Actions job within a workflow.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs.
|
||||
*/
|
||||
class Job extends YAMLNode, YAMLMapping {
|
||||
class Job extends YamlNode, YamlMapping {
|
||||
string jobId;
|
||||
Workflow workflow;
|
||||
|
||||
@@ -74,19 +74,19 @@ module Actions {
|
||||
* Gets the ID of this job, as a YAML scalar node.
|
||||
* This is the job's key within the `jobs` mapping.
|
||||
*/
|
||||
YAMLString getIdNode() { workflow.getJobs().maps(result, this) }
|
||||
YamlString getIdNode() { workflow.getJobs().maps(result, this) }
|
||||
|
||||
/** Gets the human-readable name of this job, if any, as a string. */
|
||||
string getName() { result = this.getNameNode().getValue() }
|
||||
|
||||
/** Gets the human-readable name of this job, if any, as a YAML scalar node. */
|
||||
YAMLString getNameNode() { result = this.lookup("name") }
|
||||
YamlString getNameNode() { result = this.lookup("name") }
|
||||
|
||||
/** Gets the step at the given index within this job. */
|
||||
Step getStep(int index) { result.getJob() = this and result.getIndex() = index }
|
||||
|
||||
/** Gets the sequence of `steps` within this job. */
|
||||
YAMLSequence getSteps() { result = this.lookup("steps") }
|
||||
YamlSequence getSteps() { result = this.lookup("steps") }
|
||||
|
||||
/** Gets the workflow this job belongs to. */
|
||||
Workflow getWorkflow() { result = workflow }
|
||||
@@ -99,7 +99,7 @@ module Actions {
|
||||
* An `if` within a job.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif.
|
||||
*/
|
||||
class JobIf extends YAMLNode, YAMLScalar {
|
||||
class JobIf extends YamlNode, YamlScalar {
|
||||
Job job;
|
||||
|
||||
JobIf() { job.lookup("if") = this }
|
||||
@@ -112,7 +112,7 @@ module Actions {
|
||||
* A step within an Actions job.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idsteps.
|
||||
*/
|
||||
class Step extends YAMLNode, YAMLMapping {
|
||||
class Step extends YamlNode, YamlMapping {
|
||||
int index;
|
||||
Job job;
|
||||
|
||||
@@ -134,14 +134,14 @@ module Actions {
|
||||
StepIf getIf() { result.getStep() = this }
|
||||
|
||||
/** Gets the ID of this step, if any. */
|
||||
string getId() { result = this.lookup("id").(YAMLString).getValue() }
|
||||
string getId() { result = this.lookup("id").(YamlString).getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `if` within a step.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsif.
|
||||
*/
|
||||
class StepIf extends YAMLNode, YAMLScalar {
|
||||
class StepIf extends YamlNode, YamlScalar {
|
||||
Step step;
|
||||
|
||||
StepIf() { step.lookup("if") = this }
|
||||
@@ -170,7 +170,7 @@ module Actions {
|
||||
*
|
||||
* Does not handle local repository references, e.g. `.github/actions/action-name`.
|
||||
*/
|
||||
class Uses extends YAMLNode, YAMLScalar {
|
||||
class Uses extends YamlNode, YamlScalar {
|
||||
Step step;
|
||||
|
||||
Uses() { step.lookup("uses") = this }
|
||||
@@ -200,7 +200,7 @@ module Actions {
|
||||
* arg2: abc
|
||||
* ```
|
||||
*/
|
||||
class With extends YAMLNode, YAMLMapping {
|
||||
class With extends YamlNode, YamlMapping {
|
||||
Step step;
|
||||
|
||||
With() { step.lookup("with") = this }
|
||||
@@ -219,7 +219,7 @@ module Actions {
|
||||
* ref: ${{ github.event.pull_request.head.sha }}
|
||||
* ```
|
||||
*/
|
||||
class Ref extends YAMLNode, YAMLString {
|
||||
class Ref extends YamlNode, YamlString {
|
||||
With with;
|
||||
|
||||
Ref() { with.lookup("ref") = this }
|
||||
@@ -232,7 +232,7 @@ module Actions {
|
||||
* A `run` field within an Actions job step, which runs command-line programs using an operating system shell.
|
||||
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
|
||||
*/
|
||||
class Run extends YAMLNode, YAMLString {
|
||||
class Run extends YamlNode, YamlString {
|
||||
Step step;
|
||||
|
||||
Run() { step.lookup("run") = this }
|
||||
|
||||
@@ -533,8 +533,9 @@ module API {
|
||||
|
||||
/** Gets a node corresponding to an import of module `m`. */
|
||||
Node moduleImport(string m) {
|
||||
result = Impl::MkModuleImport(m) or
|
||||
result = Impl::MkModuleImport(m).(Node).getMember("default")
|
||||
result = Internal::getAModuleImportRaw(m)
|
||||
or
|
||||
result = ModelOutput::getATypeNode(m, "")
|
||||
}
|
||||
|
||||
/** Gets a node corresponding to an export of module `m`. */
|
||||
@@ -544,6 +545,22 @@ module API {
|
||||
module Node {
|
||||
/** Gets a node whose type has the given qualified name. */
|
||||
Node ofType(string moduleName, string exportedName) {
|
||||
result = Internal::getANodeOfTypeRaw(moduleName, exportedName)
|
||||
or
|
||||
result = ModelOutput::getATypeNode(moduleName, exportedName)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides access to API graph nodes without taking into account types from models. */
|
||||
module Internal {
|
||||
/** Gets a node corresponding to an import of module `m` without taking into account types from models. */
|
||||
Node getAModuleImportRaw(string m) {
|
||||
result = Impl::MkModuleImport(m) or
|
||||
result = Impl::MkModuleImport(m).(Node).getMember("default")
|
||||
}
|
||||
|
||||
/** Gets a node whose type has the given qualified name, not including types from models. */
|
||||
Node getANodeOfTypeRaw(string moduleName, string exportedName) {
|
||||
result = Impl::MkTypeUse(moduleName, exportedName).(Node).getInstance()
|
||||
}
|
||||
}
|
||||
@@ -635,7 +652,7 @@ module API {
|
||||
exports(m, _, _)
|
||||
or
|
||||
exists(NodeModule nm | nm = mod |
|
||||
exists(SSA::implicitInit([nm.getModuleVariable(), nm.getExportsVariable()]))
|
||||
exists(Ssa::implicitInit([nm.getModuleVariable(), nm.getExportsVariable()]))
|
||||
)
|
||||
)
|
||||
} or
|
||||
@@ -646,7 +663,14 @@ module API {
|
||||
or
|
||||
any(Type t).hasUnderlyingType(m, _)
|
||||
} or
|
||||
MkClassInstance(DataFlow::ClassNode cls) { cls = trackDefNode(_) and hasSemantics(cls) } or
|
||||
MkClassInstance(DataFlow::ClassNode cls) {
|
||||
hasSemantics(cls) and
|
||||
(
|
||||
cls = trackDefNode(_)
|
||||
or
|
||||
cls.getAnInstanceReference() = trackDefNode(_)
|
||||
)
|
||||
} or
|
||||
MkAsyncFuncResult(DataFlow::FunctionNode f) {
|
||||
f = trackDefNode(_) and f.getFunction().isAsync() and hasSemantics(f)
|
||||
} or
|
||||
@@ -738,16 +762,6 @@ module API {
|
||||
.getStaticMember(name, DataFlow::MemberKind::getter())
|
||||
.getAReturn()
|
||||
)
|
||||
or
|
||||
// If `new C()` escapes, generate edges to its instance members
|
||||
exists(DataFlow::ClassNode cls, string name |
|
||||
pred = cls.getAClassReference().getAnInstantiation() and
|
||||
lbl = Label::member(name)
|
||||
|
|
||||
rhs = cls.getInstanceMethod(name)
|
||||
or
|
||||
rhs = cls.getInstanceMember(name, DataFlow::MemberKind::getter()).getAReturn()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ClassNode cls, string name |
|
||||
@@ -1236,9 +1250,13 @@ module API {
|
||||
succ = MkUse(ref)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node rhs |
|
||||
rhs(pred, lbl, rhs) and
|
||||
exists(DataFlow::Node rhs | rhs(pred, lbl, rhs) |
|
||||
succ = MkDef(rhs)
|
||||
or
|
||||
exists(DataFlow::ClassNode cls |
|
||||
cls.getAnInstanceReference() = rhs and
|
||||
succ = MkClassInstance(cls)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node def |
|
||||
@@ -1477,7 +1495,7 @@ module API {
|
||||
/** Gets the EntryPoint associated with this label. */
|
||||
API::EntryPoint getEntryPoint() { result = e }
|
||||
|
||||
override string toString() { result = "getASuccessor(Label::entryPoint(\"" + e + "\"))" }
|
||||
override string toString() { result = "entryPoint(\"" + e + "\")" }
|
||||
}
|
||||
|
||||
/** A label that gets a promised value. */
|
||||
|
||||
@@ -493,6 +493,9 @@ class MemberDeclaration extends @property, Documentable {
|
||||
*/
|
||||
predicate isStatic() { is_static(this) }
|
||||
|
||||
/** Gets a boolean indicating if this member is static. */
|
||||
boolean getStaticAsBool() { if this.isStatic() then result = true else result = false }
|
||||
|
||||
/**
|
||||
* Holds if this member is abstract.
|
||||
*
|
||||
@@ -694,10 +697,10 @@ class MethodDeclaration extends MemberDeclaration {
|
||||
* the overload index is defined as if only one of them was concrete.
|
||||
*/
|
||||
int getOverloadIndex() {
|
||||
exists(ClassOrInterface type, string name |
|
||||
exists(ClassOrInterface type, string name, boolean static |
|
||||
this =
|
||||
rank[result + 1](MethodDeclaration method, int i |
|
||||
methodDeclaredInType(type, name, i, method)
|
||||
methodDeclaredInType(type, name, static, i, method)
|
||||
|
|
||||
method order by i
|
||||
)
|
||||
@@ -718,10 +721,11 @@ class MethodDeclaration extends MemberDeclaration {
|
||||
* Holds if the `index`th member of `type` is `method`, which has the given `name`.
|
||||
*/
|
||||
private predicate methodDeclaredInType(
|
||||
ClassOrInterface type, string name, int index, MethodDeclaration method
|
||||
ClassOrInterface type, string name, boolean static, int index, MethodDeclaration method
|
||||
) {
|
||||
not method instanceof ConstructorDeclaration and // distinguish methods named "constructor" from the constructor
|
||||
type.getMemberByIndex(index) = method and
|
||||
static = method.getStaticAsBool() and
|
||||
method.getName() = name
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -178,7 +178,7 @@ predicate isGeneratedFileName(File f) {
|
||||
predicate isGenerated(TopLevel tl) {
|
||||
tl.isMinified() or
|
||||
isBundle(tl) or
|
||||
tl instanceof GWTGeneratedTopLevel or
|
||||
tl instanceof GwtGeneratedTopLevel or
|
||||
tl instanceof DartGeneratedTopLevel or
|
||||
exists(GeneratedCodeMarkerComment gcmc | tl = gcmc.getTopLevel()) or
|
||||
hasManyInvocations(tl) or
|
||||
|
||||
@@ -43,7 +43,7 @@ class JsonStringifyCall extends DataFlow::CallNode {
|
||||
/**
|
||||
* A taint step through the [`json2csv`](https://www.npmjs.com/package/json2csv) library.
|
||||
*/
|
||||
class JSON2CSVTaintStep extends TaintTracking::SharedTaintStep {
|
||||
class Json2CsvTaintStep extends TaintTracking::SharedTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::CallNode call |
|
||||
call =
|
||||
@@ -59,6 +59,9 @@ class JSON2CSVTaintStep extends TaintTracking::SharedTaintStep {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for Json2CsvTaintStep */
|
||||
deprecated class JSON2CSVTaintStep = Json2CsvTaintStep;
|
||||
|
||||
/**
|
||||
* A step through the [`prettyjson`](https://www.npmjs.com/package/prettyjson) library.
|
||||
* This is not quite a `JSON.stringify` call, as it e.g. does not wrap keys in double quotes.
|
||||
|
||||
@@ -168,18 +168,24 @@ class PackageJson extends JsonObject {
|
||||
JsonArray getCPUs() { result = this.getPropValue("cpu") }
|
||||
|
||||
/** Gets a platform supported by this package. */
|
||||
string getWhitelistedCPU() {
|
||||
string getWhitelistedCpu() {
|
||||
result = this.getCPUs().getElementStringValue(_) and
|
||||
not result.matches("!%")
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getWhitelistedCpu */
|
||||
deprecated string getWhitelistedCPU() { result = this.getWhitelistedCpu() }
|
||||
|
||||
/** Gets a platform not supported by this package. */
|
||||
string getBlacklistedCPU() {
|
||||
string getBlacklistedCpu() {
|
||||
exists(string str | str = this.getCPUs().getElementStringValue(_) |
|
||||
result = str.regexpCapture("!(.*)", 1)
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getBlacklistedCpu */
|
||||
deprecated string getBlacklistedCPU() { result = this.getBlacklistedCpu() }
|
||||
|
||||
/** Holds if this package prefers to be installed globally. */
|
||||
predicate isPreferGlobal() { this.getPropValue("preferGlobal").(JsonBoolean).getValue() = "true" }
|
||||
|
||||
|
||||
@@ -90,10 +90,11 @@ bindingset[name]
|
||||
private string getStem(string name) { result = name.regexpCapture("(.+?)(?:\\.([^.]+))?", 1) }
|
||||
|
||||
/**
|
||||
* Gets the main module described by `pkg` with the given `priority`.
|
||||
* Gets a file that a main module from `pkg` exported as `mainPath` with the given `priority`.
|
||||
* `mainPath` is "." if it's the main module of the package.
|
||||
*/
|
||||
File resolveMainModule(PackageJson pkg, int priority) {
|
||||
exists(PathExpr main | main = MainModulePath::of(pkg, ".") |
|
||||
private File resolveMainPath(PackageJson pkg, string mainPath, int priority) {
|
||||
exists(PathExpr main | main = MainModulePath::of(pkg, mainPath) |
|
||||
result = main.resolve() and priority = 0
|
||||
or
|
||||
result = tryExtensions(main.resolve(), "index", priority)
|
||||
@@ -102,6 +103,26 @@ File resolveMainModule(PackageJson pkg, int priority) {
|
||||
exists(int n | n = main.getNumComponent() |
|
||||
result = tryExtensions(main.resolveUpTo(n - 1), getStem(main.getComponent(n - 1)), priority)
|
||||
)
|
||||
or
|
||||
// assuming the files get moved from one dir to another during compilation:
|
||||
not exists(main.resolve()) and // didn't resolve
|
||||
count(int i, string comp | comp = main.getComponent(i) and not comp = "." | i) = 2 and // is down one folder
|
||||
exists(Folder subFolder | subFolder = pkg.getFile().getParentContainer().getAFolder() |
|
||||
// is in one folder below the package.json, and has the right basename
|
||||
result =
|
||||
tryExtensions(subFolder, getStem(main.getComponent(main.getNumComponent() - 1)),
|
||||
priority - 999) // very high priority, to make sure everything else is tried first
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the main module described by `pkg` with the given `priority`.
|
||||
*/
|
||||
File resolveMainModule(PackageJson pkg, int priority) {
|
||||
exists(int subPriority, string mainPath |
|
||||
result = resolveMainPath(pkg, mainPath, subPriority) and
|
||||
if mainPath = "." then subPriority = priority else priority = subPriority + 1000
|
||||
)
|
||||
or
|
||||
exists(Folder folder, Folder child |
|
||||
|
||||
@@ -52,6 +52,16 @@ private DataFlow::Node getAValueExportedByPackage() {
|
||||
not isPrivateMethodDeclaration(result)
|
||||
)
|
||||
or
|
||||
// module.exports.foo = function () {
|
||||
// return new Foo(); // <- result
|
||||
// };
|
||||
exists(DataFlow::FunctionNode func, DataFlow::NewNode inst, DataFlow::ClassNode clz |
|
||||
func = getAValueExportedByPackage().getALocalSource() and inst = unique( | | func.getAReturn())
|
||||
|
|
||||
clz.getAnInstanceReference() = inst and
|
||||
result = clz.getAnInstanceMember(_)
|
||||
)
|
||||
or
|
||||
result = getAValueExportedByPackage().getALocalSource()
|
||||
or
|
||||
// Nested property reads.
|
||||
|
||||
@@ -64,8 +64,8 @@ private newtype TPrintAstNode =
|
||||
// JSON
|
||||
TJsonNode(JsonValue value) { shouldPrint(value, _) and not isNotNeeded(value) } or
|
||||
// YAML
|
||||
TYamlNode(YAMLNode n) { shouldPrint(n, _) and not isNotNeeded(n) } or
|
||||
TYamlMappingNode(YAMLMapping mapping, int i) {
|
||||
TYamlNode(YamlNode n) { shouldPrint(n, _) and not isNotNeeded(n) } or
|
||||
TYamlMappingNode(YamlMapping mapping, int i) {
|
||||
shouldPrint(mapping, _) and not isNotNeeded(mapping) and exists(mapping.getKeyNode(i))
|
||||
} or
|
||||
// HTML
|
||||
@@ -628,7 +628,7 @@ module PrintYaml {
|
||||
* A print node representing a YAML value in a .yml file.
|
||||
*/
|
||||
class YamlNodeNode extends PrintAstNode, TYamlNode {
|
||||
YAMLNode node;
|
||||
YamlNode node;
|
||||
|
||||
YamlNodeNode() { this = TYamlNode(node) }
|
||||
|
||||
@@ -639,10 +639,10 @@ module PrintYaml {
|
||||
/**
|
||||
* Gets the `YAMLNode` represented by this node.
|
||||
*/
|
||||
final YAMLNode getValue() { result = node }
|
||||
final YamlNode getValue() { result = node }
|
||||
|
||||
override PrintAstNode getChild(int childIndex) {
|
||||
exists(YAMLNode child | result.(YamlNodeNode).getValue() = child |
|
||||
exists(YamlNode child | result.(YamlNodeNode).getValue() = child |
|
||||
child = node.getChildNode(childIndex)
|
||||
)
|
||||
}
|
||||
@@ -657,7 +657,7 @@ module PrintYaml {
|
||||
* Each child of this node aggregates the key and value of a mapping.
|
||||
*/
|
||||
class YamlMappingNode extends YamlNodeNode {
|
||||
override YAMLMapping node;
|
||||
override YamlMapping node;
|
||||
|
||||
override PrintAstNode getChild(int childIndex) {
|
||||
exists(YamlMappingMapNode map | map = result | map.maps(node, childIndex))
|
||||
@@ -671,21 +671,21 @@ module PrintYaml {
|
||||
* A print node representing the `i`th mapping in `mapping`.
|
||||
*/
|
||||
class YamlMappingMapNode extends PrintAstNode, TYamlMappingNode {
|
||||
YAMLMapping mapping;
|
||||
YamlMapping mapping;
|
||||
int i;
|
||||
|
||||
YamlMappingMapNode() { this = TYamlMappingNode(mapping, i) }
|
||||
|
||||
override string toString() {
|
||||
result = "(Mapping " + i + ")" and not exists(mapping.getKeyNode(i).(YAMLScalar).getValue())
|
||||
result = "(Mapping " + i + ")" and not exists(mapping.getKeyNode(i).(YamlScalar).getValue())
|
||||
or
|
||||
result = "(Mapping " + i + ") " + mapping.getKeyNode(i).(YAMLScalar).getValue() + ":"
|
||||
result = "(Mapping " + i + ") " + mapping.getKeyNode(i).(YamlScalar).getValue() + ":"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this print node represents the `index`th mapping of `m`.
|
||||
*/
|
||||
predicate maps(YAMLMapping m, int index) {
|
||||
predicate maps(YamlMapping m, int index) {
|
||||
m = mapping and
|
||||
index = i
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ module RangeAnalysis {
|
||||
* the given increment/decrement expression.
|
||||
*/
|
||||
private DataFlow::Node updateExprResult(UpdateExpr expr) {
|
||||
result = DataFlow::ssaDefinitionNode(SSA::definition(expr))
|
||||
result = DataFlow::ssaDefinitionNode(Ssa::definition(expr))
|
||||
or
|
||||
expr.isPrefix() and
|
||||
result = expr.flow()
|
||||
@@ -113,7 +113,7 @@ module RangeAnalysis {
|
||||
* Gets a data flow node holding the result of the given componund assignment.
|
||||
*/
|
||||
private DataFlow::Node compoundAssignResult(CompoundAssignExpr expr) {
|
||||
result = DataFlow::ssaDefinitionNode(SSA::definition(expr))
|
||||
result = DataFlow::ssaDefinitionNode(Ssa::definition(expr))
|
||||
or
|
||||
result = expr.flow()
|
||||
}
|
||||
|
||||
@@ -1005,7 +1005,10 @@ module RegExpPatterns {
|
||||
* Gets a pattern that matches common top-level domain names in lower case.
|
||||
* DEPRECATED: use `getACommonTld` instead
|
||||
*/
|
||||
deprecated predicate commonTLD = getACommonTld/0;
|
||||
deprecated predicate commonTld = getACommonTld/0;
|
||||
|
||||
/** DEPRECATED: Alias for commonTld */
|
||||
deprecated predicate commonTLD = commonTld/0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -282,7 +282,7 @@ module Routing {
|
||||
* Gets an HTTP method name which this node will accept, or nothing if the node accepts all HTTP methods, not
|
||||
* taking into account the context from ancestors or children nodes.
|
||||
*/
|
||||
HTTP::RequestMethodName getOwnHttpMethod() { none() } // Overridden in subclass
|
||||
Http::RequestMethodName getOwnHttpMethod() { none() } // Overridden in subclass
|
||||
|
||||
private Node getAUseSiteInRouteSetup() {
|
||||
if this.getParent() instanceof RouteSetup
|
||||
@@ -383,7 +383,7 @@ module Routing {
|
||||
* Gets an HTTP request method name (in upper case) matched by this node, or nothing
|
||||
* if all HTTP request method names are accepted.
|
||||
*/
|
||||
HTTP::RequestMethodName getHttpMethod() { none() }
|
||||
Http::RequestMethodName getHttpMethod() { none() }
|
||||
}
|
||||
|
||||
private class ValueNodeImpl extends Node, MkValueNode {
|
||||
@@ -407,7 +407,7 @@ module Routing {
|
||||
|
||||
override string getRelativePath() { result = range.getRelativePath() }
|
||||
|
||||
override HTTP::RequestMethodName getOwnHttpMethod() { result = range.getHttpMethod() }
|
||||
override Http::RequestMethodName getOwnHttpMethod() { result = range.getHttpMethod() }
|
||||
}
|
||||
|
||||
private StepSummary routeStepSummary() {
|
||||
@@ -434,7 +434,7 @@ module Routing {
|
||||
or
|
||||
StepSummary::smallstep(result, this, routeStepSummary())
|
||||
or
|
||||
HTTP::routeHandlerStep(result, this)
|
||||
Http::routeHandlerStep(result, this)
|
||||
or
|
||||
RouteHandlerTrackingStep::step(result, this)
|
||||
or
|
||||
@@ -599,7 +599,7 @@ module Routing {
|
||||
* Gets an HTTP request method name (in upper case) matched by this node, or nothing
|
||||
* if all HTTP request method names are accepted.
|
||||
*/
|
||||
HTTP::RequestMethodName getHttpMethod() { none() }
|
||||
Http::RequestMethodName getHttpMethod() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this route setup targets `router` and occurs at the given `cfgNode`.
|
||||
@@ -635,7 +635,7 @@ module Routing {
|
||||
|
||||
override string getRelativePath() { result = range.getRelativePath() }
|
||||
|
||||
override HTTP::RequestMethodName getOwnHttpMethod() { result = range.getHttpMethod() }
|
||||
override Http::RequestMethodName getOwnHttpMethod() { result = range.getHttpMethod() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -737,7 +737,7 @@ class SsaRefinementNode extends SsaPseudoDefinition, TRefinement {
|
||||
}
|
||||
}
|
||||
|
||||
module SSA {
|
||||
module Ssa {
|
||||
/** Gets the SSA definition corresponding to the implicit initialization of `v`. */
|
||||
SsaImplicitInit implicitInit(SsaSourceVariable v) { result.getSourceVariable() = v }
|
||||
|
||||
@@ -747,3 +747,6 @@ module SSA {
|
||||
/** Gets the SSA variable corresponding to `d`. */
|
||||
SsaVariable variable(VarDef d) { result.getDefinition() = definition(d) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for Ssa */
|
||||
deprecated module SSA = Ssa;
|
||||
|
||||
@@ -291,10 +291,13 @@ class StrictModeDecl extends KnownDirective {
|
||||
* "use asm";
|
||||
* ```
|
||||
*/
|
||||
class ASMJSDirective extends KnownDirective {
|
||||
ASMJSDirective() { this.getDirectiveText() = "use asm" }
|
||||
class AsmJSDirective extends KnownDirective {
|
||||
AsmJSDirective() { this.getDirectiveText() = "use asm" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for AsmJSDirective */
|
||||
deprecated class ASMJSDirective = AsmJSDirective;
|
||||
|
||||
/**
|
||||
* A Babel directive.
|
||||
*
|
||||
|
||||
@@ -20,13 +20,13 @@ import javascript
|
||||
* << : *DEFAULTS # an alias node referring to anchor `DEFAULTS`
|
||||
* ```
|
||||
*/
|
||||
class YAMLNode extends @yaml_node, Locatable {
|
||||
class YamlNode extends @yaml_node, Locatable {
|
||||
override Location getLocation() { yaml_locations(this, result) }
|
||||
|
||||
/**
|
||||
* Gets the parent node of this node, which is always a collection.
|
||||
*/
|
||||
YAMLCollection getParentNode() { yaml(this, _, result, _, _, _) }
|
||||
YamlCollection getParentNode() { yaml(this, _, result, _, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th child node of this node.
|
||||
@@ -34,12 +34,12 @@ class YAMLNode extends @yaml_node, Locatable {
|
||||
* _Note_: The index of a child node relative to its parent is considered
|
||||
* an implementation detail and may change between versions of the extractor.
|
||||
*/
|
||||
YAMLNode getChildNode(int i) { yaml(result, _, this, i, _, _) }
|
||||
YamlNode getChildNode(int i) { yaml(result, _, this, i, _, _) }
|
||||
|
||||
/**
|
||||
* Gets a child node of this node.
|
||||
*/
|
||||
YAMLNode getAChildNode() { result = this.getChildNode(_) }
|
||||
YamlNode getAChildNode() { result = this.getChildNode(_) }
|
||||
|
||||
/**
|
||||
* Gets the number of child nodes of this node.
|
||||
@@ -49,12 +49,12 @@ class YAMLNode extends @yaml_node, Locatable {
|
||||
/**
|
||||
* Gets the `i`th child of this node, as a YAML value.
|
||||
*/
|
||||
YAMLValue getChild(int i) { result = this.getChildNode(i).eval() }
|
||||
YamlValue getChild(int i) { result = this.getChildNode(i).eval() }
|
||||
|
||||
/**
|
||||
* Gets a child of this node, as a YAML value.
|
||||
*/
|
||||
YAMLValue getAChild() { result = this.getChild(_) }
|
||||
YamlValue getAChild() { result = this.getChild(_) }
|
||||
|
||||
/**
|
||||
* Gets the tag of this node.
|
||||
@@ -79,16 +79,19 @@ class YAMLNode extends @yaml_node, Locatable {
|
||||
/**
|
||||
* Gets the toplevel document to which this node belongs.
|
||||
*/
|
||||
YAMLDocument getDocument() { result = this.getParentNode*() }
|
||||
YamlDocument getDocument() { result = this.getParentNode*() }
|
||||
|
||||
/**
|
||||
* Gets the YAML value this node corresponds to after resolving aliases and includes.
|
||||
*/
|
||||
YAMLValue eval() { result = this }
|
||||
YamlValue eval() { result = this }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YAMLNode" }
|
||||
override string getAPrimaryQlClass() { result = "YamlNode" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlNode */
|
||||
deprecated class YAMLNode = YamlNode;
|
||||
|
||||
/**
|
||||
* A YAML value; that is, either a scalar or a collection.
|
||||
*
|
||||
@@ -102,7 +105,10 @@ class YAMLNode extends @yaml_node, Locatable {
|
||||
* - sequence
|
||||
* ```
|
||||
*/
|
||||
abstract class YAMLValue extends YAMLNode { }
|
||||
abstract class YamlValue extends YamlNode { }
|
||||
|
||||
/** DEPRECATED: Alias for YamlValue */
|
||||
deprecated class YAMLValue = YamlValue;
|
||||
|
||||
/**
|
||||
* A YAML scalar.
|
||||
@@ -118,7 +124,7 @@ abstract class YAMLValue extends YAMLNode { }
|
||||
* "hello"
|
||||
* ```
|
||||
*/
|
||||
class YAMLScalar extends YAMLValue, @yaml_scalar_node {
|
||||
class YamlScalar extends YamlValue, @yaml_scalar_node {
|
||||
/**
|
||||
* Gets the style of this scalar, which is one of the following:
|
||||
*
|
||||
@@ -147,9 +153,12 @@ class YAMLScalar extends YAMLValue, @yaml_scalar_node {
|
||||
*/
|
||||
string getValue() { yaml_scalars(this, _, result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YAMLScalar" }
|
||||
override string getAPrimaryQlClass() { result = "YamlScalar" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlScalar */
|
||||
deprecated class YAMLScalar = YamlScalar;
|
||||
|
||||
/**
|
||||
* A YAML scalar representing an integer value.
|
||||
*
|
||||
@@ -160,8 +169,8 @@ class YAMLScalar extends YAMLValue, @yaml_scalar_node {
|
||||
* 0xffff
|
||||
* ```
|
||||
*/
|
||||
class YAMLInteger extends YAMLScalar {
|
||||
YAMLInteger() { this.hasStandardTypeTag("int") }
|
||||
class YamlInteger extends YamlScalar {
|
||||
YamlInteger() { this.hasStandardTypeTag("int") }
|
||||
|
||||
/**
|
||||
* Gets the value of this scalar, as an integer.
|
||||
@@ -169,6 +178,9 @@ class YAMLInteger extends YAMLScalar {
|
||||
int getIntValue() { result = this.getValue().toInt() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlInteger */
|
||||
deprecated class YAMLInteger = YamlInteger;
|
||||
|
||||
/**
|
||||
* A YAML scalar representing a floating point value.
|
||||
*
|
||||
@@ -179,8 +191,8 @@ class YAMLInteger extends YAMLScalar {
|
||||
* 6.626e-34
|
||||
* ```
|
||||
*/
|
||||
class YAMLFloat extends YAMLScalar {
|
||||
YAMLFloat() { this.hasStandardTypeTag("float") }
|
||||
class YamlFloat extends YamlScalar {
|
||||
YamlFloat() { this.hasStandardTypeTag("float") }
|
||||
|
||||
/**
|
||||
* Gets the value of this scalar, as a floating point number.
|
||||
@@ -188,6 +200,9 @@ class YAMLFloat extends YAMLScalar {
|
||||
float getFloatValue() { result = this.getValue().toFloat() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlFloat */
|
||||
deprecated class YAMLFloat = YamlFloat;
|
||||
|
||||
/**
|
||||
* A YAML scalar representing a time stamp.
|
||||
*
|
||||
@@ -197,8 +212,8 @@ class YAMLFloat extends YAMLScalar {
|
||||
* 2001-12-15T02:59:43.1Z
|
||||
* ```
|
||||
*/
|
||||
class YAMLTimestamp extends YAMLScalar {
|
||||
YAMLTimestamp() { this.hasStandardTypeTag("timestamp") }
|
||||
class YamlTimestamp extends YamlScalar {
|
||||
YamlTimestamp() { this.hasStandardTypeTag("timestamp") }
|
||||
|
||||
/**
|
||||
* Gets the value of this scalar, as a date.
|
||||
@@ -206,6 +221,9 @@ class YAMLTimestamp extends YAMLScalar {
|
||||
date getDateValue() { result = this.getValue().toDate() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlTimestamp */
|
||||
deprecated class YAMLTimestamp = YamlTimestamp;
|
||||
|
||||
/**
|
||||
* A YAML scalar representing a Boolean value.
|
||||
*
|
||||
@@ -215,8 +233,8 @@ class YAMLTimestamp extends YAMLScalar {
|
||||
* true
|
||||
* ```
|
||||
*/
|
||||
class YAMLBool extends YAMLScalar {
|
||||
YAMLBool() { this.hasStandardTypeTag("bool") }
|
||||
class YamlBool extends YamlScalar {
|
||||
YamlBool() { this.hasStandardTypeTag("bool") }
|
||||
|
||||
/**
|
||||
* Gets the value of this scalar, as a Boolean.
|
||||
@@ -224,6 +242,9 @@ class YAMLBool extends YAMLScalar {
|
||||
boolean getBoolValue() { if this.getValue() = "true" then result = true else result = false }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlBool */
|
||||
deprecated class YAMLBool = YamlBool;
|
||||
|
||||
/**
|
||||
* A YAML scalar representing the null value.
|
||||
*
|
||||
@@ -233,10 +254,13 @@ class YAMLBool extends YAMLScalar {
|
||||
* null
|
||||
* ```
|
||||
*/
|
||||
class YAMLNull extends YAMLScalar {
|
||||
YAMLNull() { this.hasStandardTypeTag("null") }
|
||||
class YamlNull extends YamlScalar {
|
||||
YamlNull() { this.hasStandardTypeTag("null") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlNull */
|
||||
deprecated class YAMLNull = YamlNull;
|
||||
|
||||
/**
|
||||
* A YAML scalar representing a string value.
|
||||
*
|
||||
@@ -246,10 +270,13 @@ class YAMLNull extends YAMLScalar {
|
||||
* "hello"
|
||||
* ```
|
||||
*/
|
||||
class YAMLString extends YAMLScalar {
|
||||
YAMLString() { this.hasStandardTypeTag("str") }
|
||||
class YamlString extends YamlScalar {
|
||||
YamlString() { this.hasStandardTypeTag("str") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlString */
|
||||
deprecated class YAMLString = YamlString;
|
||||
|
||||
/**
|
||||
* A YAML scalar representing a merge key.
|
||||
*
|
||||
@@ -260,10 +287,13 @@ class YAMLString extends YAMLScalar {
|
||||
* << : *DEFAULTS # merge key
|
||||
* ```
|
||||
*/
|
||||
class YAMLMergeKey extends YAMLScalar {
|
||||
YAMLMergeKey() { this.hasStandardTypeTag("merge") }
|
||||
class YamlMergeKey extends YamlScalar {
|
||||
YamlMergeKey() { this.hasStandardTypeTag("merge") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlMergeKey */
|
||||
deprecated class YAMLMergeKey = YamlMergeKey;
|
||||
|
||||
/**
|
||||
* A YAML scalar representing an `!include` directive.
|
||||
*
|
||||
@@ -271,11 +301,11 @@ class YAMLMergeKey extends YAMLScalar {
|
||||
* !include common.yaml
|
||||
* ```
|
||||
*/
|
||||
class YAMLInclude extends YAMLScalar {
|
||||
YAMLInclude() { this.getTag() = "!include" }
|
||||
class YamlInclude extends YamlScalar {
|
||||
YamlInclude() { this.getTag() = "!include" }
|
||||
|
||||
override YAMLValue eval() {
|
||||
exists(YAMLDocument targetDoc |
|
||||
override YamlValue eval() {
|
||||
exists(YamlDocument targetDoc |
|
||||
targetDoc.getFile().getAbsolutePath() = this.getTargetPath() and
|
||||
result = targetDoc.eval()
|
||||
)
|
||||
@@ -293,6 +323,9 @@ class YAMLInclude extends YAMLScalar {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlInclude */
|
||||
deprecated class YAMLInclude = YamlInclude;
|
||||
|
||||
/**
|
||||
* A YAML collection, that is, either a mapping or a sequence.
|
||||
*
|
||||
@@ -310,10 +343,13 @@ class YAMLInclude extends YAMLScalar {
|
||||
* - -blue
|
||||
* ```
|
||||
*/
|
||||
class YAMLCollection extends YAMLValue, @yaml_collection_node {
|
||||
override string getAPrimaryQlClass() { result = "YAMLCollection" }
|
||||
class YamlCollection extends YamlValue, @yaml_collection_node {
|
||||
override string getAPrimaryQlClass() { result = "YamlCollection" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlCollection */
|
||||
deprecated class YAMLCollection = YamlCollection;
|
||||
|
||||
/**
|
||||
* A YAML mapping.
|
||||
*
|
||||
@@ -324,11 +360,11 @@ class YAMLCollection extends YAMLValue, @yaml_collection_node {
|
||||
* y: 1
|
||||
* ```
|
||||
*/
|
||||
class YAMLMapping extends YAMLCollection, @yaml_mapping_node {
|
||||
class YamlMapping extends YamlCollection, @yaml_mapping_node {
|
||||
/**
|
||||
* Gets the `i`th key of this mapping.
|
||||
*/
|
||||
YAMLNode getKeyNode(int i) {
|
||||
YamlNode getKeyNode(int i) {
|
||||
i >= 0 and
|
||||
exists(int j | i = j - 1 and result = this.getChildNode(j))
|
||||
}
|
||||
@@ -336,7 +372,7 @@ class YAMLMapping extends YAMLCollection, @yaml_mapping_node {
|
||||
/**
|
||||
* Gets the `i`th value of this mapping.
|
||||
*/
|
||||
YAMLNode getValueNode(int i) {
|
||||
YamlNode getValueNode(int i) {
|
||||
i >= 0 and
|
||||
exists(int j | i = -j - 1 and result = this.getChildNode(j))
|
||||
}
|
||||
@@ -344,30 +380,33 @@ class YAMLMapping extends YAMLCollection, @yaml_mapping_node {
|
||||
/**
|
||||
* Gets the `i`th key of this mapping, as a YAML value.
|
||||
*/
|
||||
YAMLValue getKey(int i) { result = this.getKeyNode(i).eval() }
|
||||
YamlValue getKey(int i) { result = this.getKeyNode(i).eval() }
|
||||
|
||||
/**
|
||||
* Gets the `i`th value of this mapping, as a YAML value.
|
||||
*/
|
||||
YAMLValue getValue(int i) { result = this.getValueNode(i).eval() }
|
||||
YamlValue getValue(int i) { result = this.getValueNode(i).eval() }
|
||||
|
||||
/**
|
||||
* Holds if this mapping maps `key` to `value`.
|
||||
*/
|
||||
predicate maps(YAMLValue key, YAMLValue value) {
|
||||
predicate maps(YamlValue key, YamlValue value) {
|
||||
exists(int i | key = this.getKey(i) and value = this.getValue(i))
|
||||
or
|
||||
exists(YAMLMergeKey merge, YAMLMapping that | this.maps(merge, that) | that.maps(key, value))
|
||||
exists(YamlMergeKey merge, YamlMapping that | this.maps(merge, that) | that.maps(key, value))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value that this mapping maps `key` to.
|
||||
*/
|
||||
YAMLValue lookup(string key) { exists(YAMLScalar s | s.getValue() = key | this.maps(s, result)) }
|
||||
YamlValue lookup(string key) { exists(YamlScalar s | s.getValue() = key | this.maps(s, result)) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YAMLMapping" }
|
||||
override string getAPrimaryQlClass() { result = "YamlMapping" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlMapping */
|
||||
deprecated class YAMLMapping = YamlMapping;
|
||||
|
||||
/**
|
||||
* A YAML sequence.
|
||||
*
|
||||
@@ -379,20 +418,23 @@ class YAMLMapping extends YAMLCollection, @yaml_mapping_node {
|
||||
* - blue
|
||||
* ```
|
||||
*/
|
||||
class YAMLSequence extends YAMLCollection, @yaml_sequence_node {
|
||||
class YamlSequence extends YamlCollection, @yaml_sequence_node {
|
||||
/**
|
||||
* Gets the `i`th element in this sequence.
|
||||
*/
|
||||
YAMLNode getElementNode(int i) { result = this.getChildNode(i) }
|
||||
YamlNode getElementNode(int i) { result = this.getChildNode(i) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th element in this sequence, as a YAML value.
|
||||
*/
|
||||
YAMLValue getElement(int i) { result = this.getElementNode(i).eval() }
|
||||
YamlValue getElement(int i) { result = this.getElementNode(i).eval() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YAMLSequence" }
|
||||
override string getAPrimaryQlClass() { result = "YamlSequence" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlSequence */
|
||||
deprecated class YAMLSequence = YamlSequence;
|
||||
|
||||
/**
|
||||
* A YAML alias node referring to a target anchor.
|
||||
*
|
||||
@@ -402,8 +444,8 @@ class YAMLSequence extends YAMLCollection, @yaml_sequence_node {
|
||||
* *DEFAULTS
|
||||
* ```
|
||||
*/
|
||||
class YAMLAliasNode extends YAMLNode, @yaml_alias_node {
|
||||
override YAMLValue eval() {
|
||||
class YamlAliasNode extends YamlNode, @yaml_alias_node {
|
||||
override YamlValue eval() {
|
||||
result.getAnchor() = this.getTarget() and
|
||||
result.getDocument() = this.getDocument()
|
||||
}
|
||||
@@ -413,9 +455,12 @@ class YAMLAliasNode extends YAMLNode, @yaml_alias_node {
|
||||
*/
|
||||
string getTarget() { yaml_aliases(this, result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YAMLAliasNode" }
|
||||
override string getAPrimaryQlClass() { result = "YamlAliasNode" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlAliasNode */
|
||||
deprecated class YAMLAliasNode = YamlAliasNode;
|
||||
|
||||
/**
|
||||
* A YAML document.
|
||||
*
|
||||
@@ -427,14 +472,17 @@ class YAMLAliasNode extends YAMLNode, @yaml_alias_node {
|
||||
* y: 1
|
||||
* ```
|
||||
*/
|
||||
class YAMLDocument extends YAMLNode {
|
||||
YAMLDocument() { not exists(this.getParentNode()) }
|
||||
class YamlDocument extends YamlNode {
|
||||
YamlDocument() { not exists(this.getParentNode()) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlDocument */
|
||||
deprecated class YAMLDocument = YamlDocument;
|
||||
|
||||
/**
|
||||
* An error message produced by the YAML parser while processing a YAML file.
|
||||
*/
|
||||
class YAMLParseError extends @yaml_error, Error {
|
||||
class YamlParseError extends @yaml_error, Error {
|
||||
override Location getLocation() { yaml_locations(this, result) }
|
||||
|
||||
override string getMessage() { yaml_errors(this, result) }
|
||||
@@ -442,6 +490,9 @@ class YAMLParseError extends @yaml_error, Error {
|
||||
override string toString() { result = this.getMessage() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlParseError */
|
||||
deprecated class YAMLParseError = YamlParseError;
|
||||
|
||||
/**
|
||||
* A YAML node that may contain sub-nodes that can be identified by a name.
|
||||
* I.e. a mapping, sequence, or scalar.
|
||||
@@ -464,30 +515,30 @@ class YAMLParseError extends @yaml_error, Error {
|
||||
*
|
||||
* are equivalent.
|
||||
*/
|
||||
class YAMLMappingLikeNode extends YAMLNode {
|
||||
YAMLMappingLikeNode() {
|
||||
this instanceof YAMLMapping
|
||||
class YamlMappingLikeNode extends YamlNode {
|
||||
YamlMappingLikeNode() {
|
||||
this instanceof YamlMapping
|
||||
or
|
||||
this instanceof YAMLSequence
|
||||
this instanceof YamlSequence
|
||||
or
|
||||
this instanceof YAMLScalar
|
||||
this instanceof YamlScalar
|
||||
}
|
||||
|
||||
/** Gets sub-name identified by `name`. */
|
||||
YAMLNode getNode(string name) {
|
||||
exists(YAMLMapping mapping |
|
||||
YamlNode getNode(string name) {
|
||||
exists(YamlMapping mapping |
|
||||
mapping = this and
|
||||
result = mapping.lookup(name)
|
||||
)
|
||||
or
|
||||
exists(YAMLSequence sequence, YAMLNode node |
|
||||
exists(YamlSequence sequence, YamlNode node |
|
||||
sequence = this and
|
||||
sequence.getAChildNode() = node and
|
||||
node.eval().toString() = name and
|
||||
result = node
|
||||
)
|
||||
or
|
||||
exists(YAMLScalar scalar |
|
||||
exists(YamlScalar scalar |
|
||||
scalar = this and
|
||||
scalar.getValue() = name and
|
||||
result = scalar
|
||||
@@ -496,19 +547,22 @@ class YAMLMappingLikeNode extends YAMLNode {
|
||||
|
||||
/** Gets the number of elements in this mapping or sequence. */
|
||||
int getElementCount() {
|
||||
exists(YAMLMapping mapping |
|
||||
exists(YamlMapping mapping |
|
||||
mapping = this and
|
||||
result = mapping.getNumChild() / 2
|
||||
)
|
||||
or
|
||||
exists(YAMLSequence sequence |
|
||||
exists(YamlSequence sequence |
|
||||
sequence = this and
|
||||
result = sequence.getNumChild()
|
||||
)
|
||||
or
|
||||
exists(YAMLScalar scalar |
|
||||
exists(YamlScalar scalar |
|
||||
scalar = this and
|
||||
result = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for YamlMappingLikeNode */
|
||||
deprecated class YAMLMappingLikeNode = YamlMappingLikeNode;
|
||||
|
||||
@@ -770,7 +770,7 @@ private class FlowStepThroughImport extends SharedFlowStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(ImportSpecifier specifier |
|
||||
pred = DataFlow::valueNode(specifier) and
|
||||
succ = DataFlow::ssaDefinitionNode(SSA::definition(specifier))
|
||||
succ = DataFlow::ssaDefinitionNode(Ssa::definition(specifier))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1777,7 +1777,7 @@ class MidPathNode extends PathNode, MkMidNode {
|
||||
SsaImplicitDefinition
|
||||
or
|
||||
// Skip SSA definition of parameter as its location coincides with the parameter node
|
||||
nd = DataFlow::ssaDefinitionNode(SSA::definition(any(SimpleParameter p)))
|
||||
nd = DataFlow::ssaDefinitionNode(Ssa::definition(any(SimpleParameter p)))
|
||||
or
|
||||
// Skip to the top of big left-leaning string concatenation trees.
|
||||
nd = any(AddExpr add).flow() and
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,11 @@ private import javascript
|
||||
private import semmle.javascript.dataflow.TypeTracking
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
/**
|
||||
* An alias for `SourceNode`.
|
||||
*/
|
||||
class LocalSourceNode = SourceNode;
|
||||
|
||||
/**
|
||||
* A source node for local data flow, that is, a node from which local data flow is tracked.
|
||||
*
|
||||
@@ -343,7 +348,7 @@ private class NodeModuleSourcesNodes extends SourceNode::Range {
|
||||
|
||||
NodeModuleSourcesNodes() {
|
||||
exists(NodeModule m |
|
||||
this = DataFlow::ssaDefinitionNode(SSA::implicitInit(v)) and
|
||||
this = DataFlow::ssaDefinitionNode(Ssa::implicitInit(v)) and
|
||||
v = [m.getModuleVariable(), m.getExportsVariable()]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
or
|
||||
// spreading a tainted object into an object literal gives a tainted object
|
||||
e.(ObjectExpr).getAProperty().(SpreadProperty).getInit().(SpreadElement).getOperand() = f
|
||||
or
|
||||
// spreading a tainted value into an array literal gives a tainted array
|
||||
e.(ArrayExpr).getAnElement().(SpreadElement).getOperand() = f
|
||||
)
|
||||
succ.(DataFlow::ObjectLiteralNode).getAComputedPropertyName() = pred
|
||||
or
|
||||
// spreading a tainted object into an object literal gives a tainted object
|
||||
succ.(DataFlow::ObjectLiteralNode).getASpreadProperty() = pred
|
||||
or
|
||||
// spreading a tainted value into an array literal gives a tainted array
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -988,7 +984,7 @@ module TaintTracking {
|
||||
*
|
||||
* `<contains>` is one of: `contains`, `has`, `hasOwnProperty`
|
||||
*
|
||||
* Note that the `includes` method is covered by `StringInclusionSanitizer`.
|
||||
* Note that the `includes` method is covered by `MembershipTestSanitizer`.
|
||||
*/
|
||||
class WhitelistContainmentCallSanitizer extends AdditionalSanitizerGuardNode,
|
||||
DataFlow::MethodCallNode {
|
||||
@@ -1175,7 +1171,7 @@ module TaintTracking {
|
||||
/**
|
||||
* A check of form `x.indexOf(y) > 0` or similar, which sanitizes `y` in the "then" branch.
|
||||
*
|
||||
* The more typical case of `x.indexOf(y) >= 0` is covered by `StringInclusionSanitizer`.
|
||||
* The more typical case of `x.indexOf(y) >= 0` is covered by `MembershipTestSanitizer`.
|
||||
*/
|
||||
class PositiveIndexOfSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
|
||||
MethodCallExpr indexOf;
|
||||
|
||||
@@ -89,6 +89,18 @@ module CallGraph {
|
||||
result = getAFunctionReference(outer, 0, t.continue()).getAnInvocation() and
|
||||
locallyReturnedFunction(outer, function)
|
||||
)
|
||||
or
|
||||
// dynamic dispatch to unknown property of an object
|
||||
exists(DataFlow::ObjectLiteralNode obj, DataFlow::PropRead read |
|
||||
getAFunctionReference(function, 0, t.continue()) = obj.getAPropertySource() and
|
||||
obj.getAPropertyRead() = read and
|
||||
not exists(read.getPropertyName()) and
|
||||
result = read and
|
||||
// there exists only local reads of the object, nothing else.
|
||||
forex(DataFlow::Node ref | ref = obj.getALocalUse() and exists(ref.asExpr()) |
|
||||
ref = [obj, any(DataFlow::PropRead r).getBase()]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate locallyReturnedFunction(
|
||||
|
||||
@@ -171,10 +171,10 @@ abstract class CallWithNonLocalAnalyzedReturnFlow extends DataFlow::AnalyzedValu
|
||||
/**
|
||||
* Flow analysis for the return value of IIFEs.
|
||||
*/
|
||||
private class IIFEWithAnalyzedReturnFlow extends CallWithAnalyzedReturnFlow {
|
||||
private class IifeWithAnalyzedReturnFlow extends CallWithAnalyzedReturnFlow {
|
||||
ImmediatelyInvokedFunctionExpr iife;
|
||||
|
||||
IIFEWithAnalyzedReturnFlow() { astNode = iife.getInvocation() }
|
||||
IifeWithAnalyzedReturnFlow() { astNode = iife.getInvocation() }
|
||||
|
||||
override AnalyzedFunction getACallee() { result = iife.analyze() }
|
||||
}
|
||||
|
||||
@@ -692,10 +692,10 @@ abstract private class CallWithAnalyzedParameters extends FunctionWithAnalyzedPa
|
||||
/**
|
||||
* Flow analysis for simple parameters of IIFEs.
|
||||
*/
|
||||
private class IIFEWithAnalyzedParameters extends CallWithAnalyzedParameters {
|
||||
private class IifeWithAnalyzedParameters extends CallWithAnalyzedParameters {
|
||||
ImmediatelyInvokedFunctionExpr iife;
|
||||
|
||||
IIFEWithAnalyzedParameters() {
|
||||
IifeWithAnalyzedParameters() {
|
||||
this = iife and
|
||||
iife.getInvocationKind() = "direct"
|
||||
}
|
||||
|
||||
@@ -276,15 +276,15 @@ class ExternalScriptDependency extends ScriptDependency, @xmlattribute {
|
||||
/**
|
||||
* A dependency on GWT indicated by a GWT header script.
|
||||
*/
|
||||
private class GWTDependency extends ScriptDependency {
|
||||
GWTDependency() { this instanceof GWTHeader }
|
||||
private class GwtDependency extends ScriptDependency {
|
||||
GwtDependency() { this instanceof GwtHeader }
|
||||
|
||||
override predicate info(string id, string v) {
|
||||
id = "gwt" and
|
||||
exists(GWTHeader h | h = this |
|
||||
v = h.getGWTVersion()
|
||||
exists(GwtHeader h | h = this |
|
||||
v = h.getGwtVersion()
|
||||
or
|
||||
not exists(h.getGWTVersion()) and v = "unknown"
|
||||
not exists(h.getGwtVersion()) and v = "unknown"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -904,8 +904,8 @@ private class SinonJS extends FrameworkLibraryWithGenericUrl, FrameworkLibraryWi
|
||||
/**
|
||||
* The TinyMCE framework.
|
||||
*/
|
||||
private class TinyMCE extends FrameworkLibraryWithGenericUrl {
|
||||
TinyMCE() { this = "tinymce" }
|
||||
private class TinyMce extends FrameworkLibraryWithGenericUrl {
|
||||
TinyMce() { this = "tinymce" }
|
||||
|
||||
override string getAnAlias() { result = "jquery.tinymce" or result = "tinymce.jquery" }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -112,7 +112,7 @@ module ClientRequest {
|
||||
/**
|
||||
* Gets the name of an HTTP request method, in all-lowercase.
|
||||
*/
|
||||
private string httpMethodName() { result = any(HTTP::RequestMethodName m).toLowerCase() }
|
||||
private string httpMethodName() { result = any(Http::RequestMethodName m).toLowerCase() }
|
||||
|
||||
/**
|
||||
* Gets a model of an instance of the `request` library, or one of
|
||||
@@ -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)
|
||||
@@ -113,9 +115,9 @@ module ConnectExpressShared {
|
||||
*
|
||||
* For example, this could be the function `function(req, res, next){...}`.
|
||||
*/
|
||||
class RouteHandlerCandidate extends HTTP::RouteHandlerCandidate {
|
||||
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"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,11 +368,11 @@ private class HttpCookieWrite extends CookieWrites::CookieWrite {
|
||||
string header;
|
||||
|
||||
HttpCookieWrite() {
|
||||
exists(HTTP::CookieDefinition setCookie |
|
||||
this.asExpr() = setCookie.getHeaderArgument() and
|
||||
exists(Http::CookieDefinition setCookie |
|
||||
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()
|
||||
)
|
||||
exists(DataFlow::SourceNode asmCrypto, string algorithmName |
|
||||
asmCrypto = DataFlow::globalVarRef("asmCrypto") and
|
||||
algorithm.matchesName(algorithmName) and
|
||||
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"
|
||||
)
|
||||
|
||||
@@ -112,7 +112,7 @@ module Electron {
|
||||
*/
|
||||
class ProcessSender extends Process {
|
||||
ProcessSender() {
|
||||
exists(IPCSendRegistration reg | reg.getEmitter() instanceof MainProcess |
|
||||
exists(IpcSendRegistration reg | reg.getEmitter() instanceof MainProcess |
|
||||
this = reg.getABoundCallbackParameter(1, 0).getAPropertyRead("sender")
|
||||
)
|
||||
}
|
||||
@@ -123,28 +123,31 @@ module Electron {
|
||||
* Does mostly the same as an EventEmitter event handler,
|
||||
* except that values can be returned through the `event.returnValue` property.
|
||||
*/
|
||||
class IPCSendRegistration extends EventRegistration::DefaultEventRegistration,
|
||||
class IpcSendRegistration extends EventRegistration::DefaultEventRegistration,
|
||||
DataFlow::MethodCallNode {
|
||||
override Process emitter;
|
||||
|
||||
IPCSendRegistration() { this = emitter.ref().getAMethodCall(EventEmitter::on()) }
|
||||
IpcSendRegistration() { this = emitter.ref().getAMethodCall(EventEmitter::on()) }
|
||||
|
||||
override DataFlow::Node getAReturnedValue() {
|
||||
result = this.getABoundCallbackParameter(1, 0).getAPropertyWrite("returnValue").getRhs()
|
||||
}
|
||||
|
||||
override IPCDispatch getAReturnDispatch() { result.getCalleeName() = "sendSync" }
|
||||
override IpcDispatch getAReturnDispatch() { result.getCalleeName() = "sendSync" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for IpcSendRegistration */
|
||||
deprecated class IPCSendRegistration = IpcSendRegistration;
|
||||
|
||||
/**
|
||||
* A dispatch of an IPC event.
|
||||
* An IPC event is sent from the renderer to the main process.
|
||||
* And a value can be returned through the `returnValue` property of the event (first parameter in the callback).
|
||||
*/
|
||||
class IPCDispatch extends EventDispatch::DefaultEventDispatch, DataFlow::InvokeNode {
|
||||
class IpcDispatch extends EventDispatch::DefaultEventDispatch, DataFlow::InvokeNode {
|
||||
override Process emitter;
|
||||
|
||||
IPCDispatch() {
|
||||
IpcDispatch() {
|
||||
exists(string methodName | methodName = "sendSync" or methodName = "send" |
|
||||
this = emitter.ref().getAMemberCall(methodName)
|
||||
)
|
||||
@@ -163,7 +166,7 @@ module Electron {
|
||||
/**
|
||||
* Gets a registration that this dispatch can send an event to.
|
||||
*/
|
||||
override IPCSendRegistration getAReceiver() {
|
||||
override IpcSendRegistration getAReceiver() {
|
||||
this.getEmitter() instanceof RendererProcess and
|
||||
result.getEmitter() instanceof MainProcess
|
||||
or
|
||||
@@ -171,6 +174,9 @@ module Electron {
|
||||
result.getEmitter() instanceof RendererProcess
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for IpcDispatch */
|
||||
deprecated class IPCDispatch = IpcDispatch;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,10 +13,13 @@ abstract class EmscriptenMarkerComment extends GeneratedCodeMarkerComment { }
|
||||
/**
|
||||
* An `EMSCRIPTEN_START_ASM` marker comment.
|
||||
*/
|
||||
class EmscriptenStartASMComment extends EmscriptenMarkerComment {
|
||||
EmscriptenStartASMComment() { getText().trim() = "EMSCRIPTEN_START_ASM" }
|
||||
class EmscriptenStartAsmComment extends EmscriptenMarkerComment {
|
||||
EmscriptenStartAsmComment() { getText().trim() = "EMSCRIPTEN_START_ASM" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for EmscriptenStartAsmComment */
|
||||
deprecated class EmscriptenStartASMComment = EmscriptenStartAsmComment;
|
||||
|
||||
/**
|
||||
* An `EMSCRIPTEN_START_FUNCS` marker comment.
|
||||
*/
|
||||
@@ -27,10 +30,13 @@ class EmscriptenStartFuncsComment extends EmscriptenMarkerComment {
|
||||
/**
|
||||
* An `EMSCRIPTEN_END_ASM` marker comment.
|
||||
*/
|
||||
class EmscriptenEndASMComment extends EmscriptenMarkerComment {
|
||||
EmscriptenEndASMComment() { getText().trim() = "EMSCRIPTEN_END_ASM" }
|
||||
class EmscriptenEndAsmComment extends EmscriptenMarkerComment {
|
||||
EmscriptenEndAsmComment() { getText().trim() = "EMSCRIPTEN_END_ASM" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for EmscriptenEndAsmComment */
|
||||
deprecated class EmscriptenEndASMComment = EmscriptenEndAsmComment;
|
||||
|
||||
/**
|
||||
* An `EMSCRIPTEN_END_FUNCS` marker comment.
|
||||
*/
|
||||
|
||||
@@ -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()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +70,7 @@ module Express {
|
||||
result = "param" or
|
||||
result = "all" or
|
||||
result = "use" or
|
||||
result = any(HTTP::RequestMethodName m).toLowerCase() or
|
||||
result = any(Http::RequestMethodName m).toLowerCase() or
|
||||
// deprecated methods
|
||||
result = "error" or
|
||||
result = "del"
|
||||
@@ -83,20 +79,20 @@ 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
|
||||
result = this.getArgument(0).getStringValue()
|
||||
}
|
||||
|
||||
override HTTP::RequestMethodName getHttpMethod() { result.toLowerCase() = this.getMethodName() }
|
||||
override Http::RequestMethodName getHttpMethod() { result.toLowerCase() = this.getMethodName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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,40 +180,60 @@ 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)
|
||||
|
|
||||
result = succ.backtrack(t2, t)
|
||||
or
|
||||
HTTP::routeHandlerStep(result, succ) and
|
||||
Http::routeHandlerStep(result, succ) and
|
||||
t = t2
|
||||
)
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* Has no result for `use`, `all`, or `param` calls.
|
||||
*/
|
||||
HTTP::RequestMethodName getRequestMethod() { result.toLowerCase() = this.getMethodName() }
|
||||
Http::RequestMethodName getRequestMethod() { result.toLowerCase() = this.getMethodName() }
|
||||
|
||||
/**
|
||||
* Holds if this registers a route for all request methods.
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -258,18 +285,62 @@ module Express {
|
||||
/**
|
||||
* The callback given to passport in PassportRouteSetup.
|
||||
*/
|
||||
private class PassportRouteHandler extends RouteHandler, HTTP::Servers::StandardRouteHandler,
|
||||
DataFlow::ValueNode {
|
||||
override Function astNode;
|
||||
|
||||
private class PassportRouteHandler extends RouteHandler, Http::Servers::StandardRouteHandler,
|
||||
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()
|
||||
@@ -398,52 +470,57 @@ module Express {
|
||||
* but support for other kinds of route handlers can be added by implementing
|
||||
* additional subclasses of this class.
|
||||
*/
|
||||
abstract class RouteHandler extends HTTP::RouteHandler {
|
||||
abstract class RouteHandler extends Http::RouteHandler {
|
||||
/**
|
||||
* Gets the parameter of kind `kind` of this route handler.
|
||||
*
|
||||
* `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;
|
||||
class StandardRouteHandler extends RouteHandler, Http::Servers::StandardRouteHandler,
|
||||
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",
|
||||
@@ -453,7 +530,7 @@ module Express {
|
||||
}
|
||||
|
||||
/** An Express response source. */
|
||||
abstract class ResponseSource extends HTTP::Servers::ResponseSource { }
|
||||
abstract class ResponseSource extends Http::Servers::ResponseSource { }
|
||||
|
||||
/**
|
||||
* An Express response source, that is, the response parameter of a
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,7 +561,7 @@ module Express {
|
||||
}
|
||||
|
||||
/** An Express request source. */
|
||||
abstract class RequestSource extends HTTP::Servers::RequestSource { }
|
||||
abstract class RequestSource extends Http::Servers::RequestSource { }
|
||||
|
||||
/**
|
||||
* An Express request source, that is, the request parameter of a
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -539,16 +632,16 @@ module Express {
|
||||
}
|
||||
|
||||
/** The input parameter to an `app.param()` route handler. */
|
||||
private class ParamHandlerInputAccess extends HTTP::RequestInputAccess {
|
||||
private class ParamHandlerInputAccess extends Http::RequestInputAccess {
|
||||
RouteHandler rh;
|
||||
|
||||
ParamHandlerInputAccess() {
|
||||
exists(RouteSetup setup | rh = setup.getARouteHandler() |
|
||||
this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter"))
|
||||
this = rh.getRouteHandlerParameter("parameter")
|
||||
)
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = rh }
|
||||
override Http::RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
override string getKind() { result = "parameter" }
|
||||
}
|
||||
@@ -582,7 +675,7 @@ module Express {
|
||||
/**
|
||||
* An access to a user-controlled Express request input.
|
||||
*/
|
||||
class RequestInputAccess extends HTTP::RequestInputAccess {
|
||||
class RequestInputAccess extends Http::RequestInputAccess {
|
||||
RequestSource request;
|
||||
string kind;
|
||||
|
||||
@@ -640,7 +733,7 @@ module Express {
|
||||
/**
|
||||
* An access to a header on an Express request.
|
||||
*/
|
||||
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
|
||||
private class RequestHeaderAccess extends Http::RequestHeaderAccess {
|
||||
RequestSource request;
|
||||
|
||||
RequestHeaderAccess() {
|
||||
@@ -669,40 +762,40 @@ module Express {
|
||||
/**
|
||||
* HTTP headers created by Express calls
|
||||
*/
|
||||
abstract private class ExplicitHeader extends HTTP::ExplicitHeaderDefinition { }
|
||||
abstract private class ExplicitHeader extends Http::ExplicitHeaderDefinition { }
|
||||
|
||||
/**
|
||||
* 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()) }
|
||||
abstract private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition {
|
||||
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,16 +848,16 @@ 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" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument passed to the `send` or `end` method of an HTTP response object.
|
||||
*/
|
||||
private class ResponseSendArgument extends HTTP::ResponseSendArgument {
|
||||
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() }
|
||||
}
|
||||
@@ -788,11 +881,11 @@ module Express {
|
||||
* An expression passed to the `render` method of an HTTP response object
|
||||
* as the value of a template variable.
|
||||
*/
|
||||
private class TemplateInput extends HTTP::ResponseBody {
|
||||
private class TemplateInput extends Http::ResponseBody {
|
||||
TemplateObjectInput obj;
|
||||
|
||||
TemplateInput() {
|
||||
obj.getALocalSource().(DataFlow::ObjectLiteralNode).hasPropertyWrite(_, this.flow())
|
||||
obj.getALocalSource().(DataFlow::ObjectLiteralNode).hasPropertyWrite(_, this)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = obj.getRouteHandler() }
|
||||
@@ -820,26 +913,24 @@ module Express {
|
||||
/**
|
||||
* An Express server application.
|
||||
*/
|
||||
private class Application extends HTTP::ServerDefinition {
|
||||
Application() { this = appCreation().asExpr() }
|
||||
private class Application extends Http::ServerDefinition {
|
||||
Application() { this = appCreation() }
|
||||
|
||||
/**
|
||||
* Gets a route handler of the application, regardless of nesting.
|
||||
*/
|
||||
override HTTP::RouteHandler getARouteHandler() {
|
||||
override Http::RouteHandler getARouteHandler() {
|
||||
result = this.(RouterDefinition).getASubRouter*().getARouteHandler()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,30 +943,25 @@ 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.
|
||||
*
|
||||
* Example: `fun` for `router1.use(fun)` or `router.use("/route", fun)`
|
||||
*/
|
||||
HTTP::RouteHandler getARouteHandler() {
|
||||
result.(DataFlow::SourceNode).flowsToExpr(this.getARouteSetup().getAnArgument())
|
||||
Http::RouteHandler getARouteHandler() {
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -958,15 +1044,15 @@ module Express {
|
||||
* A function that flows to a route setup.
|
||||
*/
|
||||
private class TrackedRouteHandlerCandidateWithSetup extends RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
RouteSetup routeSetup;
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,14 +1063,14 @@ module Express {
|
||||
* `router.post(handler)` where it is unknown if `router` is an
|
||||
* Express router.
|
||||
*/
|
||||
class RouteSetupCandidate extends HTTP::RouteSetupCandidate, DataFlow::MethodCallNode {
|
||||
class RouteSetupCandidate extends Http::RouteSetupCandidate, DataFlow::MethodCallNode {
|
||||
DataFlow::ValueNode routeHandlerArg;
|
||||
|
||||
RouteSetupCandidate() {
|
||||
exists(string methodName |
|
||||
methodName = "all" or
|
||||
methodName = "use" or
|
||||
methodName = any(HTTP::RequestMethodName m).toLowerCase()
|
||||
methodName = any(Http::RequestMethodName m).toLowerCase()
|
||||
|
|
||||
this.getMethodName() = methodName and
|
||||
exists(DataFlow::ValueNode arg | arg = this.getAnArgument() |
|
||||
|
||||
@@ -18,25 +18,25 @@ module ExpressLibraries {
|
||||
/**
|
||||
* A header produced by a route handler of the "x-frame-options" module.
|
||||
*/
|
||||
class XFrameOptionsRouteHandlerHeader extends HTTP::ImplicitHeaderDefinition {
|
||||
class XFrameOptionsRouteHandlerHeader extends Http::ImplicitHeaderDefinition {
|
||||
XFrameOptionsRouteHandlerHeader() { this instanceof XFrameOptionsRouteHandler }
|
||||
|
||||
override predicate defines(string headerName, string headerValue) {
|
||||
xFrameOptionsDefaultImplicitHeaderDefinition(headerName, headerValue)
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = this }
|
||||
override Http::RouteHandler getRouteHandler() { result = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route handler from the "x-frame-options" module.
|
||||
*/
|
||||
class XFrameOptionsRouteHandler extends HTTP::RouteHandler {
|
||||
class XFrameOptionsRouteHandler extends Http::RouteHandler {
|
||||
XFrameOptionsRouteHandler() {
|
||||
this = DataFlow::moduleImport("x-frame-options").getAnInvocation()
|
||||
}
|
||||
|
||||
override HTTP::HeaderDefinition getAResponseHeader(string name) {
|
||||
override Http::HeaderDefinition getAResponseHeader(string name) {
|
||||
name = this.(XFrameOptionsRouteHandlerHeader).getAHeaderName() and
|
||||
result = this
|
||||
}
|
||||
@@ -45,23 +45,23 @@ module ExpressLibraries {
|
||||
/**
|
||||
* A header produced by a route handler of the "frameguard" module.
|
||||
*/
|
||||
class FrameGuardRouteHandlerHeader extends HTTP::ImplicitHeaderDefinition {
|
||||
class FrameGuardRouteHandlerHeader extends Http::ImplicitHeaderDefinition {
|
||||
FrameGuardRouteHandlerHeader() { this instanceof FrameGuardRouteHandler }
|
||||
|
||||
override predicate defines(string headerName, string headerValue) {
|
||||
xFrameOptionsDefaultImplicitHeaderDefinition(headerName, headerValue)
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = this }
|
||||
override Http::RouteHandler getRouteHandler() { result = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route handler from the "frameguard" module.
|
||||
*/
|
||||
class FrameGuardRouteHandler extends HTTP::RouteHandler {
|
||||
class FrameGuardRouteHandler extends Http::RouteHandler {
|
||||
FrameGuardRouteHandler() { this = DataFlow::moduleImport("frameguard").getAnInvocation() }
|
||||
|
||||
override HTTP::HeaderDefinition getAResponseHeader(string name) {
|
||||
override Http::HeaderDefinition getAResponseHeader(string name) {
|
||||
name = this.(FrameGuardRouteHandlerHeader).getAHeaderName() and
|
||||
result = this
|
||||
}
|
||||
@@ -70,20 +70,20 @@ module ExpressLibraries {
|
||||
/**
|
||||
* A header produced by a route handler of the "helmet" module.
|
||||
*/
|
||||
class HelmetRouteHandlerHeader extends HTTP::ImplicitHeaderDefinition {
|
||||
class HelmetRouteHandlerHeader extends Http::ImplicitHeaderDefinition {
|
||||
HelmetRouteHandlerHeader() { this instanceof HelmetRouteHandler }
|
||||
|
||||
override predicate defines(string headerName, string headerValue) {
|
||||
xFrameOptionsDefaultImplicitHeaderDefinition(headerName, headerValue)
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = this }
|
||||
override Http::RouteHandler getRouteHandler() { result = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route handler from the "helmet" module.
|
||||
*/
|
||||
class HelmetRouteHandler extends HTTP::RouteHandler {
|
||||
class HelmetRouteHandler extends Http::RouteHandler {
|
||||
HelmetRouteHandler() {
|
||||
exists(DataFlow::ModuleImportNode m | "helmet" = m.getPath() |
|
||||
this = m.getAnInvocation() or
|
||||
@@ -91,7 +91,7 @@ module ExpressLibraries {
|
||||
)
|
||||
}
|
||||
|
||||
override HTTP::HeaderDefinition getAResponseHeader(string name) {
|
||||
override Http::HeaderDefinition getAResponseHeader(string name) {
|
||||
name = this.(HelmetRouteHandlerHeader).getAHeaderName() and
|
||||
result = this
|
||||
}
|
||||
@@ -108,7 +108,7 @@ module ExpressLibraries {
|
||||
/**
|
||||
* A call that creates an `express-session` middleware instance.
|
||||
*/
|
||||
class MiddlewareInstance extends DataFlow::InvokeNode, HTTP::CookieMiddlewareInstance {
|
||||
class MiddlewareInstance extends DataFlow::InvokeNode, Http::CookieMiddlewareInstance {
|
||||
MiddlewareInstance() { this = expressSession().getACall() }
|
||||
|
||||
/**
|
||||
@@ -135,7 +135,7 @@ module ExpressLibraries {
|
||||
/**
|
||||
* A call that creates a `cookie-parser` middleware instance.
|
||||
*/
|
||||
class MiddlewareInstance extends DataFlow::InvokeNode, HTTP::CookieMiddlewareInstance {
|
||||
class MiddlewareInstance extends DataFlow::InvokeNode, Http::CookieMiddlewareInstance {
|
||||
MiddlewareInstance() { this = cookieParser().getACall() }
|
||||
|
||||
/**
|
||||
@@ -164,7 +164,7 @@ module ExpressLibraries {
|
||||
/**
|
||||
* A call that creates a `cookie-session` middleware instance.
|
||||
*/
|
||||
class MiddlewareInstance extends DataFlow::InvokeNode, HTTP::CookieMiddlewareInstance {
|
||||
class MiddlewareInstance extends DataFlow::InvokeNode, Http::CookieMiddlewareInstance {
|
||||
MiddlewareInstance() { this = cookieSession().getACall() }
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,15 +12,13 @@ module Fastify {
|
||||
/**
|
||||
* An expression that creates a new Fastify server.
|
||||
*/
|
||||
abstract class ServerDefinition extends HTTP::Servers::StandardServerDefinition { }
|
||||
abstract class ServerDefinition extends Http::Servers::StandardServerDefinition { }
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
@@ -78,7 +76,7 @@ module Fastify {
|
||||
* but support for other kinds of route handlers can be added by implementing
|
||||
* additional subclasses of this class.
|
||||
*/
|
||||
abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode {
|
||||
abstract class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::ValueNode {
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
@@ -105,7 +103,7 @@ module Fastify {
|
||||
* A Fastify reply source, that is, the `reply` parameter of a
|
||||
* route handler.
|
||||
*/
|
||||
private class ReplySource extends HTTP::Servers::ResponseSource {
|
||||
private class ReplySource extends Http::Servers::ResponseSource {
|
||||
RouteHandler rh;
|
||||
|
||||
ReplySource() { this = rh.getReplyParameter() }
|
||||
@@ -120,7 +118,7 @@ module Fastify {
|
||||
* A Fastify request source, that is, the request parameter of a
|
||||
* route handler.
|
||||
*/
|
||||
private class RequestSource extends HTTP::Servers::RequestSource {
|
||||
private class RequestSource extends Http::Servers::RequestSource {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestSource() { this = rh.getRequestParameter() }
|
||||
@@ -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,31 +147,36 @@ 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 }
|
||||
|
||||
/** Gets an argument that represents a route handler being registered. */
|
||||
DataFlow::Node getARouteHandlerExpr() {
|
||||
/**
|
||||
* 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 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"
|
||||
}
|
||||
|
||||
override string getRelativePath() { result = this.getArgument(0).getStringValue() }
|
||||
|
||||
override HTTP::RequestMethodName getHttpMethod() { result = this.getMethodName().toUpperCase() }
|
||||
override Http::RequestMethodName getHttpMethod() { result = this.getMethodName().toUpperCase() }
|
||||
}
|
||||
|
||||
/** Gets the name of the `n`th handler function that can be installed a route setup, in order of execution. */
|
||||
@@ -185,13 +188,13 @@ module Fastify {
|
||||
|
||||
private class FullRoutingTreeSetup extends Routing::RouteSetup::MethodCall {
|
||||
FullRoutingTreeSetup() {
|
||||
this.asExpr() instanceof RouteSetup and
|
||||
this instanceof RouteSetup and
|
||||
this.getMethodName() = "route"
|
||||
}
|
||||
|
||||
override string getRelativePath() { result = this.getOptionArgument(0, "url").getStringValue() }
|
||||
|
||||
override HTTP::RequestMethodName getHttpMethod() {
|
||||
override Http::RequestMethodName getHttpMethod() {
|
||||
result = this.getOptionArgument(0, "method").getStringValue().toUpperCase()
|
||||
}
|
||||
|
||||
@@ -223,7 +226,7 @@ module Fastify {
|
||||
result = this.pluginBody(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
override HTTP::RequestMethodName getHttpMethod() {
|
||||
override Http::RequestMethodName getHttpMethod() {
|
||||
result = this.getOptionArgument(1, "method").getStringValue().toUpperCase()
|
||||
}
|
||||
|
||||
@@ -249,7 +252,7 @@ module Fastify {
|
||||
/**
|
||||
* An access to a user-controlled Fastify request input.
|
||||
*/
|
||||
private class RequestInputAccess extends HTTP::RequestInputAccess {
|
||||
private class RequestInputAccess extends Http::RequestInputAccess {
|
||||
RouteHandler rh;
|
||||
string kind;
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -317,7 +308,7 @@ module Fastify {
|
||||
/**
|
||||
* An access to a header on a Fastify request.
|
||||
*/
|
||||
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
|
||||
private class RequestHeaderAccess extends Http::RequestHeaderAccess {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestHeaderAccess() {
|
||||
@@ -336,13 +327,13 @@ module Fastify {
|
||||
/**
|
||||
* An argument passed to the `send` or `end` method of an HTTP response object.
|
||||
*/
|
||||
private class ResponseSendArgument extends HTTP::ResponseSendArgument {
|
||||
private class ResponseSendArgument extends Http::ResponseSendArgument {
|
||||
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 }
|
||||
}
|
||||
@@ -366,7 +355,7 @@ module Fastify {
|
||||
/**
|
||||
* An invocation that sets a single header of the HTTP response.
|
||||
*/
|
||||
private class SetOneHeader extends HTTP::Servers::StandardHeaderDefinition,
|
||||
private class SetOneHeader extends Http::Servers::StandardHeaderDefinition,
|
||||
DataFlow::MethodCallNode {
|
||||
RouteHandler rh;
|
||||
|
||||
@@ -381,7 +370,7 @@ module Fastify {
|
||||
/**
|
||||
* An invocation that sets any number of headers of the HTTP response.
|
||||
*/
|
||||
class SetMultipleHeaders extends HTTP::ExplicitHeaderDefinition, DataFlow::MethodCallNode {
|
||||
class SetMultipleHeaders extends Http::ExplicitHeaderDefinition, DataFlow::MethodCallNode {
|
||||
RouteHandler rh;
|
||||
|
||||
SetMultipleHeaders() {
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -425,7 +414,7 @@ module Fastify {
|
||||
override DataFlow::Node getTemplateParamsNode() { result = this.getArgument(1) }
|
||||
}
|
||||
|
||||
private class FastifyCookieMiddleware extends HTTP::CookieMiddlewareInstance {
|
||||
private class FastifyCookieMiddleware extends Http::CookieMiddlewareInstance {
|
||||
FastifyCookieMiddleware() {
|
||||
this = DataFlow::moduleImport(["fastify-cookie", "fastify-session", "fastify-secure-session"])
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
private class RouteHandler extends Express::RouteHandler, Http::Servers::StandardRouteHandler,
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,32 +7,44 @@ import javascript
|
||||
/**
|
||||
* A `$gwt_version` variable.
|
||||
*/
|
||||
class GWTVersionVariable extends GlobalVariable {
|
||||
GWTVersionVariable() { getName() = "$gwt_version" }
|
||||
class GwtVersionVariable extends GlobalVariable {
|
||||
GwtVersionVariable() { getName() = "$gwt_version" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for GwtVersionVariable */
|
||||
deprecated class GWTVersionVariable = GwtVersionVariable;
|
||||
|
||||
/**
|
||||
* A GWT header script that defines the `$gwt_version` variable.
|
||||
*/
|
||||
class GWTHeader extends InlineScript {
|
||||
GWTHeader() {
|
||||
exists(GWTVersionVariable gwtVersion | gwtVersion.getADeclaration().getTopLevel() = this)
|
||||
class GwtHeader extends InlineScript {
|
||||
GwtHeader() {
|
||||
exists(GwtVersionVariable gwtVersion | gwtVersion.getADeclaration().getTopLevel() = this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the GWT version this script was generated with, if it can be determined.
|
||||
*/
|
||||
string getGWTVersion() {
|
||||
string getGwtVersion() {
|
||||
exists(Expr e | e.getTopLevel() = this |
|
||||
e = any(GWTVersionVariable v).getAnAssignedExpr() and
|
||||
e = any(GwtVersionVariable v).getAnAssignedExpr() and
|
||||
result = e.getStringValue()
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getGwtVersion */
|
||||
deprecated string getGWTVersion() { result = getGwtVersion() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for GwtHeader */
|
||||
deprecated class GWTHeader = GwtHeader;
|
||||
|
||||
/**
|
||||
* A toplevel in a file that appears to be GWT-generated.
|
||||
*/
|
||||
class GWTGeneratedTopLevel extends TopLevel {
|
||||
GWTGeneratedTopLevel() { exists(GWTHeader h | getFile() = h.getFile()) }
|
||||
class GwtGeneratedTopLevel extends TopLevel {
|
||||
GwtGeneratedTopLevel() { exists(GwtHeader h | getFile() = h.getFile()) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for GwtGeneratedTopLevel */
|
||||
deprecated class GWTGeneratedTopLevel = GwtGeneratedTopLevel;
|
||||
|
||||
@@ -8,13 +8,13 @@ private import semmle.javascript.dataflow.internal.StepSummary
|
||||
private import semmle.javascript.dataflow.internal.CallGraphs
|
||||
private import DataFlow::PseudoProperties as PseudoProperties
|
||||
|
||||
module HTTP {
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +242,7 @@ module HTTP {
|
||||
DataFlow::functionOneWayForwardingStep(pred.getALocalUse(), succ)
|
||||
or
|
||||
// a container containing route-handlers.
|
||||
exists(HTTP::RouteHandlerCandidateContainer container | pred = container.getRouteHandler(succ))
|
||||
exists(Http::RouteHandlerCandidateContainer container | pred = container.getRouteHandler(succ))
|
||||
or
|
||||
// (function (req, res) {}).bind(this);
|
||||
exists(DataFlow::PartialInvokeNode call |
|
||||
@@ -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 request expression arising from a request source.
|
||||
*/
|
||||
deprecated class StandardRequestExpr extends RequestExpr {
|
||||
RequestSource src;
|
||||
|
||||
StandardRequestExpr() { src.ref().flowsToExpr(this) }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = src.getRouteHandler() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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::ValueNode {
|
||||
override MethodCallExpr astNode;
|
||||
|
||||
override predicate definesExplicitly(string headerName, Expr headerValue) {
|
||||
headerName = this.getNameExpr().getStringValue().toLowerCase() and
|
||||
headerValue = astNode.getArgument(1)
|
||||
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 Expr getNameExpr() { result = astNode.getArgument(0) }
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -610,7 +677,7 @@ module HTTP {
|
||||
/**
|
||||
* A collection that contains one or more route potential handlers.
|
||||
*/
|
||||
private class ContainerCollection extends HTTP::RouteHandlerCandidateContainer::Range,
|
||||
private class ContainerCollection extends Http::RouteHandlerCandidateContainer::Range,
|
||||
DataFlow::NewNode {
|
||||
ContainerCollection() {
|
||||
this = DataFlow::globalVarRef("Map").getAnInstantiation() and // restrict to Map for now
|
||||
@@ -632,3 +699,6 @@ module HTTP {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for Http */
|
||||
deprecated module HTTP = Http;
|
||||
|
||||
@@ -9,49 +9,44 @@ 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() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hapi response source, that is, an access to the `response` property
|
||||
* of a request object.
|
||||
*/
|
||||
private class ResponseSource extends HTTP::Servers::ResponseSource {
|
||||
RequestExpr req;
|
||||
private class ResponseSource extends Http::Servers::ResponseSource {
|
||||
RequestNode req;
|
||||
|
||||
ResponseSource() { asExpr().(PropAccess).accesses(req, "response") }
|
||||
ResponseSource() { this.(DataFlow::PropRead).accesses(req, "response") }
|
||||
|
||||
/**
|
||||
* Gets the route handler that provides this response.
|
||||
@@ -63,10 +58,10 @@ module Hapi {
|
||||
* A Hapi request source, that is, the request parameter of a
|
||||
* route handler.
|
||||
*/
|
||||
private class RequestSource extends HTTP::Servers::RequestSource {
|
||||
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,59 +70,75 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a user-controlled Hapi request input.
|
||||
*/
|
||||
private class RequestInputAccess extends HTTP::RequestInputAccess {
|
||||
private class RequestInputAccess extends Http::RequestInputAccess {
|
||||
RouteHandler rh;
|
||||
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
|
||||
@@ -145,15 +156,15 @@ module Hapi {
|
||||
/**
|
||||
* An access to an HTTP header on a Hapi request.
|
||||
*/
|
||||
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
|
||||
private class RequestHeaderAccess extends Http::RequestHeaderAccess {
|
||||
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, _)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -170,12 +181,12 @@ module Hapi {
|
||||
/**
|
||||
* An HTTP header defined in a Hapi server.
|
||||
*/
|
||||
private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition {
|
||||
ResponseExpr res;
|
||||
private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition {
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,7 +236,7 @@ module Hapi {
|
||||
*
|
||||
* For example, this could be the function `function(request, h){...}`.
|
||||
*/
|
||||
class RouteHandlerCandidate extends HTTP::RouteHandlerCandidate {
|
||||
class RouteHandlerCandidate extends Http::RouteHandlerCandidate {
|
||||
RouteHandlerCandidate() {
|
||||
exists(string request, string responseToolkit |
|
||||
(request = "request" or request = "req") and
|
||||
@@ -245,7 +256,7 @@ module Hapi {
|
||||
* A function that looks like a Hapi route handler and flows to a route setup.
|
||||
*/
|
||||
private class TrackedRouteHandlerCandidateWithSetup extends RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
TrackedRouteHandlerCandidateWithSetup() { this = any(RouteSetup s).getARouteHandler() }
|
||||
}
|
||||
|
||||
@@ -257,18 +268,18 @@ 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) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A return from a route handler.
|
||||
*/
|
||||
private class HandlerReturn extends HTTP::ResponseSendArgument {
|
||||
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,25 +9,25 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An HTTP header defined in a Koa application.
|
||||
*/
|
||||
private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition {
|
||||
private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition {
|
||||
RouteHandler rh;
|
||||
|
||||
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 }
|
||||
@@ -36,14 +36,21 @@ module Koa {
|
||||
/**
|
||||
* A Koa route handler.
|
||||
*/
|
||||
abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::SourceNode {
|
||||
abstract class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::SourceNode {
|
||||
/**
|
||||
* 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.
|
||||
@@ -221,7 +242,7 @@ module Koa {
|
||||
* A Koa request source, accessed through the a request property of a
|
||||
* generator route handler (deprecated in Koa 3).
|
||||
*/
|
||||
private class GeneratorRequestSource extends HTTP::Servers::RequestSource {
|
||||
private class GeneratorRequestSource extends Http::Servers::RequestSource {
|
||||
RouteHandler rh;
|
||||
|
||||
GeneratorRequestSource() {
|
||||
@@ -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,23 +301,39 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a user-controlled Koa request input.
|
||||
*/
|
||||
private class RequestInputAccess extends HTTP::RequestInputAccess {
|
||||
private class RequestInputAccess extends Http::RequestInputAccess {
|
||||
RouteHandler rh;
|
||||
string kind;
|
||||
|
||||
@@ -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,31 +390,31 @@ 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()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to an HTTP header on a Koa request.
|
||||
*/
|
||||
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
|
||||
private class RequestHeaderAccess extends Http::RequestHeaderAccess {
|
||||
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,37 +435,33 @@ 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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A value assigned to the body of an HTTP response object.
|
||||
*/
|
||||
private class ResponseSendArgument extends HTTP::ResponseSendArgument {
|
||||
private class ResponseSendArgument extends Http::ResponseSendArgument {
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ 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() }
|
||||
class ServerDefinition extends Http::Servers::StandardServerDefinition {
|
||||
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"])
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,32 @@ module Markdown {
|
||||
}
|
||||
}
|
||||
|
||||
/** A taint step for the `mermaid` library. */
|
||||
private class MermaidStep extends MarkdownStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::CallNode call |
|
||||
call =
|
||||
[API::moduleImport("mermaid"), API::moduleImport("mermaid").getMember("mermaidAPI")]
|
||||
.getMember("render")
|
||||
.getACall()
|
||||
|
|
||||
succ = [call, call.getParameter(2).getParameter(0).asSource()] and
|
||||
pred = call.getArgument(1)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::CallNode call |
|
||||
call =
|
||||
[
|
||||
DataFlow::globalVarRef("mermaid"),
|
||||
DataFlow::globalVarRef("mermaid").getAPropertyRead("mermaidAPI")
|
||||
].getAMemberCall("render")
|
||||
|
|
||||
succ = [call.(DataFlow::Node), call.getABoundCallbackParameter(2, 0)] and
|
||||
pred = call.getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates for modeling taint steps in `unified` and `remark`.
|
||||
*/
|
||||
|
||||
@@ -42,47 +42,55 @@ private module Micro {
|
||||
/**
|
||||
* A function passed to `micro` or `micro.run`.
|
||||
*/
|
||||
class MicroRouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
class MicroRouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
MicroRouteHandler() { this = microRouteHandler().getAFunctionValue() }
|
||||
}
|
||||
|
||||
class MicroRequestSource extends HTTP::Servers::RequestSource {
|
||||
class MicroRequestSource extends Http::Servers::RequestSource {
|
||||
MicroRouteHandler h;
|
||||
|
||||
MicroRequestSource() { this = h.getParameter(0) }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = h }
|
||||
override Http::RouteHandler getRouteHandler() { result = h }
|
||||
}
|
||||
|
||||
class MicroResponseSource extends HTTP::Servers::ResponseSource {
|
||||
class MicroResponseSource extends Http::Servers::ResponseSource {
|
||||
MicroRouteHandler h;
|
||||
|
||||
MicroResponseSource() { this = h.getParameter(1) }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = h }
|
||||
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;
|
||||
}
|
||||
|
||||
private HTTP::RouteHandler getRouteHandlerFromReqRes(DataFlow::Node node) {
|
||||
exists(HTTP::Servers::RequestSource src |
|
||||
class MicroResponseNode extends NodeJSLib::ResponseNode {
|
||||
override MicroResponseSource src;
|
||||
}
|
||||
|
||||
private Http::RouteHandler getRouteHandlerFromReqRes(DataFlow::Node node) {
|
||||
exists(Http::Servers::RequestSource src |
|
||||
src.ref().flowsTo(node) and
|
||||
result = src.getRouteHandler()
|
||||
)
|
||||
or
|
||||
exists(HTTP::Servers::ResponseSource src |
|
||||
exists(Http::Servers::ResponseSource src |
|
||||
src.ref().flowsTo(node) and
|
||||
result = src.getRouteHandler()
|
||||
)
|
||||
}
|
||||
|
||||
class MicroBodyParserCall extends HTTP::RequestInputAccess, DataFlow::CallNode {
|
||||
class MicroBodyParserCall extends Http::RequestInputAccess, DataFlow::CallNode {
|
||||
string name;
|
||||
|
||||
MicroBodyParserCall() {
|
||||
@@ -92,22 +100,22 @@ private module Micro {
|
||||
|
||||
override string getKind() { result = "body" }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() {
|
||||
override Http::RouteHandler getRouteHandler() {
|
||||
result = getRouteHandlerFromReqRes(getArgument(0))
|
||||
}
|
||||
|
||||
override predicate isUserControlledObject() { name = "json" }
|
||||
}
|
||||
|
||||
class MicroSendArgument extends HTTP::ResponseSendArgument {
|
||||
class MicroSendArgument extends Http::ResponseSendArgument {
|
||||
CallNode send;
|
||||
|
||||
MicroSendArgument() {
|
||||
send = moduleMember("micro", ["send", "sendError"]).getACall() and
|
||||
this = send.getLastArgument().asExpr()
|
||||
this = send.getLastArgument()
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() {
|
||||
override Http::RouteHandler getRouteHandler() {
|
||||
result = getRouteHandlerFromReqRes(send.getArgument([0, 1]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ module NestJS {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
private class NestJSRouteHandler extends HTTP::RouteHandler, DataFlow::FunctionNode {
|
||||
private class NestJSRouteHandler extends Http::RouteHandler, DataFlow::FunctionNode {
|
||||
NestJSRouteHandler() {
|
||||
getAFunctionDecorator(this) =
|
||||
nestjs()
|
||||
@@ -42,7 +42,7 @@ module NestJS {
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override HTTP::HeaderDefinition getAResponseHeader(string name) { none() }
|
||||
override Http::HeaderDefinition getAResponseHeader(string name) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this has the `@Redirect()` decorator.
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ module NestJS {
|
||||
* The type of remote flow depends on which decorator is applied at the parameter, so
|
||||
* we just classify it as a `RemoteFlowSource`.
|
||||
*/
|
||||
private class NestJSCustomPipeInput extends HTTP::RequestInputAccess {
|
||||
private class NestJSCustomPipeInput extends Http::RequestInputAccess {
|
||||
CustomPipeClass pipe;
|
||||
|
||||
NestJSCustomPipeInput() {
|
||||
@@ -273,7 +273,7 @@ module NestJS {
|
||||
result = pipe.getAnAffectedParameter().getInputKind()
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() {
|
||||
override Http::RouteHandler getRouteHandler() {
|
||||
result = pipe.getAnAffectedParameter().getNestRouteHandler()
|
||||
}
|
||||
}
|
||||
@@ -295,18 +295,18 @@ module NestJS {
|
||||
* as a source of untrusted data.
|
||||
*/
|
||||
private class NestJSRequestInputAsRequestInputAccess extends NestJSRequestInput,
|
||||
HTTP::RequestInputAccess {
|
||||
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
|
||||
@@ -316,7 +316,7 @@ module NestJS {
|
||||
}
|
||||
|
||||
private class NestJSHeaderAccess extends NestJSRequestInputAsRequestInputAccess,
|
||||
HTTP::RequestHeaderAccess {
|
||||
Http::RequestHeaderAccess {
|
||||
NestJSHeaderAccess() { decoratorName = "Headers" and decorator.getNumArgument() > 0 }
|
||||
|
||||
override string getAHeaderName() {
|
||||
@@ -344,20 +344,20 @@ module NestJS {
|
||||
* ```
|
||||
* writes `<b>Hello</b>` to the response.
|
||||
*/
|
||||
private class ReturnValueAsResponseSend extends HTTP::ResponseSendArgument {
|
||||
private class ReturnValueAsResponseSend extends Http::ResponseSendArgument {
|
||||
NestJSRouteHandler handler;
|
||||
|
||||
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())
|
||||
)
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = handler }
|
||||
override Http::RouteHandler getRouteHandler() { result = handler }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -439,7 +439,7 @@ module NestJS {
|
||||
/**
|
||||
* Gets the route handler that handles this request.
|
||||
*/
|
||||
override HTTP::RouteHandler getRouteHandler() {
|
||||
override Http::RouteHandler getRouteHandler() {
|
||||
result.(DataFlow::FunctionNode).getAParameter() = this
|
||||
}
|
||||
}
|
||||
@@ -456,7 +456,7 @@ module NestJS {
|
||||
/**
|
||||
* Gets the route handler that handles this request.
|
||||
*/
|
||||
override HTTP::RouteHandler getRouteHandler() {
|
||||
override Http::RouteHandler getRouteHandler() {
|
||||
result.(DataFlow::FunctionNode).getAParameter() = this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -154,14 +153,14 @@ module NextJS {
|
||||
/**
|
||||
* A Next.js function that is exected on the server for every request, seen as a routehandler.
|
||||
*/
|
||||
class NextHttpRouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
class NextHttpRouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
NextHttpRouteHandler() { this = getServerSidePropsFunction(_) or this = getInitialProps(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that handles both a request and response from Next.js, seen as a routehandler.
|
||||
*/
|
||||
class NextReqResHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
class NextReqResHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
DataFlow::ParameterNode req;
|
||||
DataFlow::ParameterNode res;
|
||||
|
||||
@@ -183,28 +182,28 @@ module NextJS {
|
||||
* A NodeJS HTTP request object in a Next.js page.
|
||||
*/
|
||||
class NextHttpRequestSource extends NodeJSLib::RequestSource {
|
||||
HTTP::RouteHandler rh;
|
||||
Http::RouteHandler rh;
|
||||
|
||||
NextHttpRequestSource() {
|
||||
this = rh.(NextHttpRouteHandler).getParameter(0).getAPropertyRead("req") or
|
||||
this = rh.(NextReqResHandler).getRequest()
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = rh }
|
||||
override Http::RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
* A NodeJS HTTP response object in a Next.js page.
|
||||
*/
|
||||
class NextHttpResponseSource extends NodeJSLib::ResponseSource {
|
||||
HTTP::RouteHandler rh;
|
||||
Http::RouteHandler rh;
|
||||
|
||||
NextHttpResponseSource() {
|
||||
this = rh.(NextHttpRouteHandler).getParameter(0).getAPropertyRead("res") or
|
||||
this = rh.(NextReqResHandler).getResponse()
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = rh }
|
||||
override Http::RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,17 +222,17 @@ module NextJS {
|
||||
* and we therefore model the routehandler as an Express.js routehandler.
|
||||
*/
|
||||
class NextApiRouteHandler extends DataFlow::FunctionNode, Express::RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler {
|
||||
Http::Servers::StandardRouteHandler {
|
||||
NextApiRouteHandler() {
|
||||
exists(Module mod | mod.getFile().getParentContainer() = apiFolder() |
|
||||
this = mod.getAnExportedValue("default").getAFunctionValue()
|
||||
)
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -87,16 +109,16 @@ module NodeJSLib {
|
||||
* but support for other kinds of route handlers can be added by implementing
|
||||
* additional subclasses of this class.
|
||||
*/
|
||||
abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
abstract class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
/**
|
||||
* 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) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +131,7 @@ module NodeJSLib {
|
||||
/**
|
||||
* A Node.js response source.
|
||||
*/
|
||||
abstract class ResponseSource extends HTTP::Servers::ResponseSource { }
|
||||
abstract class ResponseSource extends Http::Servers::ResponseSource { }
|
||||
|
||||
/**
|
||||
* A standard Node.js response source, that is, the response parameter of a
|
||||
@@ -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.
|
||||
@@ -129,7 +151,7 @@ module NodeJSLib {
|
||||
/**
|
||||
* A Node.js request source.
|
||||
*/
|
||||
abstract class RequestSource extends HTTP::Servers::RequestSource { }
|
||||
abstract class RequestSource extends Http::Servers::RequestSource { }
|
||||
|
||||
/**
|
||||
* A standard Node.js request source, that is, the request parameter of a
|
||||
@@ -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;
|
||||
private class RequestInputAccess extends Http::RequestInputAccess {
|
||||
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 |
|
||||
@@ -185,7 +223,7 @@ module NodeJSLib {
|
||||
)
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = request.getRouteHandler() }
|
||||
override Http::RouteHandler getRouteHandler() { result = request.getRouteHandler() }
|
||||
|
||||
override string getKind() { result = kind }
|
||||
}
|
||||
@@ -193,15 +231,15 @@ 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;
|
||||
private class RequestHeaderAccess extends Http::RequestHeaderAccess {
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -209,23 +247,23 @@ module NodeJSLib {
|
||||
result = this.(DataFlow::PropRead).getPropertyName().toLowerCase()
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = request.getRouteHandler() }
|
||||
override Http::RouteHandler getRouteHandler() { result = request.getRouteHandler() }
|
||||
|
||||
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)
|
||||
@@ -244,31 +282,37 @@ module NodeJSLib {
|
||||
result = succ.backtrack(t2, t)
|
||||
or
|
||||
t = t2 and
|
||||
HTTP::routeHandlerStep(result, succ)
|
||||
Http::routeHandlerStep(result, succ)
|
||||
)
|
||||
}
|
||||
|
||||
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;
|
||||
abstract private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition {
|
||||
ResponseNode r;
|
||||
|
||||
HeaderDefinition() { astNode.getReceiver() = r }
|
||||
HeaderDefinition() { this.getReceiver() = r }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = r.getRouteHandler() }
|
||||
override Http::RouteHandler getRouteHandler() { result = r.getRouteHandler() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
)
|
||||
}
|
||||
@@ -359,34 +403,33 @@ module NodeJSLib {
|
||||
* An expression passed as the first argument to the `write` or `end` method
|
||||
* of an HTTP response.
|
||||
*/
|
||||
private class ResponseSendArgument extends HTTP::ResponseSendArgument {
|
||||
HTTP::RouteHandler rh;
|
||||
private class ResponseSendArgument extends Http::ResponseSendArgument {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = rh }
|
||||
override Http::RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that creates a new Node.js server.
|
||||
*/
|
||||
class ServerDefinition extends HTTP::Servers::StandardServerDefinition {
|
||||
class ServerDefinition extends Http::Servers::StandardServerDefinition {
|
||||
ServerDefinition() { isCreateServer(this) }
|
||||
}
|
||||
|
||||
/** 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")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -777,7 +820,7 @@ module NodeJSLib {
|
||||
*
|
||||
* For example, this could be the function `function(req, res){...}`.
|
||||
*/
|
||||
class RouteHandlerCandidate extends HTTP::RouteHandlerCandidate {
|
||||
class RouteHandlerCandidate extends Http::RouteHandlerCandidate {
|
||||
RouteHandlerCandidate() {
|
||||
exists(string request, string response |
|
||||
(request = "request" or request = "req") and
|
||||
@@ -797,7 +840,7 @@ module NodeJSLib {
|
||||
* A function that flows to a route setup.
|
||||
*/
|
||||
private class TrackedRouteHandlerCandidateWithSetup extends RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
TrackedRouteHandlerCandidateWithSetup() { this = any(RouteSetup s).getARouteHandler() }
|
||||
}
|
||||
|
||||
@@ -828,7 +871,7 @@ module NodeJSLib {
|
||||
* For example, this could be the call `server.on("request", handler)`
|
||||
* where it is unknown if `server` is a Node.js server.
|
||||
*/
|
||||
class RouteSetupCandidate extends HTTP::RouteSetupCandidate, DataFlow::MethodCallNode {
|
||||
class RouteSetupCandidate extends Http::RouteSetupCandidate, DataFlow::MethodCallNode {
|
||||
DataFlow::ValueNode arg;
|
||||
|
||||
RouteSetupCandidate() {
|
||||
@@ -869,7 +912,7 @@ module NodeJSLib {
|
||||
exists(string moduleName, DataFlow::SourceNode callee | this = callee.getACall() |
|
||||
(moduleName = "http" or moduleName = "https") and
|
||||
(
|
||||
callee = DataFlow::moduleMember(moduleName, any(HTTP::RequestMethodName m).toLowerCase())
|
||||
callee = DataFlow::moduleMember(moduleName, any(Http::RequestMethodName m).toLowerCase())
|
||||
or
|
||||
callee = DataFlow::moduleMember(moduleName, "request")
|
||||
) and
|
||||
@@ -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() {
|
||||
@@ -17,12 +17,12 @@ module Request {
|
||||
action = mod.getAnInvocation()
|
||||
or
|
||||
// specialized form: `request.get(...)`
|
||||
action = mod.getAMemberCall(any(HTTP::RequestMethodName n).toLowerCase())
|
||||
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,17 +9,17 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Restify route handler.
|
||||
*/
|
||||
class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode {
|
||||
class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::ValueNode {
|
||||
Function function;
|
||||
|
||||
RouteHandler() {
|
||||
@@ -42,7 +42,7 @@ module Restify {
|
||||
* A Restify response source, that is, the response parameter of a
|
||||
* route handler.
|
||||
*/
|
||||
private class ResponseSource extends HTTP::Servers::ResponseSource {
|
||||
private class ResponseSource extends Http::Servers::ResponseSource {
|
||||
RouteHandler rh;
|
||||
|
||||
ResponseSource() { this = DataFlow::parameterNode(rh.getResponseParameter()) }
|
||||
@@ -57,7 +57,7 @@ module Restify {
|
||||
* A Restify request source, that is, the request parameter of a
|
||||
* route handler.
|
||||
*/
|
||||
private class RequestSource extends HTTP::Servers::RequestSource {
|
||||
private class RequestSource extends Http::Servers::RequestSource {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) }
|
||||
@@ -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;
|
||||
private class RequestInputAccess extends Http::RequestInputAccess {
|
||||
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() }
|
||||
@@ -125,31 +140,30 @@ module Restify {
|
||||
/**
|
||||
* An HTTP header defined in a Restify server.
|
||||
*/
|
||||
private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition {
|
||||
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
|
||||
|
||||
@@ -15,21 +15,21 @@ private module ServerLess {
|
||||
* `codeURI` defaults to the empty string if no explicit value is set in the configuration.
|
||||
*/
|
||||
private predicate hasServerlessHandler(File ymlFile, string handler, string codeUri) {
|
||||
exists(YAMLMapping resource | ymlFile = resource.getFile() |
|
||||
exists(YamlMapping resource | ymlFile = resource.getFile() |
|
||||
// There exists at least "AWS::Serverless::Function" and "Aliyun::Serverless::Function"
|
||||
resource.lookup("Type").(YAMLScalar).getValue().regexpMatch(".*::Serverless::Function") and
|
||||
exists(YAMLMapping properties | properties = resource.lookup("Properties") |
|
||||
handler = properties.lookup("Handler").(YAMLScalar).getValue() and
|
||||
resource.lookup("Type").(YamlScalar).getValue().regexpMatch(".*::Serverless::Function") and
|
||||
exists(YamlMapping properties | properties = resource.lookup("Properties") |
|
||||
handler = properties.lookup("Handler").(YamlScalar).getValue() and
|
||||
if exists(properties.lookup("CodeUri"))
|
||||
then codeUri = properties.lookup("CodeUri").(YAMLScalar).getValue()
|
||||
then codeUri = properties.lookup("CodeUri").(YamlScalar).getValue()
|
||||
else codeUri = ""
|
||||
)
|
||||
or
|
||||
// The `serverless` library, which specifies a top-level `functions` property
|
||||
exists(YAMLMapping functions |
|
||||
exists(YamlMapping functions |
|
||||
functions = resource.lookup("functions") and
|
||||
not exists(resource.getParentNode()) and
|
||||
handler = functions.getValue(_).(YAMLMapping).lookup("handler").(YAMLScalar).getValue() and
|
||||
handler = functions.getValue(_).(YamlMapping).lookup("handler").(YamlScalar).getValue() and
|
||||
codeUri = ""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -151,7 +151,7 @@ module Templating {
|
||||
|
||||
/** Gets the data flow node representing the initialization of the given variable in this scope. */
|
||||
DataFlow::Node getVariableInit(string name) {
|
||||
result = DataFlow::ssaDefinitionNode(SSA::implicitInit(this.getScope().getVariable(name)))
|
||||
result = DataFlow::ssaDefinitionNode(Ssa::implicitInit(this.getScope().getVariable(name)))
|
||||
}
|
||||
|
||||
/** Gets a data flow node corresponding to a use of the given template variable within this top-level. */
|
||||
|
||||
@@ -27,8 +27,8 @@ class QUnitTest extends Test, @call_expr {
|
||||
* that is, an invocation of a function named `it` where the first argument
|
||||
* is a string and the second argument is a function.
|
||||
*/
|
||||
class BDDTest extends Test, @call_expr {
|
||||
BDDTest() {
|
||||
class BddTest extends Test, @call_expr {
|
||||
BddTest() {
|
||||
exists(CallExpr call | call = this |
|
||||
call.getCallee().(VarAccess).getName() = "it" and
|
||||
exists(call.getArgument(0).getStringValue()) and
|
||||
@@ -37,6 +37,9 @@ class BDDTest extends Test, @call_expr {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for BddTest */
|
||||
deprecated class BDDTest = BddTest;
|
||||
|
||||
/**
|
||||
* Gets the test file for `f` with stem extension `stemExt`, where `stemExt` is "test" or "spec".
|
||||
* That is, a file named `<base>.<stemExt>.<ext>` in the
|
||||
|
||||
@@ -226,21 +226,21 @@ module ServerWebSocket {
|
||||
* A `socket.on("connection", (msg, req) => {})` call seen as a HTTP route handler.
|
||||
* `req` is a `HTTP::IncomingMessage` instance.
|
||||
*/
|
||||
class ConnectionCallAsRouteHandler extends HTTP::RouteHandler, DataFlow::CallNode {
|
||||
class ConnectionCallAsRouteHandler extends Http::RouteHandler, DataFlow::CallNode {
|
||||
ConnectionCallAsRouteHandler() { this = getAConnectionCall(_) }
|
||||
|
||||
override HTTP::HeaderDefinition getAResponseHeader(string name) { none() }
|
||||
override Http::HeaderDefinition getAResponseHeader(string name) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `req` parameter of a `socket.on("connection", (msg, req) => {})` call.
|
||||
*/
|
||||
class ServerHttpRequest extends HTTP::Servers::RequestSource {
|
||||
class ServerHttpRequest extends Http::Servers::RequestSource {
|
||||
ConnectionCallAsRouteHandler handler;
|
||||
|
||||
ServerHttpRequest() { this = handler.getCallback(1).getParameter(1) }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = handler }
|
||||
override Http::RouteHandler getRouteHandler() { result = handler }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ServerHttpRequest */
|
||||
@@ -249,7 +249,7 @@ module ServerWebSocket {
|
||||
/**
|
||||
* An access user-controlled HTTP request input in a request to a WebSocket server.
|
||||
*/
|
||||
class WebSocketRequestInput extends HTTP::RequestInputAccess {
|
||||
class WebSocketRequestInput extends Http::RequestInputAccess {
|
||||
ServerHttpRequest request;
|
||||
string kind;
|
||||
|
||||
@@ -267,7 +267,7 @@ module ServerWebSocket {
|
||||
|
||||
override string getKind() { result = kind }
|
||||
|
||||
override HTTP::RouteHandler getRouteHandler() { result = request.getRouteHandler() }
|
||||
override Http::RouteHandler getRouteHandler() { result = request.getRouteHandler() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,6 +72,8 @@ private class Unit = Specific::Unit;
|
||||
|
||||
private module API = Specific::API;
|
||||
|
||||
private module DataFlow = Specific::DataFlow;
|
||||
|
||||
private import Specific::AccessPathSyntax
|
||||
|
||||
/** Module containing hooks for providing input data to be interpreted as a model. */
|
||||
@@ -155,6 +157,38 @@ module ModelInput {
|
||||
*/
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional type model rows from CodeQL models.
|
||||
*/
|
||||
class TypeModel extends Unit {
|
||||
/**
|
||||
* Gets a data-flow node that is a source of the type `package;type`.
|
||||
*/
|
||||
DataFlow::Node getASource(string package, string type) { none() }
|
||||
|
||||
/**
|
||||
* Gets a data flow node that is a sink of the type `package;type`,
|
||||
* usually because it is an argument passed to a parameter of that type.
|
||||
*/
|
||||
DataFlow::Node getASink(string package, string type) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional type variable model rows.
|
||||
*/
|
||||
class TypeVariableModelCsv extends Unit {
|
||||
/**
|
||||
* Holds if `row` specifies a path through a type variable.
|
||||
*
|
||||
* A row of form,
|
||||
* ```
|
||||
* name;path
|
||||
* ```
|
||||
* means `path` can be substituted for a token `TypeVar[name]`.
|
||||
*/
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
}
|
||||
|
||||
private import ModelInput
|
||||
@@ -182,6 +216,8 @@ private predicate summaryModel(string row) { any(SummaryModelCsv s).row(inverseP
|
||||
|
||||
private predicate typeModel(string row) { any(TypeModelCsv s).row(inversePad(row)) }
|
||||
|
||||
private predicate typeVariableModel(string row) { any(TypeVariableModelCsv s).row(inversePad(row)) }
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(string package, string type, string path, string kind) {
|
||||
exists(string row |
|
||||
@@ -219,7 +255,7 @@ private predicate summaryModel(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if an type model exists for the given parameters. */
|
||||
/** Holds if a type model exists for the given parameters. */
|
||||
private predicate typeModel(
|
||||
string package1, string type1, string package2, string type2, string path
|
||||
) {
|
||||
@@ -233,6 +269,15 @@ private predicate typeModel(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a type variable model exists for the given parameters. */
|
||||
private predicate typeVariableModel(string name, string path) {
|
||||
exists(string row |
|
||||
typeVariableModel(row) and
|
||||
row.splitAt(";", 0) = name and
|
||||
row.splitAt(";", 1) = path
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a package that should be seen as an alias for the given other `package`,
|
||||
* or the `package` itself.
|
||||
@@ -253,7 +298,7 @@ private predicate isRelevantPackage(string package) {
|
||||
sourceModel(package, _, _, _) or
|
||||
sinkModel(package, _, _, _) or
|
||||
summaryModel(package, _, _, _, _, _) or
|
||||
typeModel(package, _, _, _, _)
|
||||
typeModel(_, _, package, _, _)
|
||||
) and
|
||||
(
|
||||
Specific::isPackageUsed(package)
|
||||
@@ -290,6 +335,8 @@ private class AccessPathRange extends AccessPath::Range {
|
||||
summaryModel(package, _, _, this, _, _) or
|
||||
summaryModel(package, _, _, _, this, _)
|
||||
)
|
||||
or
|
||||
typeVariableModel(_, this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,6 +386,57 @@ private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, A
|
||||
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
|
||||
}
|
||||
|
||||
private class TypeModelUseEntry extends API::EntryPoint {
|
||||
private string package;
|
||||
private string type;
|
||||
|
||||
TypeModelUseEntry() {
|
||||
exists(any(TypeModel tm).getASource(package, type)) and
|
||||
this = "TypeModelUseEntry;" + package + ";" + type
|
||||
}
|
||||
|
||||
override DataFlow::LocalSourceNode getASource() {
|
||||
result = any(TypeModel tm).getASource(package, type)
|
||||
}
|
||||
|
||||
API::Node getNodeForType(string package_, string type_) {
|
||||
package = package_ and type = type_ and result = this.getANode()
|
||||
}
|
||||
}
|
||||
|
||||
private class TypeModelDefEntry extends API::EntryPoint {
|
||||
private string package;
|
||||
private string type;
|
||||
|
||||
TypeModelDefEntry() {
|
||||
exists(any(TypeModel tm).getASink(package, type)) and
|
||||
this = "TypeModelDefEntry;" + package + ";" + type
|
||||
}
|
||||
|
||||
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(package, type) }
|
||||
|
||||
API::Node getNodeForType(string package_, string type_) {
|
||||
package = package_ and type = type_ and result = this.getANode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an API node identified by the given `(package,type)` pair.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private API::Node getNodeFromType(string package, string type) {
|
||||
exists(string package2, string type2, AccessPath path2 |
|
||||
typeModel(package, type, package2, type2, path2) and
|
||||
result = getNodeFromPath(package2, type2, path2)
|
||||
)
|
||||
or
|
||||
result = any(TypeModelUseEntry e).getNodeForType(package, type)
|
||||
or
|
||||
result = any(TypeModelDefEntry e).getNodeForType(package, type)
|
||||
or
|
||||
result = Specific::getExtraNodeFromType(package, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API node identified by the first `n` tokens of `path` in the given `(package, type, path)` tuple.
|
||||
*/
|
||||
@@ -347,12 +445,8 @@ private API::Node getNodeFromPath(string package, string type, AccessPath path,
|
||||
isRelevantFullPath(package, type, path) and
|
||||
(
|
||||
n = 0 and
|
||||
exists(string package2, string type2, AccessPath path2 |
|
||||
typeModel(package, type, package2, type2, path2) and
|
||||
result = getNodeFromPath(package2, type2, path2, path2.getNumToken())
|
||||
)
|
||||
result = getNodeFromType(package, type)
|
||||
or
|
||||
// Language-specific cases, such as handling of global variables
|
||||
result = Specific::getExtraNodeFromPath(package, type, path, n)
|
||||
)
|
||||
or
|
||||
@@ -361,6 +455,72 @@ private API::Node getNodeFromPath(string package, string type, AccessPath path,
|
||||
// Similar to the other recursive case, but where the path may have stepped through one or more call-site filters
|
||||
result =
|
||||
getSuccessorFromInvoke(getInvocationFromPath(package, type, path, n - 1), path.getToken(n - 1))
|
||||
or
|
||||
// Apply a subpath
|
||||
result =
|
||||
getNodeFromSubPath(getNodeFromPath(package, type, path, n - 1), getSubPathAt(path, n - 1))
|
||||
or
|
||||
// Apply a type step
|
||||
typeStep(getNodeFromPath(package, type, path, n), result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a subpath for the `TypeVar` token found at the `n`th token of `path`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private AccessPath getSubPathAt(AccessPath path, int n) {
|
||||
exists(string typeVarName |
|
||||
path.getToken(n).getAnArgument("TypeVar") = typeVarName and
|
||||
typeVariableModel(typeVarName, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that is found by evaluating the first `n` tokens of `subPath` starting at `base`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n) {
|
||||
exists(AccessPath path, int k |
|
||||
base = [getNodeFromPath(_, _, path, k), getNodeFromSubPath(_, path, k)] and
|
||||
subPath = getSubPathAt(path, k) and
|
||||
result = base and
|
||||
n = 0
|
||||
)
|
||||
or
|
||||
exists(string package, string type, AccessPath basePath |
|
||||
typeStepModel(package, type, basePath, subPath) and
|
||||
base = getNodeFromPath(package, type, basePath) and
|
||||
result = base and
|
||||
n = 0
|
||||
)
|
||||
or
|
||||
result = getSuccessorFromNode(getNodeFromSubPath(base, subPath, n - 1), subPath.getToken(n - 1))
|
||||
or
|
||||
result =
|
||||
getSuccessorFromInvoke(getInvocationFromSubPath(base, subPath, n - 1), subPath.getToken(n - 1))
|
||||
or
|
||||
result =
|
||||
getNodeFromSubPath(getNodeFromSubPath(base, subPath, n - 1), getSubPathAt(subPath, n - 1))
|
||||
or
|
||||
typeStep(getNodeFromSubPath(base, subPath, n), result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call site that is found by evaluating the first `n` tokens of `subPath` starting at `base`.
|
||||
*/
|
||||
private Specific::InvokeNode getInvocationFromSubPath(API::Node base, AccessPath subPath, int n) {
|
||||
result = Specific::getAnInvocationOf(getNodeFromSubPath(base, subPath, n))
|
||||
or
|
||||
result = getInvocationFromSubPath(base, subPath, n - 1) and
|
||||
invocationMatchesCallSiteFilter(result, subPath.getToken(n - 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that is found by evaluating `subPath` starting at `base`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath) {
|
||||
result = getNodeFromSubPath(base, subPath, subPath.getNumToken())
|
||||
}
|
||||
|
||||
/** Gets the node identified by the given `(package, type, path)` tuple. */
|
||||
@@ -368,6 +528,20 @@ API::Node getNodeFromPath(string package, string type, AccessPath path) {
|
||||
result = getNodeFromPath(package, type, path, path.getNumToken())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typeStepModel(string package, string type, AccessPath basePath, AccessPath output) {
|
||||
summaryModel(package, type, basePath, "", output, "type")
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typeStep(API::Node pred, API::Node succ) {
|
||||
exists(string package, string type, AccessPath basePath, AccessPath output |
|
||||
typeStepModel(package, type, basePath, output) and
|
||||
pred = getNodeFromPath(package, type, basePath) and
|
||||
succ = getNodeFromSubPath(pred, output)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an invocation identified by the given `(package, type, path)` tuple.
|
||||
*
|
||||
@@ -390,7 +564,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
name = ["Argument", "Parameter", "ReturnValue", "WithArity"]
|
||||
name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar"]
|
||||
or
|
||||
Specific::isExtraValidTokenNameInIdentifyingAccessPath(name)
|
||||
}
|
||||
@@ -418,6 +592,9 @@ predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argume
|
||||
name = "WithArity" and
|
||||
argument.regexpMatch("\\d+(\\.\\.(\\d+)?)?")
|
||||
or
|
||||
name = "TypeVar" and
|
||||
exists(argument)
|
||||
or
|
||||
Specific::isExtraValidTokenArgumentInIdentifyingAccessPath(name, argument)
|
||||
}
|
||||
|
||||
@@ -469,12 +646,7 @@ module ModelOutput {
|
||||
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
|
||||
* contributed by a CSV model.
|
||||
*/
|
||||
API::Node getATypeNode(string package, string type) {
|
||||
exists(string package2, string type2, AccessPath path |
|
||||
typeModel(package, type, package2, type2, path) and
|
||||
result = getNodeFromPath(package2, type2, path)
|
||||
)
|
||||
}
|
||||
API::Node getATypeNode(string package, string type) { result = getNodeFromType(package, type) }
|
||||
|
||||
/**
|
||||
* Gets an error message relating to an invalid CSV row in a model.
|
||||
@@ -489,6 +661,8 @@ module ModelOutput {
|
||||
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 6
|
||||
or
|
||||
any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 5
|
||||
or
|
||||
any(TypeVariableModelCsv csv).row(row) and kind = "type-variable" and expectedArity = 2
|
||||
|
|
||||
actualArity = count(row.indexOf(";")) + 1 and
|
||||
actualArity != expectedArity and
|
||||
@@ -499,7 +673,7 @@ module ModelOutput {
|
||||
or
|
||||
// Check names and arguments of access path tokens
|
||||
exists(AccessPath path, AccessPathToken token |
|
||||
isRelevantFullPath(_, _, path) and
|
||||
(isRelevantFullPath(_, _, path) or typeVariableModel(_, path)) and
|
||||
token = path.getToken(_)
|
||||
|
|
||||
not isValidTokenNameInIdentifyingAccessPath(token.getName()) and
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
private import javascript as JS
|
||||
private import JS::DataFlow as DataFlow
|
||||
private import ApiGraphModels
|
||||
|
||||
class Unit = JS::Unit;
|
||||
@@ -29,6 +28,7 @@ class Unit = JS::Unit;
|
||||
module API = JS::API;
|
||||
|
||||
import semmle.javascript.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
|
||||
import JS::DataFlow as DataFlow
|
||||
private import AccessPathSyntax
|
||||
|
||||
/**
|
||||
@@ -77,10 +77,6 @@ private API::Node getGlobalNode(string globalName) {
|
||||
/** Gets a JavaScript-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
|
||||
bindingset[package, type, path]
|
||||
API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) {
|
||||
type = "" and
|
||||
n = 0 and
|
||||
result = API::moduleImport(package)
|
||||
or
|
||||
// Global variable accesses is via the 'global' package
|
||||
exists(AccessPathToken token |
|
||||
package = getAPackageAlias("global") and
|
||||
@@ -90,10 +86,15 @@ API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int
|
||||
result = getGlobalNode(token.getAnArgument()) and
|
||||
n = 1
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a JavaScript-specific interpretation of the `(package, type)` tuple. */
|
||||
API::Node getExtraNodeFromType(string package, string type) {
|
||||
type = "" and
|
||||
result = API::moduleImport(package)
|
||||
or
|
||||
// Access instance of a type based on type annotations
|
||||
n = 0 and
|
||||
result = API::Node::ofType(getAPackageAlias(package), type)
|
||||
result = API::Internal::getANodeOfTypeRaw(getAPackageAlias(package), type)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,6 +105,9 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||
token.getName() = "Member" and
|
||||
result = node.getMember(token.getAnArgument())
|
||||
or
|
||||
token.getName() = "AnyMember" and
|
||||
result = node.getAMember()
|
||||
or
|
||||
token.getName() = "Instance" and
|
||||
result = node.getInstance()
|
||||
or
|
||||
@@ -163,6 +167,16 @@ predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPat
|
||||
token.getName() = "Call" and
|
||||
invoke instanceof API::CallNode and
|
||||
invoke instanceof DataFlow::CallNode // Workaround compiler bug
|
||||
or
|
||||
token.getName() = "WithStringArgument" and
|
||||
exists(string operand, string argIndex, string stringValue |
|
||||
operand = token.getAnArgument() and
|
||||
argIndex = operand.splitAt("=", 0) and
|
||||
stringValue = operand.splitAt("=", 1) and
|
||||
invoke
|
||||
.getArgument(AccessPath::parseIntWithArity(argIndex, invoke.getNumArgument()))
|
||||
.getStringValue() = stringValue
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,8 +239,9 @@ bindingset[name]
|
||||
predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
name =
|
||||
[
|
||||
"Member", "Instance", "Awaited", "ArrayElement", "Element", "MapValue", "NewCall", "Call",
|
||||
"DecoratedClass", "DecoratedMember", "DecoratedParameter"
|
||||
"Member", "AnyMember", "Instance", "Awaited", "ArrayElement", "Element", "MapValue",
|
||||
"NewCall", "Call", "DecoratedClass", "DecoratedMember", "DecoratedParameter",
|
||||
"WithStringArgument"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -237,7 +252,7 @@ predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
name =
|
||||
[
|
||||
"Instance", "Awaited", "ArrayElement", "Element", "MapValue", "NewCall", "Call",
|
||||
"AnyMember", "Instance", "Awaited", "ArrayElement", "Element", "MapValue", "NewCall", "Call",
|
||||
"DecoratedClass", "DecoratedMember", "DecoratedParameter"
|
||||
]
|
||||
}
|
||||
@@ -250,4 +265,8 @@ bindingset[name, argument]
|
||||
predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument) {
|
||||
name = ["Member"] and
|
||||
exists(argument)
|
||||
or
|
||||
name = "WithStringArgument" and
|
||||
exists(argument.indexOf("=")) and
|
||||
exists(AccessPath::parseIntWithArity(argument.splitAt("=", 0), 10))
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ private module JQueryClientRequest {
|
||||
.getParameter(0)
|
||||
or
|
||||
result =
|
||||
getAResponseNodeFromAnXHRObject(this.getOptionArgument([0 .. 1],
|
||||
getAResponseNodeFromAnXhrObject(this.getOptionArgument([0 .. 1],
|
||||
any(string method | method = "error" or method = "complete"))
|
||||
.getALocalSource()
|
||||
.(DataFlow::FunctionNode)
|
||||
@@ -303,15 +303,15 @@ private module JQueryClientRequest {
|
||||
.getParameter(0)
|
||||
or
|
||||
result =
|
||||
getAResponseNodeFromAnXHRObject(request.getAMemberCall("fail").getCallback(0).getParameter(0))
|
||||
getAResponseNodeFromAnXhrObject(request.getAMemberCall("fail").getCallback(0).getParameter(0))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node referring to the response contained in an `jqXHR` object.
|
||||
*/
|
||||
private DataFlow::SourceNode getAResponseNodeFromAnXHRObject(DataFlow::SourceNode jqXHR) {
|
||||
private DataFlow::SourceNode getAResponseNodeFromAnXhrObject(DataFlow::SourceNode jqXhr) {
|
||||
result =
|
||||
jqXHR
|
||||
jqXhr
|
||||
.getAPropertyRead(any(string s |
|
||||
s = "responseText" or
|
||||
s = "responseXML"
|
||||
|
||||
@@ -11,14 +11,14 @@ private import semmle.javascript.frameworks.ConnectExpressShared
|
||||
* Add `NodeJSLib::RouteHandlerCandidate` to the extent of `NodeJSLib::RouteHandler`.
|
||||
*/
|
||||
private class PromotedNodeJSLibCandidate extends NodeJSLib::RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler {
|
||||
Http::Servers::StandardRouteHandler {
|
||||
PromotedNodeJSLibCandidate() { this instanceof NodeJSLib::RouteHandlerCandidate }
|
||||
}
|
||||
|
||||
/**
|
||||
* Add `Hapi::RouteHandlerCandidate` to the extent of `Hapi::RouteHandler`.
|
||||
*/
|
||||
private class PromotedHapiCandidate extends Hapi::RouteHandler, HTTP::Servers::StandardRouteHandler {
|
||||
private class PromotedHapiCandidate extends Hapi::RouteHandler, Http::Servers::StandardRouteHandler {
|
||||
PromotedHapiCandidate() { this instanceof Hapi::RouteHandlerCandidate }
|
||||
}
|
||||
|
||||
@@ -26,11 +26,11 @@ private class PromotedHapiCandidate extends Hapi::RouteHandler, HTTP::Servers::S
|
||||
* Add `ConnectExpressShared::RouteHandlerCandidate` to the extent of `Express::RouteHandler`.
|
||||
*/
|
||||
private class PromotedExpressCandidate extends Express::RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ private class PromotedExpressCandidate extends Express::RouteHandler,
|
||||
* Add `ConnectExpressShared::RouteHandlerCandidate` to the extent of `Connect::RouteHandler`.
|
||||
*/
|
||||
private class PromotedConnectCandidate extends Connect::RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,3 +87,70 @@ module Cryptography {
|
||||
predicate isWeak() { this = "ECB" }
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides classes for modeling HTTP-related APIs. */
|
||||
module Http {
|
||||
/** Provides classes for modeling HTTP clients. */
|
||||
module Client {
|
||||
/**
|
||||
* A data-flow node that makes an outgoing HTTP request.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `Http::Client::Request::Range` instead.
|
||||
*/
|
||||
class Request extends DataFlow::Node instanceof Request::Range {
|
||||
/**
|
||||
* Gets a data-flow node that contributes to the URL of the request.
|
||||
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
|
||||
*/
|
||||
DataFlow::Node getAUrlPart() { result = super.getAUrlPart() }
|
||||
|
||||
/** Gets a string that identifies the framework used for this request. */
|
||||
string getFramework() { result = super.getFramework() }
|
||||
|
||||
/**
|
||||
* Holds if this request is made using a mode that disables SSL/TLS
|
||||
* certificate validation, where `disablingNode` represents the point at
|
||||
* which the validation was disabled, and `argumentOrigin` represents the origin
|
||||
* of the argument that disabled the validation (which could be the same node as
|
||||
* `disablingNode`).
|
||||
*/
|
||||
predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
super.disablesCertificateValidation(disablingNode, argumentOrigin)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new HTTP requests. */
|
||||
module Request {
|
||||
/**
|
||||
* A data-flow node that makes an outgoing HTTP request.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `Http::Client::Request` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a data-flow node that contributes to the URL of the request.
|
||||
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
|
||||
*/
|
||||
abstract DataFlow::Node getAUrlPart();
|
||||
|
||||
/** Gets a string that identifies the framework used for this request. */
|
||||
abstract string getFramework();
|
||||
|
||||
/**
|
||||
* Holds if this request is made using a mode that disables SSL/TLS
|
||||
* certificate validation, where `disablingNode` represents the point at
|
||||
* which the validation was disabled, and `argumentOrigin` represents the origin
|
||||
* of the argument that disabled the validation (which could be the same node as
|
||||
* `disablingNode`).
|
||||
*/
|
||||
abstract predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ module ESLint {
|
||||
}
|
||||
|
||||
/** An `.eslintrc.yaml` file. */
|
||||
private class EslintrcYaml extends Configuration, YAMLDocument, YAMLMapping {
|
||||
private class EslintrcYaml extends Configuration, YamlDocument, YamlMapping {
|
||||
EslintrcYaml() {
|
||||
exists(string n | n = getFile().getBaseName() |
|
||||
n = ".eslintrc.yaml" or n = ".eslintrc.yml" or n = ".eslintrc"
|
||||
@@ -71,11 +71,11 @@ module ESLint {
|
||||
}
|
||||
|
||||
/** An ESLint configuration object in YAML format. */
|
||||
private class YamlConfigurationObject extends ConfigurationObject, YAMLMapping {
|
||||
private class YamlConfigurationObject extends ConfigurationObject, YamlMapping {
|
||||
override Configuration getConfiguration() { this = result.(EslintrcYaml).getValue(_) }
|
||||
|
||||
override boolean getBooleanProperty(string p) {
|
||||
exists(string v | v = lookup(p).(YAMLBool).getValue() |
|
||||
exists(string v | v = lookup(p).(YamlBool).getValue() |
|
||||
v = "true" and result = true
|
||||
or
|
||||
v = "false" and result = false
|
||||
|
||||
@@ -96,7 +96,10 @@ class OverlyWideRange extends RegExpCharacterRange {
|
||||
toCodePoint("A") <= high
|
||||
or
|
||||
// a non-alphanumeric char as part of the range boundaries
|
||||
exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode()))
|
||||
exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and
|
||||
// while still being ascii
|
||||
low < 128 and
|
||||
high < 128
|
||||
) and
|
||||
// allowlist for known ranges
|
||||
not this = allowedWideRanges()
|
||||
@@ -173,7 +176,7 @@ module RangePrinter {
|
||||
}
|
||||
|
||||
/** Gets the number of parts we should print for a given `range`. */
|
||||
private int parts(OverlyWideRange range) { result = 1 + strictcount(cutoff(range, _)) }
|
||||
private int parts(OverlyWideRange range) { result = 1 + count(cutoff(range, _)) }
|
||||
|
||||
/** Holds if the given part of a range should span from `low` to `high`. */
|
||||
private predicate part(OverlyWideRange range, int part, string low, string high) {
|
||||
|
||||
@@ -21,11 +21,11 @@ predicate config(string key, string val, Locatable valElement) {
|
||||
val = valElement.(JsonString).getValue()
|
||||
)
|
||||
or
|
||||
exists(YAMLMapping m, YAMLString keyElement |
|
||||
exists(YamlMapping m, YamlString keyElement |
|
||||
m.maps(keyElement, valElement) and
|
||||
key = keyElement.getValue() and
|
||||
(
|
||||
val = valElement.(YAMLString).getValue()
|
||||
val = valElement.(YamlString).getValue()
|
||||
or
|
||||
valElement.toString() = "" and
|
||||
val = ""
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -80,7 +96,7 @@ private predicate writesProperty(DataFlow::Node node, string name) {
|
||||
exists(VarDef v | v.getAVariable().getName() = name |
|
||||
if exists(v.getSource())
|
||||
then v.getSource() = node.asExpr()
|
||||
else node = DataFlow::ssaDefinitionNode(SSA::definition(v))
|
||||
else node = DataFlow::ssaDefinitionNode(Ssa::definition(v))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ module TaintedObject {
|
||||
|
||||
/** Request input accesses as a JSON source. */
|
||||
private class RequestInputAsSource extends Source {
|
||||
RequestInputAsSource() { this.(HTTP::RequestInputAccess).isUserControlledObject() }
|
||||
RequestInputAsSource() { this.(Http::RequestInputAccess).isUserControlledObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,7 +127,7 @@ class UselessCat extends CommandCall {
|
||||
or
|
||||
// `exec` can use 3 parameters, `readFile` can only use two, so it is OK to have a third parameter if it is unused,
|
||||
func.getNumParameter() = 3 and
|
||||
not exists(SSA::definition(func.getParameter(2).getParameter()))
|
||||
not exists(Ssa::definition(func.getParameter(2).getParameter()))
|
||||
)
|
||||
) and
|
||||
// The process returned by an async call is unused.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user