Merge branch 'main' into codeql-ci/js/ml-powered-pack-release-0.3.3

This commit is contained in:
Henry Mercer
2022-09-13 15:15:56 +01:00
committed by GitHub
5550 changed files with 471715 additions and 181168 deletions

View File

@@ -14,7 +14,7 @@ import DataFlow::PathGraph
/**
* Gets the name of an unescaped placeholder in a lodash template.
*
* For example, the string `<h1><%= title %></h1>` contains the placeholder `title`.
* For example, the string `"<h1><%= title %></h1>"` contains the placeholder "title".
*/
bindingset[s]
string getAPlaceholderInString(string s) {

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,9 +144,9 @@ private module AccessPaths {
not param = base.getReceiver()
|
result = param and
name = param.getAnImmediateUse().asExpr().(Parameter).getName()
name = param.asSource().(DataFlow::ParameterNode).getName()
or
param.getAnImmediateUse().asExpr() instanceof DestructuringPattern and
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

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

@@ -0,0 +1,20 @@
/*
* For internal use only.
*
*
* Count the number of sinks and alerts for a particular dataflow config.
*/
import javascript
import evaluation.EndToEndEvaluation
query predicate countAlertsAndSinks(int numAlerts, int numSinks) {
numAlerts =
count(DataFlow::Configuration cfg, DataFlow::Node source, DataFlow::Node sink |
cfg.hasFlow(source, sink) and not isFlowExcluded(source, sink)
) and
numSinks =
count(DataFlow::Node sink |
exists(DataFlow::Configuration cfg | cfg.isSink(sink) or cfg.isSink(sink, _))
)
}

View File

@@ -0,0 +1,9 @@
/*
* For internal use only.
*
*
* Count the number of sinks and alerts for the `CodeInjection` security query.
*/
import semmle.javascript.security.dataflow.CodeInjectionQuery
import CountAlertsAndSinks

View File

@@ -0,0 +1,9 @@
/*
* For internal use only.
*
*
* Count the number of sinks and alerts for the `NosqlInection` security query.
*/
import semmle.javascript.security.dataflow.NosqlInjectionQuery
import CountAlertsAndSinks

View File

@@ -0,0 +1,9 @@
/*
* For internal use only.
*
*
* Count the number of sinks and alerts for the `SqlInection` security query.
*/
import semmle.javascript.security.dataflow.SqlInjectionQuery
import CountAlertsAndSinks

View File

@@ -0,0 +1,9 @@
/*
* For internal use only.
*
*
* Count the number of sinks and alerts for the `TaintedPath` security query.
*/
import semmle.javascript.security.dataflow.TaintedPathQuery
import CountAlertsAndSinks

View File

@@ -0,0 +1,9 @@
/*
* For internal use only.
*
*
* Count the number of sinks and alerts for the `DomBasedXss` security query.
*/
import semmle.javascript.security.dataflow.DomBasedXssQuery
import CountAlertsAndSinks

View File

@@ -0,0 +1,9 @@
/*
* For internal use only.
*
*
* Count the number of sinks and alerts for the `XssThroughDom` security query.
*/
import semmle.javascript.security.dataflow.XssThroughDomQuery
import CountAlertsAndSinks

View File

@@ -5,7 +5,8 @@
* evaluation pipeline.
*/
import semmle.javascript.security.dataflow.NosqlInjection
import javascript
import semmle.javascript.security.dataflow.NosqlInjectionQuery as NosqlInjection
import EndToEndEvaluation as EndToEndEvaluation
from

View File

@@ -5,7 +5,8 @@
* evaluation pipeline.
*/
import semmle.javascript.security.dataflow.SqlInjection
import javascript
import semmle.javascript.security.dataflow.SqlInjectionQuery as SqlInjection
import EndToEndEvaluation as EndToEndEvaluation
from

View File

@@ -5,7 +5,8 @@
* evaluation pipeline.
*/
import semmle.javascript.security.dataflow.TaintedPath
import javascript
import semmle.javascript.security.dataflow.TaintedPathQuery as TaintedPath
import EndToEndEvaluation as EndToEndEvaluation
from

View File

@@ -5,7 +5,8 @@
* pipeline.
*/
import semmle.javascript.security.dataflow.DomBasedXss
import javascript
import semmle.javascript.security.dataflow.DomBasedXssQuery as DomBasedXss
import EndToEndEvaluation as EndToEndEvaluation
from

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. */
@@ -188,10 +204,10 @@ module FlowFromSource {
Query getQuery() { result = q }
/** The sinks are the endpoints we're extracting. */
/** Holds if `sink` is an endpoint we're extracting. */
override predicate isSink(DataFlow::Node sink) { sink = getAnEndpoint(q) }
/** The sinks are the endpoints we're extracting. */
/** Holds if `sink` is an endpoint we're extracting. */
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel lbl) {
sink = getAnEndpoint(q) and exists(lbl)
}

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

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

@@ -3,7 +3,7 @@
*
* This test checks several components of the endpoint filters for each query to see whether they
* filter out any known sinks. It explicitly does not check the endpoint filtering step that's based
* on whether the endpoint is an argument to a modelled function, since this necessarily filters out
* on whether the endpoint is an argument to a modeled function, since this necessarily filters out
* all known sinks. However, we can test all the other filtering steps against the set of known
* sinks.
*
@@ -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

@@ -1,3 +1,50 @@
## 0.2.4
### Deprecated APIs
* Many classes/predicates/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.
* The utility files previously in the `semmle.javascript.security.performance` package have been moved to the `semmle.javascript.security.regexp` package.
The previous files still exist as deprecated aliases.
### Minor Analysis Improvements
* Most deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
### Bug Fixes
* Fixed that top-level `for await` statements would produce a syntax error. These statements are now parsed correctly.
## 0.2.3
## 0.2.2
## 0.2.1
### Minor Analysis Improvements
* The `chownr` library is now modeled as a sink for the `js/path-injection` query.
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query.
## 0.2.0
### Major Analysis Improvements
* Added support for TypeScript 4.7.
### Minor Analysis Improvements
* All new ECMAScript 2022 features are now supported.
## 0.1.4
## 0.1.3
### Minor Analysis Improvements
* The `isLibaryFile` predicate from `ClassifyFiles.qll` has been renamed to `isLibraryFile` to fix a typo.
## 0.1.2
### Deprecated APIs

View File

@@ -37,6 +37,8 @@ predicate inVoidContext(Expr e) {
)
or
exists(LogicalBinaryExpr logical | e = logical.getRightOperand() and inVoidContext(logical))
or
exists(ConditionalExpr cond | e = cond.getABranch() | inVoidContext(cond))
}
/**

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

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
## 0.1.3
### Minor Analysis Improvements
* The `isLibaryFile` predicate from `ClassifyFiles.qll` has been renamed to `isLibraryFile` to fix a typo.

View File

@@ -0,0 +1 @@
## 0.1.4

View File

@@ -0,0 +1,9 @@
## 0.2.0
### Major Analysis Improvements
* Added support for TypeScript 4.7.
### Minor Analysis Improvements
* All new ECMAScript 2022 features are now supported.

View File

@@ -0,0 +1,7 @@
## 0.2.1
### Minor Analysis Improvements
* The `chownr` library is now modeled as a sink for the `js/path-injection` query.
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query.

View File

@@ -0,0 +1 @@
## 0.2.2

View File

@@ -0,0 +1 @@
## 0.2.3

View File

@@ -0,0 +1,16 @@
## 0.2.4
### Deprecated APIs
* Many classes/predicates/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.
* The utility files previously in the `semmle.javascript.security.performance` package have been moved to the `semmle.javascript.security.regexp` package.
The previous files still exist as deprecated aliases.
### Minor Analysis Improvements
* Most deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
### Bug Fixes
* Fixed that top-level `for await` statements would produce a syntax error. These statements are now parsed correctly.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.1.2
lastReleaseVersion: 0.2.4

View File

@@ -45,7 +45,7 @@ private predicate variableDefLookup(VarAccess va, AstNode def, string kind) {
/**
* Holds if variable access `va` is of kind `kind` and refers to the
* variable declaration.
* variable declaration `decl`.
*
* For example, in the statement `var x = 42, y = x;`, the initializing
* expression of `y` is a variable access `x` of kind `"V"` that refers to

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-all
version: 0.1.3-dev
version: 0.2.5-dev
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript

View File

@@ -5,6 +5,7 @@
import javascript
private import semmle.javascript.internal.CachedStages
private import Expressions.ExprHasNoEffect
/**
* An AMD `define` call.
@@ -26,7 +27,7 @@ private import semmle.javascript.internal.CachedStages
*/
class AmdModuleDefinition extends CallExpr {
AmdModuleDefinition() {
getParent() instanceof ExprStmt and
inVoidContext(this) and
getCallee().(GlobalVarAccess).getName() = "define" and
exists(int n | n = getNumArgument() |
n = 1
@@ -202,13 +203,22 @@ private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString
override string getValue() { result = getStringValue() }
}
/**
* Holds if `nd` is nested inside an AMD module definition.
*/
private predicate inAmdModuleDefinition(AstNode nd) {
nd.getParent() instanceof AmdModuleDefinition
or
inAmdModuleDefinition(nd.getParent())
}
/**
* Holds if `def` is an AMD module definition in `tl` which is not
* nested inside another module definition.
*/
private predicate amdModuleTopLevel(AmdModuleDefinition def, TopLevel tl) {
def.getTopLevel() = tl and
not def.getParent+() instanceof AmdModuleDefinition
not inAmdModuleDefinition(def)
}
/**

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,9 +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() }
/** Gets the name of the workflow file. */
string getFileName() { result = this.getFile().getBaseName() }
@@ -42,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 }
@@ -55,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;
@@ -71,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 }
@@ -96,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 }
@@ -109,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;
@@ -129,13 +132,16 @@ module Actions {
/** Gets the value of the `if` field in this step, if any. */
StepIf getIf() { result.getStep() = this }
/** Gets the ID of this step, if any. */
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 }
@@ -164,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 }
@@ -194,7 +200,7 @@ module Actions {
* arg2: abc
* ```
*/
class With extends YAMLNode, YAMLMapping {
class With extends YamlNode, YamlMapping {
Step step;
With() { step.lookup("with") = this }
@@ -213,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 }
@@ -226,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

@@ -2,11 +2,7 @@
* Provides an implementation of _API graphs_, which are an abstract representation of the API
* surface used and/or defined by a code base.
*
* The nodes of the API graph represent definitions and uses of API components. The edges are
* directed and labeled; they specify how the components represented by nodes relate to each other.
* For example, if one of the nodes represents a definition of an API function, then there
* will be nodes corresponding to the function's parameters, which are connected to the function
* node by edges labeled `parameter <i>`.
* See `API::Node` for more in-depth documentation.
*/
import javascript
@@ -14,50 +10,159 @@ private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
private import internal.CachedStages
/**
* Provides classes and predicates for working with APIs defined or used in a database.
* Provides classes and predicates for working with the API boundary between the current
* codebase and external libraries.
*
* See `API::Node` for more in-depth documentation.
*/
module API {
/**
* An abstract representation of a definition or use of an API component such as a function
* exported by an npm package, a parameter of such a function, or its result.
* A node in the API graph, representing a value that has crossed the boundary between this
* codebase and an external library (or in general, any external codebase).
*
* ### Basic usage
*
* API graphs are typically used to identify "API calls", that is, calls to an external function
* whose implementation is not necessarily part of the current codebase.
*
* The most basic use of API graphs is typically as follows:
* 1. Start with `API::moduleImport` for the relevant library.
* 2. Follow up with a chain of accessors such as `getMember` describing how to get to the relevant API function.
* 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`.
*
* For example, a simplified way to get arguments to `underscore.extend` would be
* ```ql
* API::moduleImport("underscore").getMember("extend").getParameter(0).asSink()
* ```
*
* The most commonly used accessors are `getMember`, `getParameter`, and `getReturn`.
*
* ### API graph nodes
*
* There are two kinds of nodes in the API graphs, distinguished by who is "holding" the value:
* - **Use-nodes** represent values held by the current codebase, which came from an external library.
* (The current codebase is "using" a value that came from the library).
* - **Def-nodes** represent values held by the external library, which came from this codebase.
* (The current codebase "defines" the value seen by the library).
*
* API graph nodes are associated with data-flow nodes in the current codebase.
* (Since external libraries are not part of the database, there is no way to associate with concrete
* data-flow nodes from the external library).
* - **Use-nodes** are associated with data-flow nodes where a value enters the current codebase,
* such as the return value of a call to an external function.
* - **Def-nodes** are associated with data-flow nodes where a value leaves the current codebase,
* such as an argument passed in a call to an external function.
*
*
* ### Access paths and edge labels
*
* Nodes in the API graph are associated with a set of access paths, describing a series of operations
* that may be performed to obtain that value.
*
* For example, the access path `API::moduleImport("lodash").getMember("extend")` represents the action of
* importing `lodash` and then accessing the member `extend` on the resulting object.
* It would be associated with an expression such as `require("lodash").extend`.
*
* Each edge in the graph is labelled by such an "operation". For an edge `A->B`, the type of the `A` node
* determines who is performing the operation, and the type of the `B` node determines who ends up holding
* the result:
* - An edge starting from a use-node describes what the current codebase is doing to a value that
* came from a library.
* - An edge starting from a def-node describes what the external library might do to a value that
* came from the current codebase.
* - An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node).
* - An edge ending in a def-node means the result ends up in external code (its associated data-flow node is
* the place where it was "last seen" in the current codebase before flowing out)
*
* Because the implementation of the external library is not visible, it is not known exactly what operations
* it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would
* lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform
* those operations. (When constructing these edges, we assume the library is somewhat well-behaved).
*
* For example, given this snippet:
* ```js
* require('foo')(x => { doSomething(x) })
* ```
* A callback is passed to the external function `foo`. We can't know if `foo` will actually invoke this callback.
* But _if_ the library should decide to invoke the callback, then a value will flow into the current codebase via the `x` parameter.
* For that reason, an edge is generated representing the argument-passing operation that might be performed by `foo`.
* This edge is going from the def-node associated with the callback to the use-node associated with the parameter `x`.
*
* ### Thinking in operations versus code patterns
*
* Treating edges as "operations" helps avoid a pitfall in which library models become overly specific to certain code patterns.
* Consider the following two equivalent calls to `foo`:
* ```js
* const foo = require('foo');
*
* foo({
* myMethod(x) {...}
* });
*
* foo({
* get myMethod() {
* return function(x) {...}
* }
* });
* ```
* If `foo` calls `myMethod` on its first parameter, either of the `myMethod` implementations will be invoked.
* And indeed, the access path `API::moduleImport("foo").getParameter(0).getMember("myMethod").getParameter(0)` correctly
* identifies both `x` parameters.
*
* Observe how `getMember("myMethod")` behaves when the member is defined via a getter. When thinking in code patterns,
* it might seem obvious that `getMember` should have obtained a reference to the getter method itself.
* But when seeing it as an access to `myMethod` performed by the library, we can deduce that the relevant expression
* on the client side is actually the return-value of the getter.
*
* Although one may think of API graphs as a tool to find certain program elements in the codebase,
* it can lead to some situations where intuition does not match what works best in practice.
*/
class Node extends Impl::TApiNode {
/**
* Gets a data-flow node corresponding to a use of the API component represented by this node.
* Get a data-flow node where this value may flow after entering the current codebase.
*
* For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the
* `fs` module, and `require('fs').readFileSync(file)` is a use of the return of that function.
*
* This includes indirect uses found via data flow, meaning that in
* `f(obj.foo); function f(x) {};` both `obj.foo` and `x` are uses of the `foo` member from `obj`.
*
* As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to
* `x` are uses of the first parameter of `plusOne`.
* This is similar to `asSource()` but additionally includes nodes that are transitively reachable by data flow.
* See `asSource()` for examples.
*/
pragma[inline]
DataFlow::Node getAUse() {
exists(DataFlow::SourceNode src | Impl::use(this, src) |
Impl::trackUseNode(src).flowsTo(result)
)
DataFlow::Node getAValueReachableFromSource() {
Impl::trackUseNode(this.asSource()).flowsTo(result)
}
/**
* Gets an immediate use of the API component represented by this node.
* Get a data-flow node where this value enters the current codebase.
*
* For example, `require('fs').readFileSync` is a an immediate use of the `readFileSync` member
* from the `fs` module.
* For example:
* ```js
* // API::moduleImport("fs").asSource()
* require('fs');
*
* Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses
* found via data flow. This means that in `const x = fs.readFile` only `fs.readFile` is a reference
* to the `readFile` member of `fs`, neither `x` nor any node that `x` flows to is a reference to
* this API component.
* // API::moduleImport("fs").getMember("readFile").asSource()
* require('fs').readFile;
*
* // API::moduleImport("fs").getMember("readFile").getReturn().asSource()
* require('fs').readFile();
*
* require('fs').readFile(
* filename,
* // 'y' matched by API::moduleImport("fs").getMember("readFile").getParameter(1).getParameter(0).asSource()
* y => {
* ...
* });
* ```
*/
DataFlow::SourceNode getAnImmediateUse() { Impl::use(this, result) }
DataFlow::SourceNode asSource() { Impl::use(this, result) }
/** DEPRECATED. This predicate has been renamed to `asSource`. */
deprecated DataFlow::SourceNode getAnImmediateUse() { result = this.asSource() }
/** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource`. */
deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() }
/**
* Gets a call to the function represented by this API component.
*/
CallNode getACall() { result = this.getReturn().getAnImmediateUse() }
CallNode getACall() { result = this.getReturn().asSource() }
/**
* Gets a call to the function represented by this API component,
@@ -72,7 +177,7 @@ module API {
/**
* Gets a `new` call to the function represented by this API component.
*/
NewNode getAnInstantiation() { result = this.getInstance().getAnImmediateUse() }
NewNode getAnInstantiation() { result = this.getInstance().asSource() }
/**
* Gets an invocation (with our without `new`) to the function represented by this API component.
@@ -80,26 +185,38 @@ module API {
InvokeNode getAnInvocation() { result = this.getACall() or result = this.getAnInstantiation() }
/**
* Gets a data-flow node corresponding to the right-hand side of a definition of the API
* component represented by this node.
* Get a data-flow node where this value leaves the current codebase and flows into an
* external library (or in general, any external codebase).
*
* For example, in the assignment `exports.plusOne = (x) => x+1`, the function expression
* `(x) => x+1` is the right-hand side of the definition of the member `plusOne` of
* the enclosing module, and the expression `x+1` is the right-had side of the definition of
* its result.
* Concretely, this is either an argument passed to a call to external code,
* or the right-hand side of a property write on an object flowing into such a call.
*
* Note that for parameters, it is the arguments flowing into that parameter that count as
* right-hand sides of the definition, not the declaration of the parameter itself.
* Consequently, in `require('fs').readFileSync(file)`, `file` is the right-hand
* side of a definition of the first parameter of `readFileSync` from the `fs` module.
* For example:
* ```js
* // 'x' is matched by API::moduleImport("foo").getParameter(0).asSink()
* require('foo')(x);
*
* // 'x' is matched by API::moduleImport("foo").getParameter(0).getMember("prop").asSink()
* require('foo')({
* prop: x
* });
* ```
*/
DataFlow::Node getARhs() { Impl::rhs(this, result) }
DataFlow::Node asSink() { Impl::rhs(this, result) }
/**
* Gets a data-flow node that may interprocedurally flow to the right-hand side of a definition
* of the API component represented by this node.
* Get a data-flow node that transitively flows to an external library (or in general, any external codebase).
*
* This is similar to `asSink()` but additionally includes nodes that transitively reach a sink by data flow.
* See `asSink()` for examples.
*/
DataFlow::Node getAValueReachingRhs() { result = Impl::trackDefNode(this.getARhs()) }
DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.asSink()) }
/** DEPRECATED. This predicate has been renamed to `asSink`. */
deprecated DataFlow::Node getARhs() { result = this.asSink() }
/** DEPRECATED. This predicate has been renamed to `getAValueReachingSink`. */
deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() }
/**
* Gets a node representing member `m` of this API component.
@@ -334,7 +451,7 @@ module API {
* In other words, the value of a use of `that` may flow into the right-hand side of a
* definition of this node.
*/
predicate refersTo(Node that) { this.getARhs() = that.getAUse() }
predicate refersTo(Node that) { this.asSink() = that.getAValueReachableFromSource() }
/**
* Gets the data-flow node that gives rise to this node, if any.
@@ -416,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`. */
@@ -427,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()
}
}
@@ -445,11 +579,17 @@ module API {
bindingset[this]
EntryPoint() { any() }
/** Gets a data-flow node that uses this entry point. */
abstract DataFlow::SourceNode getAUse();
/** DEPRECATED. This predicate has been renamed to `getASource`. */
deprecated DataFlow::SourceNode getAUse() { none() }
/** Gets a data-flow node that defines this entry point. */
abstract DataFlow::Node getARhs();
/** DEPRECATED. This predicate has been renamed to `getASink`. */
deprecated DataFlow::SourceNode getARhs() { none() }
/** Gets a data-flow node where a value enters the current codebase through this entry-point. */
DataFlow::SourceNode getASource() { none() }
/** Gets a data-flow node where a value leaves the current codebase through this entry-point. */
DataFlow::Node getASink() { none() }
/** Gets an API-node for this entry point. */
API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) }
@@ -523,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
@@ -567,7 +714,7 @@ module API {
base = MkRoot() and
exists(EntryPoint e |
lbl = Label::entryPoint(e) and
rhs = e.getARhs()
rhs = e.getASink()
)
or
exists(string m, string prop |
@@ -615,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 |
@@ -744,7 +881,7 @@ module API {
base = MkRoot() and
exists(EntryPoint e |
lbl = Label::entryPoint(e) and
ref = e.getAUse()
ref = e.getASource()
)
or
// property reads
@@ -1113,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 |
@@ -1178,8 +1319,8 @@ module API {
API::Node callee;
InvokeNode() {
this = callee.getReturn().getAnImmediateUse() or
this = callee.getInstance().getAnImmediateUse() or
this = callee.getReturn().asSource() or
this = callee.getInstance().asSource() or
this = Impl::getAPromisifiedInvocation(callee, _, _)
}
@@ -1194,7 +1335,7 @@ module API {
* Gets an API node where a RHS of the node is the `i`th argument to this call.
*/
pragma[noinline]
private Node getAParameterCandidate(int i) { result.getARhs() = this.getArgument(i) }
private Node getAParameterCandidate(int i) { result.asSink() = this.getArgument(i) }
/** Gets the API node for a parameter of this invocation. */
Node getAParameter() { result = this.getParameter(_) }
@@ -1205,13 +1346,13 @@ module API {
/** Gets the API node for the return value of this call. */
Node getReturn() {
result = callee.getReturn() and
result.getAnImmediateUse() = this
result.asSource() = this
}
/** Gets the API node for the object constructed by this invocation. */
Node getInstance() {
result = callee.getInstance() and
result.getAnImmediateUse() = this
result.asSource() = this
}
}
@@ -1354,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

@@ -36,12 +36,18 @@ module ArrayTaintTracking {
succ = call
)
or
// `array.filter(x => x)` keeps the taint
// `array.filter(x => x)` and `array.filter(x => !!x)` keeps the taint
call.(DataFlow::MethodCallNode).getMethodName() = "filter" and
pred = call.getReceiver() and
succ = call and
exists(DataFlow::FunctionNode callback | callback = call.getArgument(0).getAFunctionValue() |
callback.getParameter(0).getALocalUse() = callback.getAReturn()
exists(DataFlow::FunctionNode callback, DataFlow::Node param, DataFlow::Node ret |
callback = call.getArgument(0).getAFunctionValue() and
param = callback.getParameter(0).getALocalUse() and
ret = callback.getAReturn()
|
param = ret
or
param = DataFlow::exprNode(ret.asExpr().(LogNotExpr).getOperand().(LogNotExpr).getOperand())
)
or
// `array.reduce` with tainted value in callback
@@ -75,7 +81,7 @@ module ArrayTaintTracking {
succ.(DataFlow::SourceNode).getAMethodCall("splice") = call
or
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice"]) and
call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice", "at"]) and
succ = call
or
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
@@ -199,13 +205,13 @@ private module ArrayDataFlow {
}
/**
* A step for retrieving an element from an array using `.pop()` or `.shift()`.
* A step for retrieving an element from an array using `.pop()`, `.shift()`, or `.at()`.
* E.g. `array.pop()`.
*/
private class ArrayPopStep extends DataFlow::SharedFlowStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
exists(DataFlow::MethodCallNode call |
call.getMethodName() = ["pop", "shift"] and
call.getMethodName() = ["pop", "shift", "at"] and
prop = arrayElement() and
obj = call.getReceiver() and
element = call
@@ -255,14 +261,12 @@ private module ArrayDataFlow {
/**
* A step for creating an array and storing the elements in the array.
*/
private class ArrayCreationStep extends DataFlow::SharedFlowStep {
private class ArrayCreationStep extends PreCallGraphStep {
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
exists(DataFlow::ArrayCreationNode array, int i |
element = array.getElement(i) and
obj = array and
if array = any(PromiseAllCreation c).getArrayNode()
then prop = arrayElement(i)
else prop = arrayElement()
prop = arrayElement(i)
)
}
}
@@ -340,6 +344,14 @@ private module ArrayLibraries {
result = DataFlow::globalVarRef("Array").getAMemberCall("from")
or
result = DataFlow::moduleImport("array-from").getACall()
or
// Array.prototype.slice.call acts the same as Array.from, and is sometimes used with e.g. the arguments object.
result =
DataFlow::globalVarRef("Array")
.getAPropertyRead("prototype")
.getAPropertyRead("slice")
.getAMethodCall("call") and
result.getNumArgument() = 1
}
/**

View File

@@ -146,7 +146,7 @@ class BasicBlock extends @cfg_node, NodeInStmtContainer {
/** Holds if this basic block uses variable `v` in its `i`th node `u`. */
predicate useAt(int i, Variable v, VarUse u) { useAt(this, i, v, u) }
/** Holds if this basic block defines variable `v` in its `i`th node `u`. */
/** Holds if this basic block defines variable `v` in its `i`th node `d`. */
predicate defAt(int i, Variable v, VarDef d) { defAt(this, i, v, d) }
/**

View File

@@ -75,7 +75,7 @@ module CharacterEscapes {
}
/**
* Gets a character in `n` that is preceded by a single useless backslash, resulting in a likely regular expression mistake explained by `mistake`.
* Gets a character in `src` that is preceded by a single useless backslash, resulting in a likely regular expression mistake explained by `mistake`.
*
* The character is the `i`th character of the raw string value of `rawStringNode`.
*/

View File

@@ -172,7 +172,7 @@ class ClassDefinition extends @class_definition, ClassOrInterface, AST::ValueNod
/** Gets the expression denoting the super class of the defined class, if any. */
override Expr getSuperClass() { result = this.getChildExpr(1) }
/** Gets the `n`th type from the `implements` clause of this class, starting at 0. */
/** Gets the `i`th type from the `implements` clause of this class, starting at 0. */
override TypeExpr getSuperInterface(int i) {
// AST indices for super interfaces: -1, -4, -7, ...
exists(int astIndex | typeexprs(result, _, this, astIndex, _) |
@@ -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

@@ -9,118 +9,6 @@ private import semmle.javascript.dataflow.internal.StepSummary
private import semmle.javascript.dataflow.internal.PreCallGraphStep
private import DataFlow::PseudoProperties
/**
* DEPRECATED. Exists only to support other deprecated elements.
*
* Type-tracking now automatically determines the set of pseudo-properties to include
* ased on which properties are contributed by `SharedTaintStep`s.
*/
deprecated private class PseudoProperty extends string {
PseudoProperty() {
this = [arrayLikeElement(), "1"] or // the "1" is required for the `ForOfStep`.
this =
[
mapValue(any(DataFlow::CallNode c | c.getCalleeName() = "set").getArgument(0)),
mapValueAll()
]
}
}
/**
* DEPRECATED. Use `SharedFlowStep` or `SharedTaintTrackingStep` instead.
*/
abstract deprecated class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
final override predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate step(
DataFlow::Node p, DataFlow::Node s, DataFlow::FlowLabel pl, DataFlow::FlowLabel sl
) {
none()
}
/**
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
predicate load(DataFlow::Node pred, DataFlow::Node succ, PseudoProperty prop) { none() }
final override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
this.load(pred, succ, prop)
}
/**
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
*/
predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, PseudoProperty prop) { none() }
final override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
this.store(pred, succ, prop)
}
/**
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, PseudoProperty prop) { none() }
final override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
this.loadStore(pred, succ, prop, prop)
}
/**
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*/
predicate loadStore(
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty loadProp, PseudoProperty storeProp
) {
none()
}
final override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
this.loadStore(pred, succ, loadProp, storeProp)
}
}
/**
* DEPRECATED. These steps are now included in the default type tracking steps,
* in most cases one can simply use those instead.
*/
deprecated module CollectionsTypeTracking {
/**
* Gets the result from a single step through a collection, from `pred` to `result` summarized by `summary`.
*/
pragma[inline]
DataFlow::SourceNode collectionStep(DataFlow::Node pred, StepSummary summary) {
exists(PseudoProperty field |
summary = LoadStep(field) and
DataFlow::SharedTypeTrackingStep::loadStep(pred, result, field) and
not field = mapValueUnknownKey() // prune unknown reads in type-tracking
or
summary = StoreStep(field) and
DataFlow::SharedTypeTrackingStep::storeStep(pred, result, field)
or
summary = CopyStep(field) and
DataFlow::SharedTypeTrackingStep::loadStoreStep(pred, result, field)
or
exists(PseudoProperty toField | summary = LoadStoreStep(field, toField) |
DataFlow::SharedTypeTrackingStep::loadStoreStep(pred, result, field, toField)
)
)
}
/**
* Gets the result from a single step through a collection, from `pred` with tracker `t2` to `result` with tracker `t`.
*/
pragma[inline]
DataFlow::SourceNode collectionStep(
DataFlow::SourceNode pred, DataFlow::TypeTracker t, DataFlow::TypeTracker t2
) {
exists(DataFlow::Node mid, StepSummary summary | pred.flowsTo(mid) and t = t2.append(summary) |
result = collectionStep(mid, summary)
)
}
}
/**
* A module for data-flow steps related standard library collection implementations.
*/

View File

@@ -54,7 +54,7 @@ private predicate hasNamedExports(ES2015Module mod) {
}
/**
* Holds if this module contains a `default` export.
* Holds if this module contains a default export.
*/
private predicate hasDefaultExport(ES2015Module mod) {
// export default foo;
@@ -337,7 +337,7 @@ class BulkReExportDeclaration extends ReExportDeclaration, @export_all_declarati
}
/**
* Holds if the given bulk export should not re-export `name` because there is an explicit export
* Holds if the given bulk export `reExport` should not re-export `name` because there is an explicit export
* of that name in the same module.
*
* At compile time, shadowing works across declaration spaces.

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

@@ -175,6 +175,15 @@ class Folder extends Container, @folder {
result.getExtension() = extension
}
/** Like `getFile` except `d.ts` is treated as a single extension. */
private File getFileLongExtension(string stem, string extension) {
not (stem.matches("%.d") and extension = "ts") and
result = this.getFile(stem, extension)
or
extension = "d.ts" and
result = this.getFile(stem + ".d", "ts")
}
/**
* Gets the file in this folder that has the given `stem` and any of the supported JavaScript extensions.
*
@@ -188,7 +197,11 @@ class Folder extends Container, @folder {
*/
File getJavaScriptFile(string stem) {
result =
min(int p, string ext | p = getFileExtensionPriority(ext) | this.getFile(stem, ext) order by p)
min(int p, string ext |
p = getFileExtensionPriority(ext)
|
this.getFileLongExtension(stem, ext) order by p
)
}
/** Gets a subfolder contained in this folder. */
@@ -221,8 +234,6 @@ class File extends Container, @file {
/** Gets a toplevel piece of JavaScript code in this file. */
TopLevel getATopLevel() { result.getFile() = this }
override string toString() { result = Container.super.toString() }
/** Gets the URL of this file. */
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }

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

@@ -54,7 +54,9 @@ class JsonValue extends @json_value, Locatable {
int getIntValue() { result = this.(JsonNumber).getValue().toInt() }
/** If this is a boolean constant, gets its boolean value. */
boolean getBooleanValue() { result.toString() = this.(JsonBoolean).getValue() }
boolean getBooleanValue() {
result.toString() = this.(JsonBoolean).getValue() and result = [true, false]
}
override string getAPrimaryQlClass() { result = "JsonValue" }
}

View File

@@ -29,7 +29,7 @@ private class PlainJsonParserCall extends JsonParserCall {
callee =
DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"],
"parse") or
callee = API::moduleImport("replicator").getInstance().getMember("decode").getAnImmediateUse() or
callee = API::moduleImport("replicator").getInstance().getMember("decode").asSource() or
callee = DataFlow::moduleImport("parse-json") or
callee = DataFlow::moduleImport("json-parse-better-errors") or
callee = DataFlow::moduleImport("json-safe-parse") or

View File

@@ -134,7 +134,7 @@ module JsonSchema {
.ref()
.getMember(["addSchema", "validate", "compile", "compileAsync"])
.getParameter(0)
.getARhs()
.asSink()
}
}
}
@@ -184,7 +184,7 @@ module JsonSchema {
override boolean getPolarity() { none() }
override DataFlow::Node getAValidationResultAccess(boolean polarity) {
result = this.getReturn().getMember("error").getAnImmediateUse() and
result = this.getReturn().getMember("error").asSource() and
polarity = false
}
}

View File

@@ -14,7 +14,7 @@ class JsonStringifyCall extends DataFlow::CallNode {
callee =
DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"],
"stringify") or
callee = API::moduleImport("replicator").getInstance().getMember("encode").getAnImmediateUse() or
callee = API::moduleImport("replicator").getInstance().getMember("encode").asSource() or
callee =
DataFlow::moduleImport([
"json-stringify-safe", "json-stable-stringify", "stringify-object",
@@ -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

@@ -229,10 +229,10 @@ module MembershipCandidate {
membersNode = inExpr.getRightOperand()
)
or
exists(MethodCallExpr hasOwn |
this = hasOwn.getArgument(0).flow() and
test = hasOwn and
hasOwn.calls(membersNode, "hasOwnProperty")
exists(HasOwnPropertyCall hasOwn |
this = hasOwn.getProperty() and
test = hasOwn.asExpr() and
membersNode = hasOwn.getObject().asExpr()
)
}

View File

@@ -50,8 +50,23 @@ class PackageJson extends JsonObject {
/** Gets a file for this package. */
string getAFile() { result = this.getFiles().getElementStringValue(_) }
/** Gets the main module of this package. */
string getMain() { result = MainModulePath::of(this).getValue() }
/**
* Gets the main module of this package.
*
* This can be given by the `main` or `module` property, or via the
* `exports` property with the relative path `"."`.
*/
string getMain() { result = this.getExportedPath(".") }
/**
* Gets the path to the file exported with the given relative path.
*
* This can be given by the `exports` property, but also considers `main` and
* `module` paths to be exported under the relative path `"."`.
*/
string getExportedPath(string relativePath) {
result = MainModulePath::of(this, relativePath).getValue()
}
/** Gets the path of a command defined for this package. */
string getBin(string cmd) {
@@ -153,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" }
@@ -180,6 +201,47 @@ class PackageJson extends JsonObject {
Module getMainModule() {
result = min(Module m, int prio | m.getFile() = resolveMainModule(this, prio) | m order by prio)
}
/**
* Gets the module exported under the given relative path.
*
* The main module is considered exported under the path `"."`.
*/
Module getExportedModule(string relativePath) {
relativePath = "." and
result = this.getMainModule()
or
result.getFile() = MainModulePath::of(this, relativePath).resolve()
}
/**
* Gets the `types` or `typings` field of this package.
*/
string getTypings() { result = this.getPropStringValue(["types", "typings"]) }
/**
* Gets the file containing the typings of this package, which can either be from the `types` or
* `typings` field, or derived from the `main` or `module` fields.
*/
File getTypingsFile() {
result =
TypingsModulePathString::of(this).resolve(this.getFile().getParentContainer()).getContainer()
or
not exists(TypingsModulePathString::of(this)) and
exists(File mainFile |
mainFile = this.getMainModule().getFile() and
result =
mainFile
.getParentContainer()
.getFile(mainFile.getStem().regexpReplaceAll("\\.d$", "") + ".d.ts")
)
}
/**
* Gets the module containing the typings of this package, which can either be from the `types` or
* `typings` field, or derived from the `main` or `module` fields.
*/
Module getTypingsModule() { result.getFile() = this.getTypingsFile() }
}
/** DEPRECATED: Alias for PackageJson */

View File

@@ -3,6 +3,7 @@
import javascript
private import NodeModuleResolutionImpl
private import semmle.javascript.DynamicPropertyAccess as DynamicPropertyAccess
private import semmle.javascript.internal.CachedStages
/**
* A Node.js module.
@@ -113,6 +114,7 @@ class NodeModule extends Module {
}
override DataFlow::Node getABulkExportedNode() {
Stages::Imports::ref() and
exists(DataFlow::PropWrite write |
write.getBase().asExpr() = this.getModuleVariable().getAnAccess() and
write.getPropertyName() = "exports" and

View File

@@ -33,6 +33,8 @@ int getFileExtensionPriority(string ext) {
ext = "json" and result = 8
or
ext = "node" and result = 9
or
ext = "d.ts" and result = 10
}
int prioritiesPerCandidate() { result = 3 * (numberOfExtensions() + 1) }
@@ -88,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)
@@ -100,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 |
@@ -140,17 +163,28 @@ File resolveMainModule(PackageJson pkg, int priority) {
private string getASrcFolderName() { result = ["ts", "js", "src", "lib"] }
/**
* A JSON string in a `package.json` file specifying the path of the main
* module of the package.
* A JSON string in a `package.json` file specifying the path of one of the exported
* modules of the package.
*/
class MainModulePath extends PathExpr, @json_string {
PackageJson pkg;
MainModulePath() { this = pkg.getPropValue(["main", "module"]) }
MainModulePath() {
this = pkg.getPropValue(["main", "module"])
or
this = getAPartOfExportsSection(pkg)
}
/** Gets the `package.json` file in which this path occurs. */
PackageJson getPackageJson() { result = pkg }
/** Gets the relative path under which this is exported, usually starting with a `.`. */
string getRelativePath() {
result = getExportRelativePath(this)
or
not exists(getExportRelativePath(this)) and result = "."
}
/** DEPRECATED: Alias for getPackageJson */
deprecated PackageJSON getPackageJSON() { result = getPackageJson() }
@@ -162,8 +196,38 @@ class MainModulePath extends PathExpr, @json_string {
}
}
private JsonValue getAPartOfExportsSection(PackageJson pkg) {
result = pkg.getPropValue("exports")
or
result = getAPartOfExportsSection(pkg).getPropValue(_)
}
/** Gets the text of one of the conditions or paths enclosing the given `part` of an `exports` section. */
private string getAnEnclosingExportProperty(JsonValue part) {
exists(JsonObject parent, string prop |
parent = getAPartOfExportsSection(_) and
part = parent.getPropValue(prop)
|
result = prop
or
result = getAnEnclosingExportProperty(parent)
)
}
private string getExportRelativePath(JsonValue part) {
result = getAnEnclosingExportProperty(part) and
result.matches(".%")
}
module MainModulePath {
MainModulePath of(PackageJson pkg) { result.getPackageJson() = pkg }
/** Gets the path to the main entry point of `pkg`. */
MainModulePath of(PackageJson pkg) { result = of(pkg, ".") }
/** Gets the path to the file exported from `pkg` as `relativePath`. */
MainModulePath of(PackageJson pkg, string relativePath) {
result.getPackageJson() = pkg and
result.getRelativePath() = relativePath
}
}
/**
@@ -176,7 +240,7 @@ private class FilesPath extends PathExpr, @json_string {
FilesPath() {
this = pkg.getPropValue("files").(JsonArray).getElementValue(_) and
not exists(MainModulePath::of(pkg))
not exists(MainModulePath::of(pkg, _))
}
/** Gets the `package.json` file in which this path occurs. */
@@ -196,3 +260,29 @@ private class FilesPath extends PathExpr, @json_string {
private module FilesPath {
FilesPath of(PackageJson pkg) { result.getPackageJson() = pkg }
}
/**
* A JSON string in a `package.json` file specifying the path of the
* TypeScript typings entry point.
*/
class TypingsModulePathString extends PathString {
PackageJson pkg;
TypingsModulePathString() {
this = pkg.getTypings()
or
not exists(pkg.getTypings()) and
this = pkg.getMain().regexpReplaceAll("\\.[mc]?js$", ".d.ts")
}
/** Gets the `package.json` file containing this path. */
PackageJson getPackageJson() { result = pkg }
override Folder getARootFolder() { result = pkg.getFile().getParentContainer() }
}
/** Companion module to the `TypingsModulePathString` class. */
module TypingsModulePathString {
/** Get the typings path for the given `package.json` file. */
TypingsModulePathString of(PackageJson pkg) { result.getPackageJson() = pkg }
}

View File

@@ -11,36 +11,14 @@ private import semmle.javascript.internal.CachedStages
* Gets a parameter that is a library input to a top-level package.
*/
cached
DataFlow::SourceNode getALibraryInputParameter() {
DataFlow::Node getALibraryInputParameter() {
Stages::Taint::ref() and
exists(int bound, DataFlow::FunctionNode func |
func = getAValueExportedByPackage().getABoundFunctionValue(bound)
|
result = func.getParameter(any(int arg | arg >= bound))
or
result = getAnArgumentsRead(func.getFunction())
)
}
private DataFlow::SourceNode getAnArgumentsRead(Function func) {
exists(DataFlow::PropRead read |
not read.getPropertyName() = "length" and
result = read
|
read.getBase() = func.getArgumentsVariable().getAnAccess().flow()
or
exists(DataFlow::MethodCallNode call |
call =
DataFlow::globalVarRef("Array")
.getAPropertyRead("prototype")
.getAPropertyRead("slice")
.getAMethodCall("call")
or
call = DataFlow::globalVarRef("Array").getAMethodCall("from")
|
call.getArgument(0) = func.getArgumentsVariable().getAnAccess().flow() and
call.flowsTo(read.getBase())
)
result = func.getFunction().getArgumentsVariable().getAnAccess().flow()
)
}
@@ -52,7 +30,7 @@ private import NodeModuleResolutionImpl as NodeModule
private DataFlow::Node getAValueExportedByPackage() {
// The base case, an export from a named `package.json` file.
result =
getAnExportFromModule(any(PackageJson pack | exists(pack.getPackageName())).getMainModule())
getAnExportFromModule(any(PackageJson pack | exists(pack.getPackageName())).getExportedModule(_))
or
// module.exports.bar.baz = result;
exists(DataFlow::PropWrite write |
@@ -74,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

@@ -180,7 +180,7 @@ private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) {
}
/**
* Gets the `i`th component of the path `str`, where `base` is the resolved path one level up.
* Gets the `n`th component of the path `str`, where `base` is the resolved path one level up.
* Supports that the root directory might be compiled output from TypeScript.
* `inTS` is true if the result is TypeScript that is compiled into the path specified by `str`.
*/
@@ -227,7 +227,7 @@ private module TypeScriptOutDir {
}
/**
* Gets the `outDir` option from a tsconfig file from the folder `parent`.
* Gets the "outDir" option from a `tsconfig` file from the folder `parent`.
*/
private string getOutDir(JsonObject tsconfig, Folder parent) {
tsconfig.getFile().getBaseName().regexpMatch("tsconfig.*\\.json") and

View File

@@ -36,7 +36,7 @@ private predicate isNotNeeded(Locatable el) {
el.getLocation().getStartLine() = 0 and
el.getLocation().getStartColumn() = 0
or
// relaxing aggresive type inference.
// relaxing aggressive type inference.
none()
}
@@ -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
@@ -195,7 +195,7 @@ private module PrintJavaScript {
* Gets the `i`th child of `element`.
* Can be overridden in subclasses to get more specific behavior for `getChild()`.
*/
AstNode getChildNode(int childIndex) { result = getLocationSortedChild(element, childIndex) }
AstNode getChildNode(int i) { result = getLocationSortedChild(element, i) }
}
/** Provides predicates for pretty printing `AstNode`s. */
@@ -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

@@ -189,6 +189,13 @@ module Promises {
* Gets the pseudo-field used to describe rejected values in a promise.
*/
string errorProp() { result = "$PromiseRejectField$" }
/** A property set containing the pseudo-properites of a promise object. */
class PromiseProps extends DataFlow::PropertySet {
PromiseProps() { this = "PromiseProps" }
override string getAProperty() { result = [valueProp(), errorProp()] }
}
}
/**
@@ -274,6 +281,24 @@ private class PromiseStep extends PreCallGraphStep {
}
}
/**
* A step from `p -> await p` for the case where `p` is not a promise.
*
* In this case, `await p` just returns `p` itself. We block flow of the promise-related
* pseudo properties through this edge.
*/
private class RawAwaitStep extends DataFlow::SharedTypeTrackingStep {
override predicate withoutPropStep(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::PropertySet props
) {
exists(AwaitExpr await |
pred = await.getOperand().flow() and
succ = await.flow() and
props instanceof Promises::PromiseProps
)
}
}
/**
* This module defines how data-flow propagates into and out of a Promise.
* The data-flow is based on pseudo-properties rather than tainting the Promise object (which is what `PromiseTaintStep` does).

View File

@@ -260,7 +260,7 @@ module RangeAnalysis {
}
/**
* Holds if the given comparison can be modeled as `A <op> B + bias` where `<op>` is the comparison operator,
* Holds if the given `comparison` can be modeled as `A <op> B + bias` where `<op>` is the comparison operator,
* and `A` is `a * asign` and likewise `B` is `b * bsign`.
*/
predicate linearComparison(
@@ -310,18 +310,18 @@ module RangeAnalysis {
* Holds if `guard` asserts that the outcome of `A <op> B + bias` is true, where `<op>` is a comparison operator.
*/
predicate linearComparisonGuard(
ConditionGuardNode guard, DataFlow::Node a, int asign, string operator, DataFlow::Node b,
int bsign, Bias bias
ConditionGuardNode guard, DataFlow::Node a, int asign, string op, DataFlow::Node b, int bsign,
Bias bias
) {
exists(Comparison compare |
compare = guard.getTest().flow().getImmediatePredecessor*().asExpr() and
linearComparison(compare, a, asign, b, bsign, bias) and
(
guard.getOutcome() = true and operator = compare.getOperator()
guard.getOutcome() = true and op = compare.getOperator()
or
not hasNaNIndicator(guard.getContainer()) and
guard.getOutcome() = false and
operator = negateOperator(compare.getOperator())
op = negateOperator(compare.getOperator())
)
)
}
@@ -657,13 +657,13 @@ module RangeAnalysis {
*/
pragma[noopt]
private predicate reachableByNegativeEdges(
DataFlow::Node a, int asign, DataFlow::Node b, int bsign, ControlFlowNode cfg
DataFlow::Node src, int asign, DataFlow::Node dst, int bsign, ControlFlowNode cfg
) {
negativeEdge(a, asign, b, bsign, cfg)
negativeEdge(src, asign, dst, bsign, cfg)
or
exists(DataFlow::Node mid, int midx, ControlFlowNode midcfg |
reachableByNegativeEdges(a, asign, mid, midx, cfg) and
negativeEdge(mid, midx, b, bsign, midcfg) and
reachableByNegativeEdges(src, asign, mid, midx, cfg) and
negativeEdge(mid, midx, dst, bsign, midcfg) and
exists(BasicBlock bb, int i, int j |
bb.getNode(i) = midcfg and
bb.getNode(j) = cfg and
@@ -676,8 +676,8 @@ module RangeAnalysis {
DataFlow::Node mid, int midx, ControlFlowNode midcfg, BasicBlock midBB,
ReachableBasicBlock midRBB, BasicBlock cfgBB
|
reachableByNegativeEdges(a, asign, mid, midx, cfg) and
negativeEdge(mid, midx, b, bsign, midcfg) and
reachableByNegativeEdges(src, asign, mid, midx, cfg) and
negativeEdge(mid, midx, dst, bsign, midcfg) and
midBB = midcfg.getBasicBlock() and
midRBB = midBB.(ReachableBasicBlock) and
cfgBB = cfg.getBasicBlock() and

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

@@ -148,6 +148,18 @@ module Routing {
this instanceof MkRouter
}
/**
* Like `mayResumeDispatch` but without the assumption that functions with an unknown
* implementation invoke their continuation.
*/
predicate definitelyResumesDispatch() {
this.getLastChild().definitelyResumesDispatch()
or
exists(this.(RouteHandler).getAContinuationInvocation())
or
this instanceof MkRouter
}
/** Gets the parent of this node, provided that this node may invoke its continuation. */
private Node getContinuationParent() {
result = this.getParent() and
@@ -229,11 +241,11 @@ module Routing {
}
/**
* Holds if `node` has processed the incoming request strictly prior to this node.
* Holds if `guard` has processed the incoming request strictly prior to this node.
*/
pragma[inline]
private predicate isGuardedByNodeInternal(Node guard) {
// Look for a common ancestor `fork` whose child leading to `guard` ("base1") preceeds
// Look for a common ancestor `fork` whose child leading to `guard` ("base1") precedes
// the child leading to `this` ("base2").
//
// Schematically:

View File

@@ -501,7 +501,7 @@ class SsaExplicitDefinition extends SsaDefinition, TExplicitDef {
}
/** This SSA definition corresponds to the definition of `v` at `def`. */
predicate defines(VarDef d, SsaSourceVariable v) { this = TExplicitDef(_, _, d, v) }
predicate defines(VarDef def, SsaSourceVariable v) { this = TExplicitDef(_, _, def, v) }
/** Gets the variable definition wrapped by this SSA definition. */
VarDef getDef() { this = TExplicitDef(_, _, result, _) }

View File

@@ -192,3 +192,35 @@ class StringSplitCall extends DataFlow::MethodCallNode {
bindingset[i]
DataFlow::Node getASubstringRead(int i) { result = this.getAPropertyRead(i.toString()) }
}
/**
* A call to `Object.prototype.hasOwnProperty`, `Object.hasOwn`, or a library that implements
* the same functionality.
*/
class HasOwnPropertyCall extends DataFlow::Node instanceof DataFlow::CallNode {
DataFlow::Node object;
DataFlow::Node property;
HasOwnPropertyCall() {
// Make sure we handle reflective calls since libraries love to do that.
super.getCalleeNode().getALocalSource().(DataFlow::PropRead).getPropertyName() =
"hasOwnProperty" and
object = super.getReceiver() and
property = super.getArgument(0)
or
this =
[
DataFlow::globalVarRef("Object").getAMemberCall("hasOwn"), //
DataFlow::moduleImport("has").getACall(), //
LodashUnderscore::member("has").getACall()
] and
object = super.getArgument(0) and
property = super.getArgument(1)
}
/** Gets the object whose property is being checked. */
DataFlow::Node getObject() { result = object }
/** Gets the property being checked. */
DataFlow::Node getProperty() { result = property }
}

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

@@ -751,7 +751,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef {
}
/**
* Gets a suitable name for the library imported by `import`.
* Gets a suitable name for the library imported by `imprt`.
*
* For relative imports, this is the snapshot-relative path to the imported module.
* For non-relative imports, it is the import path itself.
@@ -896,21 +896,28 @@ class ArrayTypeExpr extends @array_typeexpr, TypeExpr {
override string getAPrimaryQlClass() { result = "ArrayTypeExpr" }
}
private class RawUnionOrIntersectionTypeExpr = @union_typeexpr or @intersection_typeexpr;
/**
* A union type, such as `string|number|boolean`.
* A union or intersection type, such as `string|number|boolean` or `A & B`.
*/
class UnionTypeExpr extends @union_typeexpr, TypeExpr {
/** Gets the `n`th type in the union, starting at 0. */
class UnionOrIntersectionTypeExpr extends RawUnionOrIntersectionTypeExpr, TypeExpr {
/** Gets the `n`th type in the union or intersection, starting at 0. */
TypeExpr getElementType(int n) { result = this.getChildTypeExpr(n) }
/** Gets any of the types in the union. */
/** Gets any of the types in the union or intersection. */
TypeExpr getAnElementType() { result = this.getElementType(_) }
/** Gets the number of types in the union. This is always at least two. */
/** Gets the number of types in the union or intersection. This is always at least two. */
int getNumElementType() { result = count(this.getAnElementType()) }
override TypeExpr getAnUnderlyingType() { result = this.getAnElementType().getAnUnderlyingType() }
}
/**
* A union type, such as `string|number|boolean`.
*/
class UnionTypeExpr extends @union_typeexpr, UnionOrIntersectionTypeExpr {
override string getAPrimaryQlClass() { result = "UnionTypeExpr" }
}
@@ -932,18 +939,7 @@ class IndexedAccessTypeExpr extends @indexed_access_typeexpr, TypeExpr {
*
* In general, there are can more than two operands to an intersection type.
*/
class IntersectionTypeExpr extends @intersection_typeexpr, TypeExpr {
/** Gets the `n`th operand of the intersection type, starting at 0. */
TypeExpr getElementType(int n) { result = this.getChildTypeExpr(n) }
/** Gets any of the operands to the intersection type. */
TypeExpr getAnElementType() { result = this.getElementType(_) }
/** Gets the number of operands to the intersection type. This is always at least two. */
int getNumElementType() { result = count(this.getAnElementType()) }
override TypeExpr getAnUnderlyingType() { result = this.getAnElementType().getAnUnderlyingType() }
class IntersectionTypeExpr extends @intersection_typeexpr, UnionOrIntersectionTypeExpr {
override string getAPrimaryQlClass() { result = "IntersectionTypeExpr" }
}
@@ -1286,6 +1282,8 @@ class ExpressionWithTypeArguments extends @expression_with_type_arguments, Expr
override ControlFlowNode getFirstControlFlowNode() {
result = this.getExpression().getFirstControlFlowNode()
}
override string getAPrimaryQlClass() { result = "ExpressionWithTypeArguments" }
}
/**

View File

@@ -873,6 +873,18 @@ class DeclarationSpace extends string {
DeclarationSpace() { this = "variable" or this = "type" or this = "namespace" }
}
/** Module containing the `DeclarationSpace` constants. */
module DeclarationSpace {
/** Gets the declaration space for variables/values. */
DeclarationSpace variable() { result = "variable" }
/** Gets the declaration space for types. */
DeclarationSpace type() { result = "type" }
/** Gets the declaration space for namespaces. */
DeclarationSpace namespace() { result = "namespace" }
}
/**
* A name that is declared in a particular scope.
*

107
javascript/ql/lib/semmle/javascript/XML.qll Executable file → Normal file
View File

@@ -8,7 +8,7 @@ private class TXmlLocatable =
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
/** An XML element that has a location. */
class XMLLocatable extends @xmllocatable, TXmlLocatable {
class XmlLocatable extends @xmllocatable, TXmlLocatable {
/** Gets the source location for this element. */
Location getLocation() { xmllocations(this, result) }
@@ -32,13 +32,16 @@ class XMLLocatable extends @xmllocatable, TXmlLocatable {
string toString() { none() } // overridden in subclasses
}
/** DEPRECATED: Alias for XmlLocatable */
deprecated class XMLLocatable = XmlLocatable;
/**
* An `XMLParent` is either an `XMLElement` or an `XMLFile`,
* An `XmlParent` is either an `XmlElement` or an `XmlFile`,
* both of which can contain other elements.
*/
class XMLParent extends @xmlparent {
XMLParent() {
// explicitly restrict `this` to be either an `XMLElement` or an `XMLFile`;
class XmlParent extends @xmlparent {
XmlParent() {
// explicitly restrict `this` to be either an `XmlElement` or an `XmlFile`;
// the type `@xmlparent` currently also includes non-XML files
this instanceof @xmlelement or xmlEncoding(this, _)
}
@@ -50,28 +53,28 @@ class XMLParent extends @xmlparent {
string getName() { none() } // overridden in subclasses
/** Gets the file to which this XML parent belongs. */
XMLFile getFile() { result = this or xmlElements(this, _, _, _, result) }
XmlFile getFile() { result = this or xmlElements(this, _, _, _, result) }
/** Gets the child element at a specified index of this XML parent. */
XMLElement getChild(int index) { xmlElements(result, _, this, index, _) }
XmlElement getChild(int index) { xmlElements(result, _, this, index, _) }
/** Gets a child element of this XML parent. */
XMLElement getAChild() { xmlElements(result, _, this, _, _) }
XmlElement getAChild() { xmlElements(result, _, this, _, _) }
/** Gets a child element of this XML parent with the given `name`. */
XMLElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
XmlElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
/** Gets a comment that is a child of this XML parent. */
XMLComment getAComment() { xmlComments(result, _, this, _) }
XmlComment getAComment() { xmlComments(result, _, this, _) }
/** Gets a character sequence that is a child of this XML parent. */
XMLCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
XmlCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
/** Gets the depth in the tree. (Overridden in XMLElement.) */
/** Gets the depth in the tree. (Overridden in XmlElement.) */
int getDepth() { result = 0 }
/** Gets the number of child XML elements of this XML parent. */
int getNumberOfChildren() { result = count(XMLElement e | xmlElements(e, _, this, _, _)) }
int getNumberOfChildren() { result = count(XmlElement e | xmlElements(e, _, this, _, _)) }
/** Gets the number of places in the body of this XML parent where text occurs. */
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
@@ -92,9 +95,12 @@ class XMLParent extends @xmlparent {
string toString() { result = this.getName() }
}
/** DEPRECATED: Alias for XmlParent */
deprecated class XMLParent = XmlParent;
/** An XML file. */
class XMLFile extends XMLParent, File {
XMLFile() { xmlEncoding(this, _) }
class XmlFile extends XmlParent, File {
XmlFile() { xmlEncoding(this, _) }
/** Gets a printable representation of this XML file. */
override string toString() { result = this.getName() }
@@ -120,15 +126,21 @@ class XMLFile extends XMLParent, File {
string getEncoding() { xmlEncoding(this, result) }
/** Gets the XML file itself. */
override XMLFile getFile() { result = this }
override XmlFile getFile() { result = this }
/** Gets a top-most element in an XML file. */
XMLElement getARootElement() { result = this.getAChild() }
XmlElement getARootElement() { result = this.getAChild() }
/** Gets a DTD associated with this XML file. */
XMLDTD getADTD() { xmlDTDs(result, _, _, _, this) }
XmlDtd getADtd() { xmlDTDs(result, _, _, _, this) }
/** DEPRECATED: Alias for getADtd */
deprecated XmlDtd getADTD() { result = this.getADtd() }
}
/** DEPRECATED: Alias for XmlFile */
deprecated class XMLFile = XmlFile;
/**
* An XML document type definition (DTD).
*
@@ -140,7 +152,7 @@ class XMLFile extends XMLParent, File {
* <!ELEMENT lastName (#PCDATA)>
* ```
*/
class XMLDTD extends XMLLocatable, @xmldtd {
class XmlDtd extends XmlLocatable, @xmldtd {
/** Gets the name of the root element of this DTD. */
string getRoot() { xmlDTDs(this, result, _, _, _) }
@@ -154,7 +166,7 @@ class XMLDTD extends XMLLocatable, @xmldtd {
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }
/** Gets the parent of this DTD. */
XMLParent getParent() { xmlDTDs(this, _, _, _, result) }
XmlParent getParent() { xmlDTDs(this, _, _, _, result) }
override string toString() {
this.isPublic() and
@@ -165,6 +177,9 @@ class XMLDTD extends XMLLocatable, @xmldtd {
}
}
/** DEPRECATED: Alias for XmlDtd */
deprecated class XMLDTD = XmlDtd;
/**
* An XML element in an XML file.
*
@@ -176,7 +191,7 @@ class XMLDTD extends XMLLocatable, @xmldtd {
* </manifest>
* ```
*/
class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
class XmlElement extends @xmlelement, XmlParent, XmlLocatable {
/** Holds if this XML element has the given `name`. */
predicate hasName(string name) { name = this.getName() }
@@ -184,10 +199,10 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
override string getName() { xmlElements(this, result, _, _, _) }
/** Gets the XML file in which this XML element occurs. */
override XMLFile getFile() { xmlElements(this, _, _, _, result) }
override XmlFile getFile() { xmlElements(this, _, _, _, result) }
/** Gets the parent of this XML element. */
XMLParent getParent() { xmlElements(this, _, result, _, _) }
XmlParent getParent() { xmlElements(this, _, result, _, _) }
/** Gets the index of this XML element among its parent's children. */
int getIndex() { xmlElements(this, _, _, result, _) }
@@ -196,7 +211,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
predicate hasNamespace() { xmlHasNs(this, _, _) }
/** Gets the namespace of this XML element, if any. */
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }
/** Gets the index of this XML element among its parent's children. */
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }
@@ -205,10 +220,10 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
override int getDepth() { result = this.getParent().getDepth() + 1 }
/** Gets an XML attribute of this XML element. */
XMLAttribute getAnAttribute() { result.getElement() = this }
XmlAttribute getAnAttribute() { result.getElement() = this }
/** Gets the attribute with the specified `name`, if any. */
XMLAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
XmlAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
/** Holds if this XML element has an attribute with the specified `name`. */
predicate hasAttribute(string name) { exists(this.getAttribute(name)) }
@@ -220,6 +235,9 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
override string toString() { result = this.getName() }
}
/** DEPRECATED: Alias for XmlElement */
deprecated class XMLElement = XmlElement;
/**
* An attribute that occurs inside an XML element.
*
@@ -230,18 +248,18 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
* android:versionCode="1"
* ```
*/
class XMLAttribute extends @xmlattribute, XMLLocatable {
class XmlAttribute extends @xmlattribute, XmlLocatable {
/** Gets the name of this attribute. */
string getName() { xmlAttrs(this, _, result, _, _, _) }
/** Gets the XML element to which this attribute belongs. */
XMLElement getElement() { xmlAttrs(this, result, _, _, _, _) }
XmlElement getElement() { xmlAttrs(this, result, _, _, _, _) }
/** Holds if this attribute has a namespace. */
predicate hasNamespace() { xmlHasNs(this, _, _) }
/** Gets the namespace of this attribute, if any. */
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }
/** Gets the value of this attribute. */
string getValue() { xmlAttrs(this, _, _, result, _, _) }
@@ -250,6 +268,9 @@ class XMLAttribute extends @xmlattribute, XMLLocatable {
override string toString() { result = this.getName() + "=" + this.getValue() }
}
/** DEPRECATED: Alias for XmlAttribute */
deprecated class XMLAttribute = XmlAttribute;
/**
* A namespace used in an XML file.
*
@@ -259,23 +280,29 @@ class XMLAttribute extends @xmlattribute, XMLLocatable {
* xmlns:android="http://schemas.android.com/apk/res/android"
* ```
*/
class XMLNamespace extends XMLLocatable, @xmlnamespace {
class XmlNamespace extends XmlLocatable, @xmlnamespace {
/** Gets the prefix of this namespace. */
string getPrefix() { xmlNs(this, result, _, _) }
/** Gets the URI of this namespace. */
string getURI() { xmlNs(this, _, result, _) }
string getUri() { xmlNs(this, _, result, _) }
/** DEPRECATED: Alias for getUri */
deprecated string getURI() { result = this.getUri() }
/** Holds if this namespace has no prefix. */
predicate isDefault() { this.getPrefix() = "" }
override string toString() {
this.isDefault() and result = this.getURI()
this.isDefault() and result = this.getUri()
or
not this.isDefault() and result = this.getPrefix() + ":" + this.getURI()
not this.isDefault() and result = this.getPrefix() + ":" + this.getUri()
}
}
/** DEPRECATED: Alias for XmlNamespace */
deprecated class XMLNamespace = XmlNamespace;
/**
* A comment in an XML file.
*
@@ -285,17 +312,20 @@ class XMLNamespace extends XMLLocatable, @xmlnamespace {
* <!-- This is a comment. -->
* ```
*/
class XMLComment extends @xmlcomment, XMLLocatable {
class XmlComment extends @xmlcomment, XmlLocatable {
/** Gets the text content of this XML comment. */
string getText() { xmlComments(this, result, _, _) }
/** Gets the parent of this XML comment. */
XMLParent getParent() { xmlComments(this, _, result, _) }
XmlParent getParent() { xmlComments(this, _, result, _) }
/** Gets a printable representation of this XML comment. */
override string toString() { result = this.getText() }
}
/** DEPRECATED: Alias for XmlComment */
deprecated class XMLComment = XmlComment;
/**
* A sequence of characters that occurs between opening and
* closing tags of an XML element, excluding other elements.
@@ -306,12 +336,12 @@ class XMLComment extends @xmlcomment, XMLLocatable {
* <content>This is a sequence of characters.</content>
* ```
*/
class XMLCharacters extends @xmlcharacters, XMLLocatable {
class XmlCharacters extends @xmlcharacters, XmlLocatable {
/** Gets the content of this character sequence. */
string getCharacters() { xmlChars(this, result, _, _, _, _) }
/** Gets the parent of this character sequence. */
XMLParent getParent() { xmlChars(this, _, result, _, _, _) }
XmlParent getParent() { xmlChars(this, _, result, _, _, _) }
/** Holds if this character sequence is CDATA. */
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }
@@ -319,3 +349,6 @@ class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}
/** DEPRECATED: Alias for XmlCharacters */
deprecated class XMLCharacters = XmlCharacters;

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

@@ -353,7 +353,7 @@ abstract class BarrierGuardNode extends DataFlow::Node {
}
/**
* Holds if data flow node `nd` acts as a barrier for data flow.
* Holds if data flow node `guard` acts as a barrier for data flow.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
@@ -382,7 +382,7 @@ private predicate barrierGuardIsRelevant(BarrierGuardNode guard) {
}
/**
* Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through
* Holds if data flow node `guard` acts as a barrier for data flow due to aliasing through
* an access path.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
@@ -523,74 +523,6 @@ abstract class LabeledBarrierGuardNode extends BarrierGuardNode {
override predicate blocks(boolean outcome, Expr e) { none() }
}
/**
* DEPRECATED. Subclasses should extend `SharedFlowStep` instead, unless the subclass
* is part of a query, in which case it should be moved into the `isAdditionalFlowStep` predicate
* of the relevant data-flow configuration.
* Other uses of the predicate in this class should instead reference the predicates in the
* `SharedFlowStep::` module, such as `SharedFlowStep::step`.
*
* A data flow edge that should be added to all data flow configurations in
* addition to standard data flow edges.
*
* Note: For performance reasons, all subclasses of this class should be part
* of the standard library. Override `Configuration::isAdditionalFlowStep`
* for analysis-specific flow steps.
*/
deprecated class AdditionalFlowStep = LegacyAdditionalFlowStep;
// Internal version of AdditionalFlowStep that we can reference without deprecation warnings.
abstract private class LegacyAdditionalFlowStep extends DataFlow::Node {
/**
* Holds if `pred` &rarr; `succ` should be considered a data flow edge.
*/
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* Holds if `pred` &rarr; `succ` should be considered a data flow edge
* transforming values with label `predlbl` to have label `succlbl`.
*/
predicate step(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
) {
none()
}
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
* The object `succ` must be a `DataFlow::SourceNode` for the object wherein the value is stored.
*/
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*/
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
none()
}
}
/**
* A data flow edge that should be added to all data flow configurations in
* addition to standard data flow edges.
@@ -713,40 +645,6 @@ module SharedFlowStep {
}
}
/**
* Contributes subclasses of `AdditionalFlowStep` to `SharedFlowStep`.
*/
private class AdditionalFlowStepAsSharedStep extends SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
any(LegacyAdditionalFlowStep s).step(pred, succ)
}
override predicate step(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
) {
any(LegacyAdditionalFlowStep s).step(pred, succ, predlbl, succlbl)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(LegacyAdditionalFlowStep s).storeStep(pred, succ, prop)
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(LegacyAdditionalFlowStep s).loadStep(pred, succ, prop)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(LegacyAdditionalFlowStep s).loadStoreStep(pred, succ, prop)
}
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
any(LegacyAdditionalFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
}
}
/**
* A collection of pseudo-properties that are used in multiple files.
*
@@ -1155,7 +1053,7 @@ private predicate appendStep(
}
/**
* Holds if a function invoked at `invk` may return an expression into which `input`,
* Holds if a function invoked at `output` may return an expression into which `input`,
* which is either an argument or a definition captured by the function, flows under
* configuration `cfg`, possibly through callees.
*/
@@ -1369,56 +1267,47 @@ private predicate loadStep(
* If `onlyRelevantInCall` is true, the `base` object will not be propagated out of return edges, because
* the flow that originally reached `base.startProp` used a call edge.
*/
pragma[nomagic]
pragma[noopt]
private predicate reachableFromStoreBase(
string startProp, string endProp, DataFlow::Node base, DataFlow::Node nd,
DataFlow::Configuration cfg, PathSummary summary, boolean onlyRelevantInCall
DataFlow::Configuration cfg, TPathSummary summary, boolean onlyRelevantInCall
) {
exists(PathSummary s1, PathSummary s2, DataFlow::Node rhs |
reachableFromSource(rhs, cfg, s1) and
onlyRelevantInCall = s1.hasCall()
or
reachableFromStoreBase(_, _, _, rhs, cfg, s1, onlyRelevantInCall)
|
exists(TPathSummary s1, TPathSummary s2, DataFlow::Node rhs |
storeStep(rhs, nd, startProp, cfg, s2) and
endProp = startProp and
base = nd and
summary =
MkPathSummary(false, s2.hasCall(), DataFlow::FlowLabel::data(), DataFlow::FlowLabel::data())
exists(boolean hasCall, DataFlow::FlowLabel data |
hasCall = hasCall(s2) and
data = DataFlow::FlowLabel::data() and
summary = MkPathSummary(false, hasCall, data, data)
)
|
reachableFromSource(rhs, cfg, s1) and
onlyRelevantInCall = hasCall(s1)
or
reachableFromStoreBase(_, _, _, rhs, cfg, s1, onlyRelevantInCall)
)
or
exists(PathSummary newSummary, PathSummary oldSummary |
reachableFromStoreBaseStep(startProp, endProp, base, nd, cfg, oldSummary, newSummary,
onlyRelevantInCall) and
summary = oldSummary.appendValuePreserving(newSummary)
)
}
/**
* Holds if `base` is the base of a write to property `prop`, and `nd` is reachable
* from `base` under configuration `cfg` (possibly through callees) along a path whose
* last step is summarized by `newSummary`, and the previous steps are summarized
* by `oldSummary`.
*/
pragma[noinline]
private predicate reachableFromStoreBaseStep(
string startProp, string endProp, DataFlow::Node base, DataFlow::Node nd,
DataFlow::Configuration cfg, PathSummary oldSummary, PathSummary newSummary,
boolean onlyRelevantInCall
) {
exists(DataFlow::Node mid |
exists(DataFlow::Node mid, PathSummary oldSummary, PathSummary newSummary |
reachableFromStoreBase(startProp, endProp, base, mid, cfg, oldSummary, onlyRelevantInCall) and
flowStep(mid, cfg, nd, newSummary) and
onlyRelevantInCall.booleanAnd(newSummary.hasReturn()) = false
exists(boolean hasReturn |
hasReturn = newSummary.hasReturn() and
onlyRelevantInCall.booleanAnd(hasReturn) = false
)
or
exists(string midProp |
reachableFromStoreBase(startProp, midProp, base, mid, cfg, oldSummary, onlyRelevantInCall) and
isAdditionalLoadStoreStep(mid, nd, midProp, endProp, cfg) and
newSummary = PathSummary::level()
)
|
summary = oldSummary.appendValuePreserving(newSummary)
)
}
private boolean hasCall(PathSummary summary) { result = summary.hasCall() }
/**
* Holds if the value of `pred` is written to a property of some base object, and that base
* object may flow into the base of property read `succ` under configuration `cfg` along
@@ -1758,7 +1647,7 @@ class PathNode extends TPathNode {
this = MkSinkNode(nd, cfg)
}
/** Holds if this path node wraps data-flow node `nd` and configuration `c`. */
/** Holds if this path node wraps data-flow node `n` and configuration `c`. */
predicate wraps(DataFlow::Node n, DataFlow::Configuration c) { nd = n and cfg = c }
/** Gets the underlying configuration of this path node. */
@@ -1873,7 +1762,7 @@ class MidPathNode extends PathNode, MkMidNode {
MidPathNode() { this = MkMidNode(nd, cfg, summary) }
/** Holds if this path node wraps data-flow node `nd`, configuration `c` and summary `s`. */
/** Holds if this path node wraps data-flow node `n`, configuration `c` and summary `s`. */
predicate wraps(DataFlow::Node n, DataFlow::Configuration c, PathSummary s) {
nd = n and cfg = c and summary = s
}
@@ -2018,6 +1907,7 @@ private class BarrierGuardFunction extends Function {
BarrierGuardNode guard;
boolean guardOutcome;
string label;
int paramIndex;
BarrierGuardFunction() {
barrierGuardIsRelevant(guard) and
@@ -2041,8 +1931,7 @@ private class BarrierGuardFunction extends Function {
sanitizedParameter.flowsToExpr(e) and
barrierGuardBlocksExpr(guard, guardOutcome, e, label)
) and
getNumParameter() = 1 and
sanitizedParameter.getParameter() = getParameter(0)
sanitizedParameter.getParameter() = getParameter(paramIndex)
}
/**
@@ -2050,10 +1939,10 @@ private class BarrierGuardFunction extends Function {
*/
predicate isBarrierCall(DataFlow::CallNode call, Expr e, boolean outcome, string lbl) {
exists(DataFlow::Node arg |
argumentPassing(pragma[only_bind_into](call), pragma[only_bind_into](arg),
pragma[only_bind_into](this), pragma[only_bind_into](sanitizedParameter)) and
arg.asExpr() = e and
arg = call.getArgument(0) and
call.getNumArgument() = 1 and
argumentPassing(call, arg, this, sanitizedParameter) and
arg = call.getArgument(paramIndex) and
outcome = guardOutcome and
lbl = label
)

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.
@@ -1029,6 +1032,32 @@ module DataFlow {
override File getFile() { result = function.getFile() }
}
/**
* A data flow node representing the arguments object given to a function.
*/
class ReflectiveParametersNode extends DataFlow::Node, TReflectiveParametersNode {
Function function;
ReflectiveParametersNode() { this = TReflectiveParametersNode(function) }
override string toString() { result = "'arguments' object of " + function.describe() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
function.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override BasicBlock getBasicBlock() { result = function.getEntry().getBasicBlock() }
/**
* Gets the function whose `arguments` object is represented by this node.
*/
Function getFunction() { result = function }
override File getFile() { result = function.getFile() }
}
/**
* A data flow node representing the exceptions thrown by the callee of an invocation.
*/
@@ -1627,6 +1656,35 @@ module DataFlow {
exists(Function f | not f.isAsyncOrGenerator() |
DataFlow::functionReturnNode(succ, f) and pred = valueNode(f.getAReturnedExpr())
)
or
// from a reflective params node to a reference to the arguments object.
exists(DataFlow::ReflectiveParametersNode params, Function f | f = params.getFunction() |
succ = f.getArgumentsVariable().getAnAccess().flow() and
pred = params
)
}
/** A load step from a reflective parameter node to each parameter. */
private class ReflectiveParamsStep extends PreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f, int i |
f.getFunction() = params.getFunction() and
obj = params and
prop = i + "" and
element = f.getParameter(i)
)
}
}
/** A taint step from the reflective parameters node to any parameter. */
private class ReflectiveParamsTaintStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node obj, DataFlow::Node element) {
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f |
f.getFunction() = params.getFunction() and
obj = params and
element = f.getAParameter()
)
}
}
/**
@@ -1653,7 +1711,7 @@ module DataFlow {
}
/**
* Holds if the flow information for this node is incomplete.
* Holds if the flow information for the node `nd`.
*
* This predicate holds if there may be a source flow node from which data flows into
* this node, but that node is not a result of `getALocalSource()` due to analysis incompleteness.

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

@@ -498,7 +498,7 @@ private module ReturnPortal {
invk = callee.getAnExitNode(isRemote).getAnInvocation()
}
/** Holds if `ret` is a return node of a function flowing through `callee`. */
/** Holds if `ret` is a return node of a function flowing through `base`. */
predicate returns(Portal base, DataFlow::Node ret, boolean escapes) {
ret = base.getAnEntryNode(escapes).getALocalSource().(DataFlow::FunctionNode).getAReturn()
}

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.
*
@@ -33,13 +38,7 @@ private import semmle.javascript.internal.CachedStages
* import("fs")
* ```
*/
class SourceNode extends DataFlow::Node {
SourceNode() {
this instanceof SourceNode::Range
or
none() and this instanceof SourceNode::Internal::RecursionGuard
}
class SourceNode extends DataFlow::Node instanceof SourceNode::Range {
/**
* Holds if this node flows into `sink` in zero or more local (that is,
* intra-procedural) steps.
@@ -338,14 +337,10 @@ module SourceNode {
or
// Include return nodes because they model the implicit Promise creation in async functions.
DataFlow::functionReturnNode(this, _)
or
this instanceof DataFlow::ReflectiveParametersNode
}
}
/** INTERNAL. DO NOT USE. */
module Internal {
/** An empty class that some tests are using to enforce that SourceNode is non-recursive. */
abstract class RecursionGuard extends DataFlow::Node { }
}
}
private class NodeModuleSourcesNodes extends SourceNode::Range {

View File

@@ -320,14 +320,6 @@ module TaintTracking {
any(SharedTaintStep step).heuristicStep(pred, succ)
}
/**
* Holds if `pred -> succ` is an edge contributed by an `AdditionalTaintStep` instance.
*/
cached
predicate legacyAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
any(InternalAdditionalTaintStep step).step(pred, succ)
}
/**
* Public taint step relations.
*/
@@ -441,7 +433,6 @@ module TaintTracking {
* Holds if `pred -> succ` is an edge used by all taint-tracking configurations.
*/
predicate sharedTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
Cached::legacyAdditionalTaintStep(pred, succ) or
Cached::genericStep(pred, succ) or
Cached::heuristicStep(pred, succ) or
uriStep(pred, succ) or
@@ -456,31 +447,6 @@ module TaintTracking {
promiseStep(pred, succ)
}
/**
* DEPRECATED. Subclasses should extend `SharedTaintStep` instead, unless the subclass
* is part of a query, in which case it should be moved into the `isAdditionalTaintStep` predicate
* of the relevant taint-tracking configuration.
* Other uses of the `step` relation in this class should instead use the `TaintTracking::sharedTaintStep`
* predicate.
*
* A taint-propagating data flow edge that should be added to all taint tracking
* configurations in addition to standard data flow edges.
*
* Note: For performance reasons, all subclasses of this class should be part
* of the standard library. Override `Configuration::isAdditionalTaintStep`
* for analysis-specific taint steps.
*/
deprecated class AdditionalTaintStep = InternalAdditionalTaintStep;
/** Internal version of `AdditionalTaintStep` that won't trigger deprecation warnings. */
abstract private class InternalAdditionalTaintStep extends DataFlow::Node {
/**
* Holds if `pred` &rarr; `succ` should be considered a taint-propagating
* data flow edge.
*/
abstract predicate step(DataFlow::Node pred, DataFlow::Node succ);
}
/** Gets a data flow node referring to the client side URL. */
private DataFlow::SourceNode clientSideUrlRef(DataFlow::TypeTracker t) {
t.start() and
@@ -516,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
@@ -580,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())
)
}
}
@@ -614,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 =
@@ -634,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
@@ -649,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
@@ -778,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)
)
}
@@ -831,7 +793,7 @@ module TaintTracking {
}
/**
* Holds if the property `loadStep` should be copied from the object `pred` to the property `storeStep` of object `succ`.
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*
* This step is used to copy the value of our pseudo-property that can later be accessed using a `get` or `getAll` call.
* For an expression `url.searchParams`, the property `hiddenUrlPseudoProperty()` from the `url` object is stored in the property `getableUrlPseudoProperty()` on `url.searchParams`.
@@ -1027,18 +989,16 @@ module TaintTracking {
class WhitelistContainmentCallSanitizer extends AdditionalSanitizerGuardNode,
DataFlow::MethodCallNode {
WhitelistContainmentCallSanitizer() {
exists(string name |
name = "contains" or
name = "has" or
name = "hasOwnProperty"
|
this.getMethodName() = name
)
this.getMethodName() = ["contains", "has", "hasOwnProperty", "hasOwn"]
}
override predicate sanitizes(boolean outcome, Expr e) {
outcome = true and
e = this.getArgument(0).asExpr()
exists(int propertyIndex |
if this.getMethodName() = "hasOwn" then propertyIndex = 1 else propertyIndex = 0
|
outcome = true and
e = this.getArgument(propertyIndex).asExpr()
)
}
override predicate appliesTo(Configuration cfg) { any() }
@@ -1128,6 +1088,19 @@ module TaintTracking {
)
}
/** A test for the value of `typeof x`, restricting the potential types of `x`. */
predicate isStringTypeGuard(EqualityTest test, Expr operand, boolean polarity) {
exists(TypeofTag tag | TaintTracking::isTypeofGuard(test, operand, tag) |
// typeof x === "string" sanitizes `x` when it evaluates to false
tag = "string" and
polarity = test.getPolarity().booleanNot()
or
// typeof x === "object" sanitizes `x` when it evaluates to true
tag != "string" and
polarity = test.getPolarity()
)
}
/** Holds if `guard` is a test that checks if `operand` is a number. */
predicate isNumberGuard(DataFlow::Node guard, Expr operand, boolean polarity) {
exists(DataFlow::CallNode isNaN |

View File

@@ -70,6 +70,12 @@ class TypeTracker extends TTypeTracker {
step = LoadStep(prop) and result = MkTypeTracker(hasCall, "")
or
exists(string p | step = StoreStep(p) and prop = "" and result = MkTypeTracker(hasCall, p))
or
exists(PropertySet props |
step = WithoutPropStep(props) and
not prop = props.getAProperty() and
result = this
)
}
/** Gets a textual representation of this summary. */
@@ -306,7 +312,7 @@ class TypeBackTracker extends TTypeBackTracker {
* result = < some API call >.getArgument(< n >)
* or
* exists (DataFlow::TypeBackTracker t2 |
* t = t2.smallstep(result, myType(t2))
* t2 = t.smallstep(result, myType(t2))
* )
* }
*
@@ -373,6 +379,26 @@ class SharedTypeTrackingStep extends Unit {
) {
none()
}
/**
* Holds if type-tracking should step from `pred` to `succ` but block flow of `props` through here.
*
* This can be seen as taking a copy of the value in `pred` but without the properties in `props`.
*/
predicate withoutPropStep(DataFlow::Node pred, DataFlow::Node succ, PropertySet props) { none() }
}
/**
* A representative for a set of property names.
*
* Currently this is used to denote a set of properties in `withoutPropStep`.
*/
abstract class PropertySet extends string {
bindingset[this]
PropertySet() { any() }
/** Gets a property contained in this property set. */
abstract string getAProperty();
}
/** Provides access to the steps contributed by subclasses of `SharedTypeTrackingStep`. */
@@ -413,59 +439,13 @@ module SharedTypeTrackingStep {
) {
any(SharedTypeTrackingStep s).loadStoreStep(pred, succ, loadProp, storeProp)
}
}
/**
* DEPRECATED. Use `SharedTypeTrackingStep` instead.
*
* A data flow edge that should be followed by type tracking.
*
* Unlike `AdditionalFlowStep`, this type of edge does not affect
* the local data flow graph, and is not used by data-flow configurations.
*
* Note: For performance reasons, all subclasses of this class should be part
* of the standard library. For query-specific steps, consider including the
* custom steps in the type-tracking predicate itself.
*/
deprecated class AdditionalTypeTrackingStep = LegacyTypeTrackingStep;
// Internal version of AdditionalTypeTrackingStep that we can reference without deprecation warnings.
abstract private class LegacyTypeTrackingStep extends DataFlow::Node {
/**
* Holds if type-tracking should step from `pred` to `succ`.
*/
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* Holds if type-tracking should step from `pred` into the `prop` property of `succ`.
* Holds if type-tracking should step from `pred` to `succ` but block flow of `prop` through here.
*
* This can be seen as taking a copy of the value in `pred` but without the properties in `props`.
*/
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
* Holds if type-tracking should step from the `prop` property of `pred` to `succ`.
*/
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if type-tracking should step from the `prop` property of `pred` to the same property in `succ`.
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
}
private class LegacyStepAsSharedTypeTrackingStep extends SharedTypeTrackingStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
any(LegacyTypeTrackingStep s).step(pred, succ)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(LegacyTypeTrackingStep s).storeStep(pred, succ, prop)
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(LegacyTypeTrackingStep s).loadStep(pred, succ, prop)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(LegacyTypeTrackingStep s).loadStoreStep(pred, succ, prop)
predicate withoutPropStep(DataFlow::Node pred, DataFlow::Node succ, PropertySet props) {
any(SharedTypeTrackingStep s).withoutPropStep(pred, succ, props)
}
}

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(
@@ -190,7 +202,7 @@ module CallGraph {
}
/**
* Holds if `ref` installs an accessor on an object. Such property writes should not
* Holds if `write` installs an accessor on an object. Such property writes should not
* be considered calls to an accessor.
*/
pragma[nomagic]

View File

@@ -31,4 +31,5 @@ newtype TNode =
TExceptionalFunctionReturnNode(Function f) or
TExceptionalInvocationReturnNode(InvokeExpr e) or
TGlobalAccessPathRoot() or
TTemplatePlaceholderTag(Templating::TemplatePlaceholderTag tag)
TTemplatePlaceholderTag(Templating::TemplatePlaceholderTag tag) or
TReflectiveParametersNode(Function f)

View File

@@ -224,6 +224,14 @@ private module CachedSteps {
or
arg = invk.(DataFlow::PropWrite).getRhs() and
parm = DataFlow::parameterNode(f.getParameter(0))
or
calls(invk, f) and
exists(MethodCallExpr apply |
invk = DataFlow::reflectiveCallNode(apply) and
apply.getMethodName() = "apply" and
arg = apply.getArgument(1).flow()
) and
parm.(DataFlow::ReflectiveParametersNode).getFunction() = f
)
or
exists(DataFlow::Node callback, int i, Parameter p, Function target |

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

@@ -10,7 +10,7 @@ private import AbstractPropertiesImpl
private import AbstractValuesImpl
/**
* Flow analysis for property reads, either explicitly (`x.p` or `x[e]`) or
* An analyzed property read, either explicitly (`x.p` or `x[e]`) or
* implicitly.
*/
abstract class AnalyzedPropertyRead extends DataFlow::AnalyzedNode {
@@ -86,7 +86,7 @@ pragma[noinline]
private predicate isTrackedPropertyName(string prop) { exists(MkAbstractProperty(_, prop)) }
/**
* Flow analysis for property writes, including exports (which are
* An analyzed property write, including exports (which are
* modeled as assignments to `module.exports`).
*/
abstract class AnalyzedPropertyWrite extends DataFlow::Node {

View File

@@ -45,7 +45,8 @@ private module Cached {
CopyStep(PropertyName prop) or
LoadStoreStep(PropertyName fromProp, PropertyName toProp) {
SharedTypeTrackingStep::loadStoreStep(_, _, fromProp, toProp)
}
} or
WithoutPropStep(PropertySet props) { SharedTypeTrackingStep::withoutPropStep(_, _, props) }
}
/**
@@ -110,6 +111,11 @@ private module Cached {
summary = CopyStep(prop)
)
or
exists(PropertySet props |
SharedTypeTrackingStep::withoutPropStep(pred, succ, props) and
summary = WithoutPropStep(props)
)
or
exists(string fromProp, string toProp |
SharedTypeTrackingStep::loadStoreStep(pred, succ, fromProp, toProp) and
summary = LoadStoreStep(fromProp, toProp)
@@ -194,6 +200,8 @@ class StepSummary extends TStepSummary {
or
exists(string prop | this = CopyStep(prop) | result = "copy " + prop)
or
exists(string prop | this = WithoutPropStep(prop) | result = "without " + prop)
or
exists(string fromProp, string toProp | this = LoadStoreStep(fromProp, toProp) |
result = "load " + fromProp + " and store to " + toProp
)

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

@@ -15,11 +15,11 @@ import javascript
abstract class NgSourceProvider extends Locatable {
/**
* Holds if this element provides the source as `src` for an AngularJS expression at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* The location spans column `startColumn` of line `startLine` to
* column `endColumn` of line `endLine` in file `filepath`.
*/
abstract predicate providesSourceAt(
string src, string path, int startLine, int startColumn, int endLine, int endColumn
string src, string filepath, int startLine, int startColumn, int endLine, int endColumn
);
/**
@@ -92,10 +92,10 @@ abstract private class HtmlAttributeAsNgSourceProvider extends NgSourceProvider,
endColumn = startColumn + src.length() - 1
}
/** The source code of the expression. */
/** Gets the source code of the expression. */
abstract string getSource();
/** The offset into the attribute where the expression starts. */
/** Gets the offset into the attribute where the expression starts. */
abstract int getOffset();
override DOM::ElementDefinition getEnclosingElement() { result = this.getElement() }

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() }
}
/**
@@ -278,11 +278,11 @@ abstract private class CustomSpecialServiceDefinition extends CustomServiceDefin
bindingset[moduleMethodName]
private predicate isCustomServiceDefinitionOnModule(
DataFlow::CallNode mce, string moduleMethodName, string serviceName,
DataFlow::Node factoryArgument
DataFlow::Node factoryFunction
) {
mce = moduleRef(_).getAMethodCall(moduleMethodName) and
mce.getArgument(0).asExpr().mayHaveStringValue(serviceName) and
factoryArgument = mce.getArgument(1)
mce.getArgument(0).mayHaveStringValue(serviceName) and
factoryFunction = mce.getArgument(1)
}
pragma[inline]
@@ -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

@@ -198,7 +198,7 @@ module Babel {
.getMember(["transform", "transformSync", "transformAsync"])
.getACall() and
pred = call.getArgument(0) and
succ = [call, call.getParameter(2).getParameter(0).getAnImmediateUse()]
succ = [call, call.getParameter(2).getParameter(0).asSource()]
)
}
}

View File

@@ -102,9 +102,9 @@ private predicate isBrowserifyDependencyMap(ObjectExpr deps) {
* Holds if `m` is a function that looks like a bundled module created
* by Webpack.
*
* Parameters must be named either `module` or `exports`,
* or their name must contain the substring `webpack_require`
* or `webpack_module_template_argument`.
* Parameters must be named either "module" or "exports",
* or their name must contain the substring "webpack_require"
* or "webpack_module_template_argument".
*/
private predicate isWebpackModule(FunctionExpr m) {
forex(Parameter parm | parm = m.getAParameter() |

View File

@@ -14,7 +14,7 @@ module Cheerio {
}
/** Gets a reference to the `cheerio` function, possibly with a loaded DOM. */
DataFlow::SourceNode cheerioRef() { result = cheerioApi().getAUse() }
DataFlow::SourceNode cheerioRef() { result = cheerioApi().getAValueReachableFromSource() }
/**
* A creation of `cheerio` object, a collection of virtual DOM elements

Some files were not shown because too many files have changed in this diff Show More