Skip to content

Commit 12370e9

Browse files
committed
JS: Use VariableOrThis in variable capture as well
1 parent 0ebe8bd commit 12370e9

4 files changed

Lines changed: 36 additions & 22 deletions

File tree

javascript/ql/lib/semmle/javascript/dataflow/internal/Contents.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import javascript
22
private import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
33
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
4+
private import semmle.javascript.dataflow.internal.VariableOrThis
45
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
56

67
module Private {
@@ -75,7 +76,7 @@ module Private {
7576
MkIteratorError() or
7677
MkPromiseValue() or
7778
MkPromiseError() or
78-
MkCapturedContent(LocalVariable v) { v.isCaptured() }
79+
MkCapturedContent(LocalVariableOrThis v) { v.isCaptured() }
7980

8081
cached
8182
newtype TContentSet =
@@ -163,7 +164,7 @@ module Public {
163164
int asArrayIndex() { result = this.asPropertyName().(PropertyName).asArrayIndex() }
164165

165166
/** Gets the captured variable represented by this content, if any. */
166-
LocalVariable asCapturedVariable() { this = MkCapturedContent(result) }
167+
LocalVariableOrThis asCapturedVariable() { this = MkCapturedContent(result) }
167168

168169
/** Holds if this represents values stored at an unknown array index. */
169170
predicate isUnknownArrayElement() { this = MkArrayElementUnknown() }

javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,7 +1022,7 @@ private predicate isBlockedLegacyNode(Node node) {
10221022
// Note that some variables, such as top-level variables, are still modelled with these nodes (which will result in jump steps).
10231023
exists(LocalVariable variable |
10241024
node = TCapturedVariableNode(variable) and
1025-
variable instanceof VariableCaptureConfig::CapturedVariable
1025+
variable = any(VariableCaptureConfig::CapturedVariable v).asLocalVariable()
10261026
)
10271027
or
10281028
legacyBarrier(node)
@@ -1230,7 +1230,7 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
12301230
c = ContentSet::arrayElement()
12311231
)
12321232
or
1233-
exists(LocalVariable variable |
1233+
exists(LocalVariableOrThis variable |
12341234
VariableCaptureOutput::readStep(getClosureNode(node1), variable, getClosureNode(node2)) and
12351235
c.asSingleton() = MkCapturedContent(variable)
12361236
)
@@ -1349,7 +1349,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
13491349
c = ContentSet::promiseValue()
13501350
)
13511351
or
1352-
exists(LocalVariable variable |
1352+
exists(LocalVariableOrThis variable |
13531353
VariableCaptureOutput::storeStep(getClosureNode(node1), variable, getClosureNode(node2)) and
13541354
c.asSingleton() = MkCapturedContent(variable)
13551355
)

javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
private import javascript as js
22
private import semmle.javascript.dataflow.internal.DataFlowNode
3+
private import semmle.javascript.dataflow.internal.VariableOrThis
34
private import codeql.dataflow.VariableCapture
45
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
56

@@ -51,7 +52,7 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
5152
)
5253
}
5354

54-
class CapturedVariable extends js::LocalVariable {
55+
class CapturedVariable extends LocalVariableOrThis {
5556
CapturedVariable() {
5657
DataFlowImplCommon::forceCachingInSameStage() and
5758
this.isCaptured() and
@@ -63,7 +64,9 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
6364

6465
additional predicate captures(js::Function fun, CapturedVariable variable) {
6566
(
66-
variable.getAnAccess().getContainer().getFunctionBoundary() = fun
67+
variable.asLocalVariable().getAnAccess().getContainer().getFunctionBoundary() = fun
68+
or
69+
variable.getAThisUse().getUseContainer() = fun
6770
or
6871
exists(js::Function inner |
6972
captures(inner, variable) and
@@ -122,7 +125,8 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
122125
private predicate isCapturedByOwnInitializer(js::VariableDeclarator decl) {
123126
exists(js::Function function |
124127
function = getACapturingFunctionInTree(decl.getInit()) and
125-
captures(function, decl.getBindingPattern().(js::VarDecl).getVariable())
128+
captures(function,
129+
LocalVariableOrThis::variable(decl.getBindingPattern().(js::VarDecl).getVariable()))
126130
)
127131
}
128132

@@ -141,7 +145,7 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
141145
}
142146

143147
class CapturedParameter extends CapturedVariable {
144-
CapturedParameter() { this.isParameter() }
148+
CapturedParameter() { this.asLocalVariable().isParameter() or exists(this.asThisContainer()) }
145149
}
146150

147151
class Expr extends js::AST::ValueNode {
@@ -152,10 +156,10 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
152156
}
153157
}
154158

155-
class VariableRead extends Expr instanceof js::VarAccess, js::RValue {
159+
class VariableRead extends Expr instanceof js::ControlFlowNode {
156160
private CapturedVariable variable;
157161

158-
VariableRead() { this = variable.getAnAccess() }
162+
VariableRead() { this = variable.getAUse() }
159163

160164
CapturedVariable getVariable() { result = variable }
161165
}
@@ -178,7 +182,7 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
178182
private newtype TVariableWrite =
179183
MkExplicitVariableWrite(js::VarRef pattern) {
180184
exists(js::DataFlow::lvalueNodeInternal(pattern)) and
181-
pattern.getVariable() instanceof CapturedVariable
185+
any(CapturedVariable v).asLocalVariable() = pattern.getVariable()
182186
} or
183187
MkImplicitVariableInit(CapturedVariable v) { not v instanceof CapturedParameter }
184188

@@ -200,7 +204,7 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
200204

201205
ExplicitVariableWrite() { this = MkExplicitVariableWrite(pattern) }
202206

203-
override CapturedVariable getVariable() { result = pattern.getVariable() }
207+
override CapturedVariable getVariable() { result.asLocalVariable() = pattern.getVariable() }
204208

205209
override string toString() { result = pattern.toString() }
206210

@@ -248,7 +252,9 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
248252
override predicate hasCfgNode(BasicBlock bb, int i) {
249253
// 'i' would normally be bound to 0, but we lower it to -1 so FunctionDeclStmts can be evaluated
250254
// at index 0.
251-
any(js::SsaImplicitInit def).definesAt(bb, _, variable) and i = -1
255+
any(js::SsaImplicitInit def).definesAt(bb, _, variable.asLocalVariable()) and i = -1
256+
or
257+
bb.(js::EntryBasicBlock).getContainer() = variable.asThisContainer() and i = -1
252258
}
253259
}
254260

@@ -266,7 +272,13 @@ module VariableCaptureOutput = Flow<js::DbLocation, VariableCaptureConfig>;
266272
js::DataFlow::Node getNodeFromClosureNode(VariableCaptureOutput::ClosureNode node) {
267273
result = TValueNode(node.(VariableCaptureOutput::ExprNode).getExpr())
268274
or
269-
result = TValueNode(node.(VariableCaptureOutput::ParameterNode).getParameter().getADeclaration()) // TODO: is this subsumed by the ExprNode case?
275+
result =
276+
TValueNode(node.(VariableCaptureOutput::ParameterNode)
277+
.getParameter()
278+
.asLocalVariable()
279+
.getADeclaration()) // TODO: is this subsumed by the ExprNode case?
280+
or
281+
result = TThisNode(node.(VariableCaptureOutput::ParameterNode).getParameter().asThisContainer())
270282
or
271283
result = TExprPostUpdateNode(node.(VariableCaptureOutput::ExprPostUpdateNode).getExpr())
272284
or

javascript/ql/test/library-tests/TripleDot/useuse.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,21 +75,22 @@ function t6() {
7575
this.y = y;
7676
});
7777

78-
sink(this.x); // $ MISSING: hasValueFlow=t6.1
79-
sink(this.y); // $ MISSING: hasValueFlow=t6.1
78+
sink(this.x); // $ hasValueFlow=t6.1
79+
sink(this.y); // $ hasValueFlow=t6.2
8080

8181
invoke(() => {
82-
sink(this.x); // $ MISSING: hasValueFlow=t6.1
83-
sink(this.y); // $ MISSING: hasValueFlow=t6.2
82+
sink(this.x); // $ hasValueFlow=t6.1
83+
sink(this.y); // $ hasValueFlow=t6.2
8484
});
8585

8686
this.methodLike = function() {
87-
sink(this.x); // $ MISSING: hasValueFlow=t6.1
88-
sink(this.y); // $ MISSING: hasValueFlow=t6.2
87+
sink(this.x); // $ hasValueFlow=t6.1
88+
sink(this.y); // $ hasValueFlow=t6.2
8989
}
9090
}
9191
}
9292
const c = new C(source('t6.1'), source('t6.2'));
9393
sink(c.x); // $ hasValueFlow=t6.1
94-
sink(c.y); // $ MISSING: hasValueFlow=t6.2
94+
sink(c.y); // $ hasValueFlow=t6.2
95+
c.methodLike();
9596
}

0 commit comments

Comments
 (0)