diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll index 414fc3be72c..d0087dcdca0 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll @@ -1888,6 +1888,8 @@ module PathNode { or // Skip captured variable nodes as the successor will be a use of that variable anyway. nd = DataFlow::capturedVariableNode(_) + or + nd instanceof DataFlow::FunctionSelfReferenceNode } } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll index 11fdc25e6f6..47fb26937cd 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll @@ -1432,6 +1432,30 @@ module DataFlow { override StmtContainer getContainer() { result = this.getTag().getInnerTopLevel() } } + /** + * A node representing the hidden parameter of a function by which a function can refer to itself. + */ + class FunctionSelfReferenceNode extends DataFlow::Node, TFunctionSelfReferenceNode { + private Function function; + + FunctionSelfReferenceNode() { this = TFunctionSelfReferenceNode(function) } + + /** Gets the function. */ + Function getFunction() { result = function } + + override StmtContainer getContainer() { result = function } + + override BasicBlock getBasicBlock() { result = function.getEntryBB() } + + override string toString() { result = "[function self-reference] " + function.toString() } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + function.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + /** * A post-update node whose pre-node corresponds to an expression. See `DataFlow::PostUpdateNode` for more details. */ diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll index 8f54d3a3496..bee7b1259b4 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll @@ -24,6 +24,7 @@ private module Cached { (kind = "call" or kind = "apply") } or TThisNode(StmtContainer f) { f.(Function).getThisBinder() = f or f instanceof TopLevel } or + TFunctionSelfReferenceNode(Function f) or TDestructuredModuleImportNode(ImportDeclaration decl) { exists(decl.getASpecifier().getImportedName()) } or diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll index 25d4af88ffa..9a9559e28db 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll @@ -152,6 +152,8 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition n = call.asBoundCall(boundArgs).getArgument(pos.asPositional() - boundArgs) ) or + pos.isFunctionSelfReference() and n = call.asOrdinaryCall().getCalleeNode() + or pos.isThis() and n = TConstructorThisArgumentNode(call.asOrdinaryCall().asExpr()) or // For now, treat all spread argument as flowing into the 'arguments' array, regardless of preceding arguments @@ -348,6 +350,7 @@ newtype TParameterPosition = MkPositionalParameter(int n) { n = [0 .. getMaxArity()] } or MkPositionalLowerBound(int n) { n = [0 .. getMaxArity()] } or MkThisParameter() or + MkFunctionSelfReferenceParameter() or MkArgumentsArrayParameter() class ParameterPosition extends TParameterPosition { @@ -363,6 +366,8 @@ class ParameterPosition extends TParameterPosition { predicate isThis() { this = MkThisParameter() } + predicate isFunctionSelfReference() { this = MkFunctionSelfReferenceParameter() } + predicate isArgumentsArray() { this = MkArgumentsArrayParameter() } string toString() { @@ -372,6 +377,8 @@ class ParameterPosition extends TParameterPosition { or this.isThis() and result = "this" or + this.isFunctionSelfReference() and result = "function" + or this.isArgumentsArray() and result = "arguments-array" } }