Merge branch 'main' into rc/3.7

This commit is contained in:
Andrew Eisenberg
2022-09-20 08:33:58 -07:00
2309 changed files with 133758 additions and 43219 deletions

View File

@@ -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",

View File

@@ -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)

View File

@@ -241,7 +241,7 @@ const astProperties: string[] = [
"constructor",
"declarationList",
"declarations",
"decorators",
"illegalDecorators",
"default",
"delete",
"dotDotDotToken",

View File

@@ -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();

View File

@@ -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==

View File

@@ -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");

View File

@@ -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;

View File

@@ -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() |

View File

@@ -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)

View File

@@ -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()
)

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-experimental-atm-lib
version: 0.3.2
version: 0.3.4
extractor: javascript
library: true
groups:

View File

@@ -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]

View File

@@ -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. */

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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"
}

View File

@@ -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))
}

View 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

View File

@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* Added support for TypeScript 4.8.

View 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.

View 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.

View File

@@ -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 }

View File

@@ -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. */

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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" }

View File

@@ -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 |

View File

@@ -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.

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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;
}
/**

View File

@@ -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() }
}
/**

View File

@@ -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;

View File

@@ -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.
*

View File

@@ -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;

View File

@@ -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

View File

@@ -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.

View File

@@ -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()
)
}
}
/**

View File

@@ -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()]
)
}

View File

@@ -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;

View File

@@ -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(

View File

@@ -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() }
}

View File

@@ -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"
}

View File

@@ -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"
)
}

View File

@@ -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" }
}

View File

@@ -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

View File

@@ -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()
)
}

View File

@@ -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()
}
}

View File

@@ -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" }
}

View File

@@ -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

View File

@@ -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)
)
}

View File

@@ -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")
)
}

View File

@@ -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()

View File

@@ -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 =
[

View File

@@ -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 }
}

View File

@@ -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 }
}

View File

@@ -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"
)

View File

@@ -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;
}
/**

View File

@@ -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.
*/

View File

@@ -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() |

View File

@@ -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() }
/**

View File

@@ -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"])
}

View File

@@ -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)
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 }
}

View File

@@ -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))
}
}
}

View File

@@ -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" }
}

View File

@@ -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. */

View File

@@ -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 }
}

View File

@@ -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 }
}
}

View File

@@ -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"])
)
}

View File

@@ -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`.
*/

View File

@@ -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]))
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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()
)
}
}

View File

@@ -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" }

View File

@@ -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:

View File

@@ -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"

View File

@@ -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 }
}
}

View File

@@ -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

View File

@@ -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 = ""
)
)

View File

@@ -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. */

View File

@@ -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

View File

@@ -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() }
}
/**

View File

@@ -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

View File

@@ -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))
}

View File

@@ -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"

View File

@@ -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)
}
}

View File

@@ -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
);
}
}
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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 = ""

View File

@@ -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() }

View File

@@ -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() }
}
/**

View File

@@ -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