forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInconsistentNew.ql
More file actions
81 lines (76 loc) · 2.88 KB
/
InconsistentNew.ql
File metadata and controls
81 lines (76 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* @name Inconsistent use of 'new'
* @description If a function is intended to be a constructor, it should always
* be invoked with 'new'. Otherwise, it should always be invoked
* as a normal function, that is, without 'new'.
* @kind problem
* @problem.severity warning
* @id js/inconsistent-use-of-new
* @tags reliability
* correctness
* language-features
* @precision very-high
*/
import javascript
import semmle.javascript.RestrictedLocations
/**
* Holds if `f` contains code to guard against being invoked without `new`.
*
* There are many ways to implement such a check, but ultimately `f` will
* have to call itself using `new`, so that is what we look for.
*/
predicate guardsAgainstMissingNew(Function f) {
exists (DataFlow::NewNode new |
new.asExpr().getEnclosingFunction() = f and
f = new.getACallee()
)
}
/**
* Holds if `callee` is a function that may be invoked at callsite `cs`,
* where `imprecision` is a heuristic measure of how likely it is that `callee`
* is only suggested as a potential callee due to imprecise analysis of global
* variables and is not, in fact, a viable callee at all.
*/
predicate calls(DataFlow::InvokeNode cs, Function callee, int imprecision) {
callee = cs.getACallee() and
(
// if global flow was used to derive the callee, we may be imprecise
if cs.isIndefinite("global") then
// callees within the same file are probably genuine
callee.getFile() = cs.getFile() and imprecision = 0
or
// calls to global functions declared in an externs file are fairly
// safe as well
callee.inExternsFile() and imprecision = 1
or
// otherwise we make worst-case assumptions
imprecision = 2
else
// no global flow, so no imprecision
imprecision = 0
)
}
/**
* Gets a function that may be invoked at `cs`, preferring callees that
* are less likely to be derived due to analysis imprecision.
*/
Function getALikelyCallee(DataFlow::InvokeNode cs) {
calls(cs, result, min(int p | calls(cs, _, p)))
}
from Function f, DataFlow::NewNode new, DataFlow::CallNode call
where // externs are special, so don't flag them
not f.inExternsFile() and
// illegal constructor calls are flagged by query 'Illegal invocation',
// so don't flag them
not f instanceof Constructor and
f = getALikelyCallee(new) and
f = getALikelyCallee(call) and
not guardsAgainstMissingNew(f) and
not new.isUncertain() and
not call.isUncertain() and
// super constructor calls behave more like `new`, so don't flag them
not call.asExpr() instanceof SuperCall and
// don't flag if there is a receiver object
not exists(call.getReceiver())
select (FirstLineOf)f, capitalize(f.describe()) + " is invoked as a constructor here $@, " +
"and as a normal function here $@.", new, new.toString(), call, call.toString()