Skip to content

Commit e6672aa

Browse files
author
Max Schaefer
authored
Merge pull request #804 from esben-semmle/js/sharpen-unneeded-defensive
JS: better handling of nested expressions in js/unneeded-defensive-code
2 parents 45476f3 + 9e46130 commit e6672aa

File tree

5 files changed

+22
-10
lines changed

5 files changed

+22
-10
lines changed

change-notes/1.20/analysis-javascript.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
| Unused parameter | Fewer false-positive results | This rule no longer flags parameters with leading underscore. |
3333
| Unused variable, import, function or class | Fewer false-positive results | This rule now flags fewer variables that are implictly used by JSX elements, and no longer flags variables with leading underscore. |
3434
| Uncontrolled data used in path expression | Fewer false-positive results | This rule now recognizes the Express `root` option, which prevents path traversal. |
35+
| Unneeded defensive code | More true-positive results, fewer false-positive results. | This rule now recognizes additional defensive code patterns. |
36+
| Useless conditional | Fewer results | Additional defensive coding patterns are now ignored. |
3537
| Useless assignment to property. | Fewer false-positive results | This rule now treats assignments with complex right-hand sides correctly. |
3638
| Unsafe dynamic method access | Fewer false-positive results | This rule no longer flags concatenated strings as unsafe method names. |
3739
| Unvalidated dynamic method call | More true-positive results | This rule now flags concatenated strings as unvalidated method names in more cases. |

javascript/ql/src/semmle/javascript/DefensiveProgramming.qll

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ module Internal {
5353
* `polarity` is true iff the inner expression is nested in an even number of negations.
5454
*/
5555
private Expr stripNotsAndParens(Expr e, boolean polarity) {
56-
exists(Expr inner | inner = e.getUnderlyingValue() |
56+
exists(Expr inner | inner = e.stripParens() |
5757
if inner instanceof LogNotExpr
5858
then result = stripNotsAndParens(inner.(LogNotExpr).getOperand(), polarity.booleanNot())
5959
else (
@@ -199,11 +199,14 @@ module Internal {
199199
Expr target;
200200

201201
UndefinedNullCrashUse() {
202-
this.(InvokeExpr).getCallee().getUnderlyingValue() = target
203-
or
204-
this.(PropAccess).getBase().getUnderlyingValue() = target
205-
or
206-
this.(MethodCallExpr).getReceiver().getUnderlyingValue() = target
202+
exists (Expr thrower |
203+
stripNotsAndParens(this, _) = thrower |
204+
thrower.(InvokeExpr).getCallee().getUnderlyingValue() = target
205+
or
206+
thrower.(PropAccess).getBase().getUnderlyingValue() = target
207+
or
208+
thrower.(MethodCallExpr).getReceiver().getUnderlyingValue() = target
209+
)
207210
}
208211

209212
/**
@@ -220,7 +223,8 @@ module Internal {
220223
private class NonFunctionCallCrashUse extends Expr {
221224
Expr target;
222225

223-
NonFunctionCallCrashUse() { this.(InvokeExpr).getCallee().getUnderlyingValue() = target }
226+
NonFunctionCallCrashUse() {
227+
stripNotsAndParens(this, _).(InvokeExpr).getCallee().getUnderlyingValue() = target }
224228

225229
/**
226230
* Gets the subexpression that will cause an exception to be thrown if it is not a `function`.
@@ -273,7 +277,6 @@ module Internal {
273277
guardVar.getVariable() = useVar.getVariable()
274278
|
275279
getAGuardedExpr(this.asExpr())
276-
.getUnderlyingValue()
277280
.(UndefinedNullCrashUse)
278281
.getVulnerableSubexpression() = useVar and
279282
// exclude types whose truthiness depend on the value
@@ -306,7 +309,6 @@ module Internal {
306309
guardVar.getVariable() = useVar.getVariable()
307310
|
308311
getAGuardedExpr(guard)
309-
.getUnderlyingValue()
310312
.(UndefinedNullCrashUse)
311313
.getVulnerableSubexpression() = useVar
312314
)
@@ -375,7 +377,6 @@ module Internal {
375377
guardVar.getVariable() = useVar.getVariable()
376378
|
377379
getAGuardedExpr(guard)
378-
.getUnderlyingValue()
379380
.(NonFunctionCallCrashUse)
380381
.getVulnerableSubexpression() = useVar
381382
) and

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,5 @@
7070
| tst.js:140:13:140:22 | x === null | This guard always evaluates to false. |
7171
| tst.js:156:23:156:31 | x != null | This guard always evaluates to true. |
7272
| tst.js:158:13:158:21 | x != null | This guard always evaluates to true. |
73+
| tst.js:177:2:177:2 | u | This guard always evaluates to false. |
74+
| tst.js:178:2:178:2 | u | This guard always evaluates to false. |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| tst.js:175:2:175:2 | u | This use of variable 'u' always evaluates to false. |
2+
| tst.js:176:2:176:2 | u | This use of variable 'u' always evaluates to false. |

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,9 @@
171171
if (typeof window !== "undefined" && window.document);
172172
if (typeof module !== "undefined" && module.exports);
173173
if (typeof global !== "undefined" && global.process);
174+
175+
u && (f(), u.p);
176+
u && (u.p, f()); // technically not OK, but it seems like an unlikely pattern
177+
u && !u.p; // NOT OK
178+
u && !u(); // NOT OK
174179
});

0 commit comments

Comments
 (0)