Skip to content

Commit a077345

Browse files
authored
Merge pull request #7180 from erik-krogh/apiLabel2
JS: Make the edges of API-graphs into IPA types
2 parents aad55ff + 0a3d62c commit a077345

File tree

18 files changed

+458
-194
lines changed

18 files changed

+458
-194
lines changed

javascript/ql/lib/semmle/javascript/ApiGraphs.qll

Lines changed: 292 additions & 141 deletions
Large diffs are not rendered by default.

javascript/ql/lib/semmle/javascript/Constants.qll

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
*/
44

55
import javascript
6+
private import semmle.javascript.internal.CachedStages
67

78
/**
89
* An expression that evaluates to a constant primitive value.
910
*/
11+
cached
1012
abstract class ConstantExpr extends Expr { }
1113

1214
/**
@@ -16,15 +18,19 @@ module SyntacticConstants {
1618
/**
1719
* An expression that evaluates to a constant value according to a bottom-up syntactic analysis.
1820
*/
21+
cached
1922
abstract class SyntacticConstant extends ConstantExpr { }
2023

2124
/**
2225
* A literal primitive expression.
2326
*
2427
* Note that `undefined`, `NaN` and `Infinity` are global variables, and are not covered by this class.
2528
*/
29+
cached
2630
class PrimitiveLiteralConstant extends SyntacticConstant {
31+
cached
2732
PrimitiveLiteralConstant() {
33+
Stages::Ast::ref() and
2834
this instanceof NumberLiteral
2935
or
3036
this instanceof StringLiteral
@@ -43,19 +49,27 @@ module SyntacticConstants {
4349
/**
4450
* A literal null expression.
4551
*/
46-
class NullConstant extends SyntacticConstant, NullLiteral { }
52+
cached
53+
class NullConstant extends SyntacticConstant, NullLiteral {
54+
cached
55+
NullConstant() { Stages::Ast::ref() and this = this }
56+
}
4757

4858
/**
4959
* A unary operation on a syntactic constant.
5060
*/
61+
cached
5162
class UnaryConstant extends SyntacticConstant, UnaryExpr {
63+
cached
5264
UnaryConstant() { getOperand() instanceof SyntacticConstant }
5365
}
5466

5567
/**
5668
* A binary operation on syntactic constants.
5769
*/
70+
cached
5871
class BinaryConstant extends SyntacticConstant, BinaryExpr {
72+
cached
5973
BinaryConstant() {
6074
getLeftOperand() instanceof SyntacticConstant and
6175
getRightOperand() instanceof SyntacticConstant
@@ -65,7 +79,9 @@ module SyntacticConstants {
6579
/**
6680
* A conditional expression on syntactic constants.
6781
*/
82+
cached
6883
class ConditionalConstant extends SyntacticConstant, ConditionalExpr {
84+
cached
6985
ConditionalConstant() {
7086
getCondition() instanceof SyntacticConstant and
7187
getConsequent() instanceof SyntacticConstant and
@@ -76,7 +92,9 @@ module SyntacticConstants {
7692
/**
7793
* A use of the global variable `undefined` or `void e`.
7894
*/
95+
cached
7996
class UndefinedConstant extends SyntacticConstant {
97+
cached
8098
UndefinedConstant() {
8199
this.(GlobalVarAccess).getName() = "undefined" or
82100
this instanceof VoidExpr
@@ -86,21 +104,27 @@ module SyntacticConstants {
86104
/**
87105
* A use of the global variable `NaN`.
88106
*/
107+
cached
89108
class NaNConstant extends SyntacticConstant {
109+
cached
90110
NaNConstant() { this.(GlobalVarAccess).getName() = "NaN" }
91111
}
92112

93113
/**
94114
* A use of the global variable `Infinity`.
95115
*/
116+
cached
96117
class InfinityConstant extends SyntacticConstant {
118+
cached
97119
InfinityConstant() { this.(GlobalVarAccess).getName() = "Infinity" }
98120
}
99121

100122
/**
101123
* An expression that wraps the syntactic constant it evaluates to.
102124
*/
125+
cached
103126
class WrappedConstant extends SyntacticConstant {
127+
cached
104128
WrappedConstant() { getUnderlyingValue() instanceof SyntacticConstant }
105129
}
106130

@@ -123,6 +147,8 @@ module SyntacticConstants {
123147
/**
124148
* An expression that evaluates to a constant string.
125149
*/
150+
cached
126151
class ConstantString extends ConstantExpr {
152+
cached
127153
ConstantString() { exists(getStringValue()) }
128154
}

javascript/ql/lib/semmle/javascript/Expr.qll

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ class ExprOrType extends @expr_or_type, Documentable {
8989
*
9090
* Also see `getUnderlyingReference` and `stripParens`.
9191
*/
92-
Expr getUnderlyingValue() { result = this }
92+
cached
93+
Expr getUnderlyingValue() { Stages::Ast::ref() and result = this }
9394
}
9495

9596
/**
@@ -274,7 +275,11 @@ private DataFlow::Node getCatchParameterFromStmt(Stmt stmt) {
274275
*/
275276
class Identifier extends @identifier, ExprOrType {
276277
/** Gets the name of this identifier. */
277-
string getName() { literals(result, _, this) }
278+
cached
279+
string getName() {
280+
Stages::Ast::ref() and
281+
literals(result, _, this)
282+
}
278283

279284
override string getAPrimaryQlClass() { result = "Identifier" }
280285
}

javascript/ql/lib/semmle/javascript/MembershipCandidates.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ module MembershipCandidate {
165165
EnumerationRegExp enumeration;
166166
boolean polarity;
167167

168+
pragma[nomagic]
168169
RegExpEnumerationCandidate() {
169170
exists(DataFlow::MethodCallNode mcn, DataFlow::Node base, string m, DataFlow::Node firstArg |
170171
(

javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -939,18 +939,21 @@ private predicate basicFlowStepNoBarrier(
939939
* This predicate is field insensitive (it does not distinguish between `x` and `x.p`)
940940
* and hence should only be used for purposes of approximation.
941941
*/
942-
pragma[inline]
942+
pragma[noinline]
943943
private predicate exploratoryFlowStep(
944944
DataFlow::Node pred, DataFlow::Node succ, DataFlow::Configuration cfg
945945
) {
946-
basicFlowStepNoBarrier(pred, succ, _, cfg) or
947-
exploratoryLoadStep(pred, succ, cfg) or
948-
isAdditionalLoadStoreStep(pred, succ, _, _, cfg) or
949-
// the following three disjuncts taken together over-approximate flow through
950-
// higher-order calls
951-
exploratoryCallbackStep(pred, succ) or
952-
succ = pred.(DataFlow::FunctionNode).getAParameter() or
953-
exploratoryBoundInvokeStep(pred, succ)
946+
isRelevantForward(pred, cfg) and
947+
(
948+
basicFlowStepNoBarrier(pred, succ, _, cfg) or
949+
exploratoryLoadStep(pred, succ, cfg) or
950+
isAdditionalLoadStoreStep(pred, succ, _, _, cfg) or
951+
// the following three disjuncts taken together over-approximate flow through
952+
// higher-order calls
953+
exploratoryCallbackStep(pred, succ) or
954+
succ = pred.(DataFlow::FunctionNode).getAParameter() or
955+
exploratoryBoundInvokeStep(pred, succ)
956+
)
954957
}
955958

956959
/**
@@ -1024,6 +1027,7 @@ private string getAPropertyUsedInLoadStore(DataFlow::Configuration cfg) {
10241027
* Holds if there exists a store-step from `pred` to `succ` under configuration `cfg`,
10251028
* and somewhere in the program there exists a load-step that could possibly read the stored value.
10261029
*/
1030+
pragma[noinline]
10271031
private predicate exploratoryForwardStoreStep(
10281032
DataFlow::Node pred, DataFlow::Node succ, DataFlow::Configuration cfg
10291033
) {
@@ -1075,8 +1079,10 @@ private string getABackwardsRelevantStoreProperty(DataFlow::Configuration cfg) {
10751079
private predicate isRelevantForward(DataFlow::Node nd, DataFlow::Configuration cfg) {
10761080
isSource(nd, cfg, _) and isLive()
10771081
or
1078-
exists(DataFlow::Node mid | isRelevantForward(mid, cfg) |
1079-
exploratoryFlowStep(mid, nd, cfg) or
1082+
exists(DataFlow::Node mid |
1083+
exploratoryFlowStep(mid, nd, cfg)
1084+
or
1085+
isRelevantForward(mid, cfg) and
10801086
exploratoryForwardStoreStep(mid, nd, cfg)
10811087
)
10821088
}
@@ -1098,11 +1104,10 @@ private predicate isRelevant(DataFlow::Node nd, DataFlow::Configuration cfg) {
10981104
private predicate isRelevantBackStep(
10991105
DataFlow::Node mid, DataFlow::Node nd, DataFlow::Configuration cfg
11001106
) {
1107+
exploratoryFlowStep(nd, mid, cfg)
1108+
or
11011109
isRelevantForward(nd, cfg) and
1102-
(
1103-
exploratoryFlowStep(nd, mid, cfg) or
1104-
exploratoryBackwardStoreStep(nd, mid, cfg)
1105-
)
1110+
exploratoryBackwardStoreStep(nd, mid, cfg)
11061111
}
11071112

11081113
/**
@@ -1273,23 +1278,30 @@ private predicate parameterPropRead(
12731278
DataFlow::Node arg, string prop, DataFlow::Node succ, DataFlow::Configuration cfg,
12741279
PathSummary summary
12751280
) {
1276-
exists(Function f, DataFlow::Node read, DataFlow::Node invk |
1281+
exists(Function f, DataFlow::Node read, DataFlow::Node invk, DataFlow::Node parm |
1282+
reachesReturn(f, read, cfg, summary) and
1283+
parameterPropReadStep(parm, read, prop, cfg, arg, invk, f, succ)
1284+
)
1285+
}
1286+
1287+
// all the non-recursive parts of parameterPropRead outlined into a precomputed predicate
1288+
pragma[noinline]
1289+
private predicate parameterPropReadStep(
1290+
DataFlow::SourceNode parm, DataFlow::Node read, string prop, DataFlow::Configuration cfg,
1291+
DataFlow::Node arg, DataFlow::Node invk, Function f, DataFlow::Node succ
1292+
) {
1293+
(
12771294
not f.isAsyncOrGenerator() and invk = succ
12781295
or
12791296
// load from an immediately awaited function call
12801297
f.isAsync() and
12811298
invk = getAwaitOperand(succ)
1282-
|
1283-
exists(DataFlow::SourceNode parm |
1284-
callInputStep(f, invk, arg, parm, cfg) and
1285-
(
1286-
reachesReturn(f, read, cfg, summary) and
1287-
read = parm.getAPropertyRead(prop)
1288-
or
1289-
reachesReturn(f, read, cfg, summary) and
1290-
exists(DataFlow::Node use | parm.flowsTo(use) | isAdditionalLoadStep(use, read, prop, cfg))
1291-
)
1292-
)
1299+
) and
1300+
callInputStep(f, invk, arg, parm, cfg) and
1301+
(
1302+
read = parm.getAPropertyRead(prop)
1303+
or
1304+
exists(DataFlow::Node use | parm.flowsTo(use) | isAdditionalLoadStep(use, read, prop, cfg))
12931305
)
12941306
}
12951307

javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,12 @@ module TaintTracking {
160160
* of the standard library. Override `Configuration::isSanitizerGuard`
161161
* for analysis-specific taint sanitizer guards.
162162
*/
163+
cached
163164
abstract class AdditionalSanitizerGuardNode extends SanitizerGuardNode {
164165
/**
165166
* Holds if this guard applies to the flow in `cfg`.
166167
*/
168+
cached
167169
abstract predicate appliesTo(Configuration cfg);
168170
}
169171

@@ -1127,7 +1129,7 @@ module TaintTracking {
11271129
idx = astNode.getAnOperand() and
11281130
idx.getPropertyNameExpr() = x and
11291131
// and the other one is guaranteed to be `undefined`
1130-
forex(InferredType tp | tp = undef.getAType() | tp = TTUndefined())
1132+
unique(InferredType tp | tp = pragma[only_bind_into](undef.getAType())) = TTUndefined()
11311133
)
11321134
}
11331135

javascript/ql/lib/semmle/javascript/frameworks/D3.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module D3 {
2323
or
2424
result = API::moduleImport("d3-node").getInstance().getMember("d3")
2525
or
26-
result = API::root().getASuccessor(any(D3GlobalEntry i))
26+
result = any(D3GlobalEntry i).getNode()
2727
}
2828

2929
/**

javascript/ql/lib/semmle/javascript/frameworks/History.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module History {
1717
* Gets a reference to the [`history`](https://npmjs.org/package/history) library.
1818
*/
1919
private API::Node history() {
20-
result = [API::moduleImport("history"), API::root().getASuccessor(any(HistoryGlobalEntry h))]
20+
result = [API::moduleImport("history"), any(HistoryGlobalEntry h).getNode()]
2121
}
2222

2323
/**

javascript/ql/lib/semmle/javascript/frameworks/Immutable.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ private module Immutable {
2727
API::Node immutableImport() {
2828
result = API::moduleImport("immutable")
2929
or
30-
result = API::root().getASuccessor(any(ImmutableGlobalEntry i))
30+
result = any(ImmutableGlobalEntry i).getNode()
3131
}
3232

3333
/**

javascript/ql/lib/semmle/javascript/frameworks/Logging.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private module Console {
4545
*/
4646
private API::Node console() {
4747
result = API::moduleImport("console") or
48-
result = API::root().getASuccessor(any(ConsoleGlobalEntry e))
48+
result = any(ConsoleGlobalEntry e).getNode()
4949
}
5050

5151
/**

javascript/ql/lib/semmle/javascript/frameworks/Nest.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ module NestJS {
151151
private API::Node validationPipe() {
152152
result = nestjs().getMember("ValidationPipe")
153153
or
154-
result = API::root().getASuccessor(any(ValidationNodeEntry e))
154+
result = any(ValidationNodeEntry e).getNode()
155155
}
156156

157157
/**

javascript/ql/lib/semmle/javascript/frameworks/Redux.qll

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,9 +1111,7 @@ module Redux {
11111111

11121112
/** A heuristic call to `connect`, recognized by it taking arguments named `mapStateToProps` and `mapDispatchToProps`. */
11131113
private class HeuristicConnectFunction extends ConnectCall {
1114-
HeuristicConnectFunction() {
1115-
this = API::root().getASuccessor(any(HeuristicConnectEntryPoint e)).getACall()
1116-
}
1114+
HeuristicConnectFunction() { this = any(HeuristicConnectEntryPoint e).getNode().getACall() }
11171115

11181116
override API::Node getMapStateToProps() {
11191117
result = getAParameter() and

javascript/ql/lib/semmle/javascript/frameworks/Vue.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module Vue {
3535
API::Node vueLibrary() {
3636
result = API::moduleImport("vue")
3737
or
38-
result = API::root().getASuccessor(any(GlobalVueEntryPoint e))
38+
result = any(GlobalVueEntryPoint e).getNode()
3939
}
4040

4141
/**
@@ -51,7 +51,7 @@ module Vue {
5151
or
5252
result = vueLibrary().getMember("component").getReturn()
5353
or
54-
result = API::root().getASuccessor(any(VueFileImportEntryPoint e))
54+
result = any(VueFileImportEntryPoint e).getNode()
5555
}
5656

5757
/**

0 commit comments

Comments
 (0)