Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6f5fb2a

Browse files
author
Esben Sparre Andreasen
committed
JS: update queries and tests for improved type inference
1 parent 3692667 commit 6f5fb2a

10 files changed

Lines changed: 231 additions & 7 deletions

File tree

javascript/ql/src/Expressions/HeterogeneousComparison.ql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,37 @@ string getTypeDescription(string message1, string message2, int complexity1, int
170170
result = message1
171171
}
172172

173+
/**
174+
* Holds if `e` directly uses a parameter's initial value as passed in from the caller.
175+
*/
176+
predicate isInitialParameterUse(Expr e) {
177+
exists (SimpleParameter p, SsaExplicitDefinition ssa |
178+
ssa.getAContributingVarDef() = p and
179+
ssa.getVariable().getAUse() = e and
180+
not p.isRestParameter()
181+
)
182+
}
183+
184+
/**
185+
* Holds if `e` is an expression that should not be considered in a heterogeneous comparison.
186+
*
187+
* We currently whitelist these kinds of expressions:
188+
*
189+
* - parameters, as passed in from the caller
190+
*/
191+
predicate whitelist(Expr e) {
192+
isInitialParameterUse(e)
193+
}
194+
173195
from ASTNode cmp,
174196
DataFlow::AnalyzedNode left, DataFlow::AnalyzedNode right,
175197
string leftTypes, string rightTypes,
176198
string leftExprDescription, string rightExprDescription,
177199
int leftTypeCount, int rightTypeCount ,
178200
string leftTypeDescription, string rightTypeDescription
179201
where isHeterogeneousComparison(cmp, left, right, leftTypes, rightTypes) and
202+
not whitelist(left.asExpr()) and
203+
not whitelist(right.asExpr()) and
180204
leftExprDescription = capitalize(getDescription(left.asExpr(), "this expression")) and
181205
rightExprDescription = getDescription(right.asExpr(), "an expression") and
182206
leftTypeCount = strictcount(left.getAType()) and

javascript/ql/src/Statements/UselessConditional.ql

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import javascript
1616
import semmle.javascript.RestrictedLocations
17+
import semmle.javascript.dataflow.Refinements
1718

1819
/**
1920
* Holds if `va` is a defensive truthiness check that may be worth keeping, even if it
@@ -62,19 +63,52 @@ predicate isConstant(Expr e) {
6263
isSymbolicConstant(e.(VarAccess).getVariable())
6364
}
6465

66+
/**
67+
* Holds if `e` directly uses a parameter's initial value as passed in from the caller.
68+
*/
69+
predicate isInitialParameterUse(Expr e) {
70+
exists (SimpleParameter p, SsaExplicitDefinition ssa |
71+
ssa.getAContributingVarDef() = p and
72+
ssa.getVariable().getAUse() = e and
73+
not p.isRestParameter()
74+
)
75+
or
76+
isInitialParameterUse(e.(LogNotExpr).getOperand())
77+
}
78+
79+
/**
80+
* Holds if `e` directly uses the returned value from a function call that returns a constant boolean value.
81+
*/
82+
predicate isConstantBooleanReturnValue(Expr e) {
83+
exists (DataFlow::CallNode call |
84+
exists (call.analyze().getTheBooleanValue()) |
85+
e = call.asExpr() or
86+
exists (SsaExplicitDefinition ssa |
87+
ssa.getDef().getSource() = call.asExpr() and
88+
ssa.getVariable().getAUse() = e
89+
)
90+
)
91+
or
92+
isConstantBooleanReturnValue(e.(LogNotExpr).getOperand())
93+
}
94+
6595
/**
6696
* Holds if `e` is an expression that should not be flagged as a useless condition.
6797
*
68-
* We currently whitelist three kinds of expressions:
98+
* We currently whitelist these kinds of expressions:
6999
*
70100
* - constants (including references to literal constants);
71101
* - negations of constants;
72-
* - defensive checks.
102+
* - defensive checks;
103+
* - parameters, as passed in from the caller;
104+
* - constant boolean returned values
73105
*/
74106
predicate whitelist(Expr e) {
75107
isConstant(e) or
76108
isConstant(e.(LogNotExpr).getOperand()) or
77-
isDefensiveInit(e)
109+
isDefensiveInit(e) or
110+
isInitialParameterUse(e) or
111+
isConstantBooleanReturnValue(e)
78112
}
79113

80114
/**
@@ -107,4 +141,4 @@ where isConditional(cond, op.asExpr()) and
107141
msg = "This expression always evaluates to " + cv + "."
108142
)
109143

110-
select sel, msg
144+
select sel, msg

javascript/ql/test/library-tests/DataFlow/incomplete.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
| tst.js:65:3:65:10 | yield 42 | yield |
2727
| tst.js:66:13:66:25 | function.sent | yield |
2828
| tst.js:68:12:68:14 | h() | call |
29-
| tst.js:69:1:69:9 | iter.next | call |
3029
| tst.js:69:1:69:9 | iter.next | heap |
3130
| tst.js:69:1:69:13 | iter.next(23) | call |
3231
| tst.js:72:3:72:11 | await p() | await |

javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/middleware-attacher-getter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ function getAttacher1 (app) {
77

88
var app = express();
99
getAttacher1(app);
10-
10+
confuse(getAttacher2); // disable the type inference
1111

1212
function getAttacher2 (app) {
1313
return function(h) {
@@ -17,3 +17,4 @@ function getAttacher2 (app) {
1717

1818
var app = express();
1919
getAttacher2(app)(function(req, res){});
20+
confuse(getAttacher2); // disable the type inference

javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/route-objects.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ function wrap(f){
5858
}
5959
}
6060
app[route3.method](route3.url, wrap(route3.handler));
61+
confuse(wrap); // confuse the type inference

javascript/ql/test/query-tests/Expressions/HeterogeneousComparison/HeterogeneousComparison.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,5 @@
4646
| tst.js:208:5:208:6 | t5 | Variable 't5' cannot be of type function or regular expression, but it is compared to $@ of type function or regular expression. | tst.js:208:12:208:13 | t2 | variable 't2' |
4747
| tst.js:209:5:209:6 | t3 | Variable 't3' is of type function, object or regular expression, but it is compared to $@ of type boolean, null, number, string or undefined. | tst.js:209:12:209:13 | t5 | variable 't5' |
4848
| tst.js:210:5:210:6 | t5 | Variable 't5' is of type boolean, null, number, string or undefined, but it is compared to $@ of type function, object or regular expression. | tst.js:210:12:210:13 | t3 | variable 't3' |
49+
| tst.js:225:13:225:14 | xy | Variable 'xy' is of type undefined, but it is compared to $@ of type string. | tst.js:225:20:225:24 | "foo" | an expression |
50+
| tst.js:233:5:233:5 | x | Variable 'x' is of type object, but it is compared to $@ of type number. | tst.js:233:11:233:12 | 42 | an expression |

javascript/ql/test/query-tests/Expressions/HeterogeneousComparison/tst.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,25 @@ function l() {
212212

213213
1n == 1; // OK
214214

215+
(function tooGeneralLocalFunctions(){
216+
function f1(x) {
217+
if (x === "foo") { // OK, whitelisted
218+
219+
}
220+
}
221+
f1(undefined);
222+
223+
function f2(x, y) {
224+
var xy = o.q? x: y;
225+
if (xy === "foo") { // NOT OK (not whitelisted like above)
226+
227+
}
228+
}
229+
f2(undefined, undefined);
230+
})();
231+
232+
function f(...x) {
233+
x === 42
234+
};
235+
215236
// semmle-extractor-options: --experimental

javascript/ql/test/query-tests/Statements/UselessConditional/UselessConditional.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,12 @@
1010
| UselessConditional.js:29:8:29:8 | x | Variable 'x' always evaluates to true here. |
1111
| UselessConditional.js:30:8:30:14 | new X() | This expression always evaluates to true. |
1212
| UselessConditional.js:33:7:33:7 | x | Variable 'x' always evaluates to false here. |
13+
| UselessConditional.js:54:9:54:13 | known | Variable 'known' always evaluates to false here. |
14+
| UselessConditional.js:60:9:60:15 | unknown | Variable 'unknown' always evaluates to false here. |
15+
| UselessConditional.js:65:5:65:5 | x | Variable 'x' always evaluates to true here. |
16+
| UselessConditional.js:76:13:76:13 | x | Variable 'x' always evaluates to true here. |
17+
| UselessConditional.js:82:13:82:13 | x | Variable 'x' always evaluates to true here. |
18+
| UselessConditionalGood.js:58:12:58:13 | x2 | Variable 'x2' always evaluates to false here. |
19+
| UselessConditionalGood.js:69:12:69:13 | xy | Variable 'xy' always evaluates to false here. |
20+
| UselessConditionalGood.js:85:12:85:13 | xy | Variable 'xy' always evaluates to false here. |
21+
| UselessConditionalGood.js:97:12:97:13 | xy | Variable 'xy' always evaluates to false here. |

javascript/ql/test/query-tests/Statements/UselessConditional/UselessConditional.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,44 @@ async function awaitFlow(){
4444
}
4545
}
4646

47+
(function(){
48+
function knownF() {
49+
return false;
50+
}
51+
var known = knownF();
52+
if (known)
53+
return;
54+
if (known)
55+
return;
56+
57+
var unknown = unknownF();
58+
if (unknown)
59+
return;
60+
if (unknown) // NOT OK
61+
return;
62+
});
63+
64+
(function (...x) {
65+
x || y // NOT OK
66+
});
67+
68+
(function() {
69+
function f1(x) {
70+
x || y // NOT OK, but whitelisted
71+
}
72+
f1(true);
73+
74+
function f2(x) {
75+
while (true)
76+
x || y // NOT OK
77+
}
78+
f2(true);
79+
80+
function f3(x) {
81+
(function(){
82+
x || y // NOT OK
83+
});
84+
}
85+
f3(true);
86+
});
4787
// semmle-extractor-options: --experimental

javascript/ql/test/query-tests/Statements/UselessConditional/UselessConditionalGood.js

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,97 @@ function getLastLine(input) {
55
if (!lines.length)
66
throw new Error("No lines!");
77
return lines[lines.length-1];
8-
}
8+
}
9+
10+
(function tooSpecificFunctions(){
11+
function f1() {
12+
return false
13+
}
14+
if(f1()){} // OK, whitelisted
15+
16+
function f2() {
17+
return false
18+
}
19+
if(!f2()){} // OK, whitelisted
20+
21+
function f3() {
22+
return false
23+
}
24+
if(!!f3()){} // OK, whitelisted
25+
26+
function f4() {
27+
return false
28+
}
29+
if(f4() || o.p){} // OK, whitelisted
30+
31+
function f5() {
32+
return false
33+
}
34+
var v5 = f5();
35+
if(v5){} // OK, whitelisted
36+
37+
function f6() {
38+
return false
39+
}
40+
var v6 = f6();
41+
if(!!v6){} // OK, whitelisted
42+
})();
43+
44+
(function tooGeneralFunctions(){
45+
function f1(x) {
46+
if(x){} // OK, whitelisted
47+
}
48+
f1(undefined);
49+
f1({});
50+
51+
function f2(x) {
52+
if(x){} // OK, whitelisted
53+
}
54+
f2(undefined);
55+
56+
function f3(x1) {
57+
var x2 = x1;
58+
if(x2){} // NOT OK, not whitelisted
59+
}
60+
f3(undefined);
61+
62+
function f4(x) {
63+
if(x && o.p){} // OK, whitelisted
64+
}
65+
f4(undefined);
66+
67+
function f5(x, y) {
68+
var xy = o.q? x: y;
69+
if(xy && o.p){} // NOT OK, not whitelisted
70+
}
71+
f5(undefined, undefined);
72+
73+
function f6(x) {
74+
if(!x){} // OK, whitelisted
75+
}
76+
f6(true);
77+
78+
function f7(x) {
79+
if(!!x){} // OK, whitelisted
80+
}
81+
f7(true);
82+
83+
function f8(x, y) {
84+
var xy = x || y;
85+
if(xy){} // NOT OK, not whitelisted
86+
}
87+
f8(undefined, undefined);
88+
89+
function f9(x, y) {
90+
var xy = !x || y;
91+
if(xy){} // OK, whitelisted
92+
}
93+
f9(undefined, undefined);
94+
95+
function f10(x, y) {
96+
var xy = !!x || y;
97+
if(xy){} // NOT OK, not whitelisted
98+
}
99+
f10(undefined, undefined);
100+
101+
})();

0 commit comments

Comments
 (0)