-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathConsistencyChecking.qll
More file actions
187 lines (169 loc) · 6.11 KB
/
ConsistencyChecking.qll
File metadata and controls
187 lines (169 loc) · 6.11 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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
* DEPRECATED, but can be imported with a `deprecated import`.
*
* Will be replaced with standardized inline test expectations in the future.
*/
import javascript
/**
* A configuration for consistency checking.
* Used to specify where the alerts are (the positives)
* And which files should be included in the consistency-check.
*
* If no configuration is specified, then the default is that the all sinks from a `DataFlow::Configuration` are alerts, and all files are consistency-checked.
*/
abstract deprecated class ConsistencyConfiguration extends string {
bindingset[this]
ConsistencyConfiguration() { any() }
/**
* Gets an alert that should be checked for consistency.
* The alert must match with a `NOT OK` comment.
*
* And likewise a `OK` comment must not have a corresponding alert on the same line.
*/
DataFlow::Node getAnAlert() { result = getASink() }
/**
* Gets a file to include in the consistency checking.
*/
File getAFile() { none() }
}
/**
* A string that either equals a `ConsistencyConfiguration`, or the empty string if no such configuration exists.
*
* Is used internally to match a configuration or lack thereof.
*/
deprecated final private class Conf extends string {
Conf() {
this instanceof ConsistencyConfiguration
or
not exists(ConsistencyConfiguration c) and
this = ""
}
}
/**
* A comment that asserts whether a result exists at that line or not.
* Can optionally include `[INCONSISTENCY]` to indicate that a consistency issue is expected at the location
*/
private class AssertionComment extends Comment {
boolean shouldHaveAlert;
AssertionComment() {
if this.getText().regexpMatch("\\s*(NOT OK|BAD).*")
then shouldHaveAlert = true
else (
this.getText().regexpMatch("\\s*(OK|GOOD).*") and shouldHaveAlert = false
)
}
/**
* Holds if there should be an alert at this location
*/
predicate shouldHaveAlert() { shouldHaveAlert = true }
/**
* Holds if a consistency issue is expected at this location.
*/
predicate expectConsistencyError() { this.getText().matches("%[INCONSISTENCY]%") }
}
deprecated private DataFlow::Node getASink() {
exists(DataFlow::Configuration cfg | cfg.hasFlow(_, result))
}
/**
* Gets all the alerts for consistency consistency checking from a configuration `conf`.
*/
deprecated private DataFlow::Node alerts(Conf conf) {
result = conf.(ConsistencyConfiguration).getAnAlert()
or
not exists(ConsistencyConfiguration r) and
result = getASink() and
conf = ""
}
/**
* Gets an alert in `file` at `line` for configuration `conf`.
* The `line` can be either the first or the last line of the alert.
* And if no expression exists at `line`, then an alert on the next line is used.
*/
deprecated private DataFlow::Node getAlert(File file, int line, Conf conf) {
result = alerts(conf) and
result.getFile() = file and
(result.hasLocationInfo(_, _, _, line, _) or result.hasLocationInfo(_, line, _, _, _))
or
// The comment can be right above the result, so an alert also counts for the line above.
not exists(Expr e |
e.getFile() = file and [e.getLocation().getStartLine(), e.getLocation().getEndLine()] = line
) and
result = alerts(conf) and
result.getFile() = file and
result.hasLocationInfo(_, line + 1, _, _, _)
}
/**
* Gets a comment that asserts either the existence or the absence of an alert in `file` at `line`.
*/
private AssertionComment getComment(File file, int line) {
result.getLocation().getEndLine() = line and
result.getFile() = file
}
/**
* Holds if there is a false positive in `file` at `line` for configuration `conf`.
*/
deprecated private predicate falsePositive(File file, int line, AssertionComment comment, Conf conf) {
exists(getAlert(file, line, conf)) and
comment = getComment(file, line) and
not comment.shouldHaveAlert()
}
/**
* Holds if there is a false negative in `file` at `line` for configuration `conf`.
*/
deprecated private predicate falseNegative(File file, int line, AssertionComment comment, Conf conf) {
not exists(getAlert(file, line, conf)) and
comment = getComment(file, line) and
comment.shouldHaveAlert()
}
/**
* Gets a file that should be included for consistency checking for configuration `conf`.
*/
deprecated private File getATestFile(string conf) {
not exists(any(ConsistencyConfiguration res).getAFile()) and
result = any(LineComment comment).getFile() and
(conf = "" or conf instanceof ConsistencyConfiguration)
or
result = conf.(ConsistencyConfiguration).getAFile()
}
/**
* Gets a description of the configuration that has a sink in `file` at `line` for configuration `conf`.
* Or the empty string
*/
bindingset[file, line]
deprecated private string getSinkDescription(File file, int line, Conf conf) {
not exists(DataFlow::Configuration c | c.hasFlow(_, getAlert(file, line, conf))) and
result = ""
or
exists(DataFlow::Configuration c | c.hasFlow(_, getAlert(file, line, conf)) |
result = " for " + c
)
}
/**
* Holds if there is a consistency-issue at `location` with description `msg` for configuration `conf`.
* The consistency issue an unexpected false positive/negative.
* Or that false positive/negative was expected, and none were found.
*/
deprecated query predicate consistencyIssue(
string location, string msg, string commentText, Conf conf
) {
exists(File file, int line |
file = getATestFile(conf) and location = file.getRelativePath() + ":" + line
|
exists(AssertionComment comment |
comment.getText().trim() = commentText and comment = getComment(file, line)
|
falsePositive(file, line, comment, conf) and
not comment.expectConsistencyError() and
msg = "did not expect an alert, but found an alert" + getSinkDescription(file, line, conf)
or
falseNegative(file, line, comment, conf) and
not comment.expectConsistencyError() and
msg = "expected an alert, but found none"
or
not falsePositive(file, line, comment, conf) and
not falseNegative(file, line, comment, conf) and
comment.expectConsistencyError() and
msg = "expected consistency issue, but found no such issue (" + comment.getText().trim() + ")"
)
)
}