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

Skip to content

Commit e4ea089

Browse files
committed
JS: add experimental PoI module
1 parent ec73c97 commit e4ea089

8 files changed

Lines changed: 395 additions & 0 deletions

File tree

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/**
2+
* Provides classes and predicates for discovering points of interest
3+
* in an unknown code base.
4+
*
5+
* To use this module, subclass the
6+
* `Poi::PoI` class, override *one* of its `is` predicates, and use
7+
* `PoI::alertQuery` as a `@kind problem` query . This will present
8+
* the desired points of interest as alerts that are easily browsable
9+
* in a codeql IDE. By itself, this is no different from an ordinary
10+
* query, but the strength of this module lies in its extensibility
11+
* and standard library:
12+
*
13+
* - points of interest can be added, removed and mixed seamlessly
14+
* - this module comes with a collection of standard points of interest (see `PoI::StandardPoIs`)
15+
* - this modules comes with groupings of related points of interest (see `PoI::StandardPoIConfigurations`)
16+
*
17+
* A global configuration for the points of interest (see
18+
* `PoI::PoIConfg`) can be used to easily manage multiple points of
19+
* interests, and to restrict the points of interest to specific
20+
* corners of the code base.
21+
*
22+
* Below is an example use of this module that will produce an alert
23+
* for each route handler and route handler setup in a file named
24+
* "server-core.js". The route setup alerts will contain a link to its
25+
* associated route handler.
26+
*
27+
* ```
28+
* /**
29+
* * @kind problem
30+
* *\/
31+
*
32+
* import PoI
33+
*
34+
* class Configuration extends PoI::PoIConfiguration {
35+
* Configuration() { this = "Configuration" }
36+
*
37+
* override predicate shown(DataFlow::Node n) { n.getFile().getBaseName() = "server-core.js" }
38+
* }
39+
*
40+
* class RouteHandlerPoI extends PoI::PoI {
41+
* RouteHandlerPoI() { this = "RouteHandlerPoI" }
42+
* override predicate is(DataFlow::Node l0) { l0 instanceof Express::RouteHandler }
43+
* }
44+
*
45+
* class RouteSetupAndRouteHandlerPoI extends PoI::PoI {
46+
* RouteSetupAndRouteHandlerPoI() { this = "RouteSetupAndRouteHandlerPoI" }
47+
*
48+
* override predicate is(DataFlow::Node l0, DataFlow::Node l1, string t1) {
49+
* l0.asExpr().(Express::RouteSetup).getARouteHandler() = l1 and t1 = "routehandler"
50+
* }
51+
* }
52+
*
53+
* query predicate problems = PoI::alertQuery/6;
54+
* ```
55+
*/
56+
57+
import javascript
58+
private import DataFlow
59+
private import filters.ClassifyFiles
60+
private import semmle.javascript.RestrictedLocations
61+
62+
module PoI {
63+
/**
64+
* Provides often used points of interest.
65+
*/
66+
module StandardPoIs {
67+
/**
68+
* An unpromoted route setup candidate.
69+
*/
70+
class UnpromotedRouteSetupPoI extends StandardPoI {
71+
UnpromotedRouteSetupPoI() { this = "UnpromotedRouteSetupPoI" }
72+
73+
override predicate is(Node l0) {
74+
l0 instanceof HTTP::RouteSetupCandidate and not l0.asExpr() instanceof HTTP::RouteSetup
75+
}
76+
}
77+
78+
/**
79+
* An unpromoted route handler candidate.
80+
*/
81+
class UnpromotedRouteHandlerPoI extends StandardPoI {
82+
UnpromotedRouteHandlerPoI() { this = "UnpromotedRouteHandlerPoI" }
83+
84+
override predicate is(Node l0) {
85+
l0 instanceof HTTP::RouteHandlerCandidate and not l0 instanceof HTTP::RouteHandler
86+
}
87+
}
88+
89+
/**
90+
* An unpromoted route handler candidate, with explnatory data flow information.
91+
*/
92+
class UnpromotedRouteHandlerWithFlowPoI extends StandardPoI {
93+
UnpromotedRouteHandlerWithFlowPoI() { this = "UnpromotedRouteHandlerWithFlowPoI" }
94+
95+
private DataFlow::SourceNode track(HTTP::RouteHandlerCandidate cand, DataFlow::TypeTracker t) {
96+
t.start() and
97+
result = cand
98+
or
99+
exists(DataFlow::TypeTracker t2 | result = track(cand, t2).track(t2, t))
100+
}
101+
102+
override predicate is(Node l0, Node l1, string t1) {
103+
l0 instanceof HTTP::RouteHandlerCandidate and
104+
not l0 instanceof HTTP::RouteHandler and
105+
l1 = track(l0, TypeTracker::end()) and
106+
(if l1 = l0 then t1 = "ends here" else t1 = "starts/ends here")
107+
}
108+
}
109+
110+
/**
111+
* A callee that is unknown.
112+
*/
113+
class UnknownCalleePoI extends StandardPoI {
114+
UnknownCalleePoI() { this = "UnknownCalleePoI" }
115+
116+
override predicate is(Node l0) {
117+
exists(InvokeNode invk | l0 = invk.getCalleeNode() and not exists(invk.getACallee()))
118+
}
119+
}
120+
121+
/**
122+
* A source of remote flow.
123+
*/
124+
class RemoteFlowSourcePoI extends StandardPoI {
125+
RemoteFlowSourcePoI() { this = "RemoteFlowSourcePoI" }
126+
127+
override predicate is(Node l0) { l0 instanceof RemoteFlowSource }
128+
}
129+
130+
/**
131+
* A "source" for any active configuration.
132+
*/
133+
class SourcePoI extends StandardPoI {
134+
SourcePoI() { this = "SourcePoI" }
135+
136+
override predicate is(Node l0) {
137+
exists(Configuration cfg | cfg.isSource(l0) or cfg.isSource(l0, _))
138+
}
139+
}
140+
141+
/**
142+
* A "sink" for any active configuration.
143+
*/
144+
class SinkPoI extends StandardPoI {
145+
SinkPoI() { this = "SinkPoI" }
146+
147+
override predicate is(Node l0) {
148+
exists(Configuration cfg | cfg.isSink(l0) or cfg.isSink(l0, _))
149+
}
150+
}
151+
152+
/**
153+
* A "barrier" for any active configuration.
154+
*/
155+
class BarrierPoI extends StandardPoI {
156+
BarrierPoI() { this = "BarrierPoI" }
157+
158+
override predicate is(Node l0) {
159+
exists(Configuration cfg |
160+
cfg.isBarrier(_) or
161+
cfg.isBarrierEdge(l0, _) or
162+
cfg.isBarrierEdge(l0, _, _) or
163+
cfg.isLabeledBarrier(l0, _)
164+
)
165+
}
166+
}
167+
}
168+
169+
/**
170+
* Provides often used point of interest configurations.
171+
*/
172+
module StandardPoIConfigurations {
173+
private import StandardPoIs
174+
175+
/**
176+
* A configuration that enables some server related points of interest.
177+
*/
178+
abstract class ServerPoIConfiguration extends PoIConfiguration {
179+
bindingset[this]
180+
ServerPoIConfiguration() { any() }
181+
182+
override predicate enabled(PoI poi) {
183+
poi instanceof UnpromotedRouteSetupPoI or
184+
poi instanceof UnpromotedRouteHandlerPoI or
185+
poi instanceof UnpromotedRouteHandlerWithFlowPoI
186+
}
187+
}
188+
189+
/**
190+
* A configuration that enables some `DataFlow::Configuration` related points of interest.
191+
*/
192+
abstract class DataFlowConfigurationPoIConfiguration extends PoIConfiguration {
193+
bindingset[this]
194+
DataFlowConfigurationPoIConfiguration() { any() }
195+
196+
override predicate enabled(PoI poi) {
197+
poi instanceof SourcePoI or
198+
poi instanceof SinkPoI
199+
}
200+
}
201+
}
202+
203+
/**
204+
* A tagging interface for the standard points of interest.
205+
*/
206+
abstract private class StandardPoI extends PoI {
207+
bindingset[this]
208+
StandardPoI() { any() }
209+
}
210+
211+
private module PoIConfigDefaults {
212+
predicate enabled(PoI poi) { not poi instanceof StandardPoI }
213+
214+
predicate shown(Node n) { not classify(n.getFile(), _) }
215+
}
216+
217+
/**
218+
* A configuration for the points of interest to display.
219+
*/
220+
abstract class PoIConfiguration extends string {
221+
bindingset[this]
222+
PoIConfiguration() { any() }
223+
224+
/**
225+
* Holds if the points of interest from `poi` should be shown.
226+
*/
227+
predicate enabled(PoI poi) { PoIConfigDefaults::enabled(poi) }
228+
229+
/**
230+
* Holds if the points of interest `n` should be shown.
231+
*/
232+
predicate shown(Node n) { PoIConfigDefaults::shown(n) }
233+
}
234+
235+
/**
236+
* A class of points of interest.
237+
*
238+
* Note that only one of the `is/1`, `is/3`, `is/5` methods should
239+
* be overridden, as two overrides will degrade the alert UI
240+
* slightly.
241+
*/
242+
abstract class PoI extends string {
243+
bindingset[this]
244+
PoI() { any() }
245+
246+
/**
247+
* Holds if `l0` is a point of interest.
248+
*/
249+
predicate is(Node l0) { none() }
250+
251+
/**
252+
* Holds if `l0` is a point of interest, with `l1` as an auxiliary location described by `t1`.
253+
*/
254+
predicate is(Node l0, Node l1, string t1) { none() }
255+
256+
/**
257+
* Holds if `l0` is a point of interest, with `l1` and `l2` as auxiliary locations described by `t1` and `t2`.
258+
*/
259+
predicate is(Node l0, Node l1, string t1, Node l2, string t2) { none() }
260+
261+
/**
262+
* Gets the message format for the point of interest.
263+
*/
264+
string getFormat() {
265+
is(_) and result = ""
266+
or
267+
is(_, _, _) and result = "$@"
268+
or
269+
is(_, _, _, _, _) and result = "$@ $@"
270+
}
271+
}
272+
273+
/**
274+
* An alert query for a point of interest.
275+
*
276+
* Should be used as: `query predicate problems = PoI::alertQuery/6;`
277+
*
278+
* Note that some points of interest do not have auxiliary
279+
* locations, so `l2`,`l3`, `s2`, `s3` may have placeholder values.
280+
*/
281+
predicate alertQuery(Locatable l1line, string msg, Node l2, string s2, Node l3, string s3) {
282+
exists(PoI poi, Node l1, string m |
283+
l1.getAstNode().(FirstLineOf) = l1line and
284+
(
285+
not exists(PoIConfiguration cfg) and
286+
PoIConfigDefaults::enabled(poi) and
287+
PoIConfigDefaults::shown(l1) and
288+
PoIConfigDefaults::shown(l2) and
289+
PoIConfigDefaults::shown(l3)
290+
or
291+
exists(PoIConfiguration cfg |
292+
cfg.enabled(poi) and
293+
cfg.shown(l1) and
294+
cfg.shown(l2) and
295+
cfg.shown(l3)
296+
)
297+
) and
298+
m = poi.getFormat() and
299+
if m = "" then msg = poi else msg = poi + ": " + m
300+
|
301+
poi.is(l1) and
302+
l1 = l2 and
303+
s2 = "irrelevant" and
304+
l1 = l3 and
305+
s3 = "irrelevant"
306+
or
307+
poi.is(l1, l2, s2) and
308+
l1 = l3 and
309+
s3 = "irrelevant"
310+
or
311+
poi.is(l1, l2, s2, l3, s3)
312+
)
313+
}
314+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
| tst.js:6:1:6:44 | app.get ... es) {}) | RouteSetupAndRouterAndRouteHandlerPoI: $@ $@ | tst.js:2:11:2:19 | express() | router | tst.js:6:23:6:43 | functio ... res) {} | routehandler |
2+
| tst.js:6:23:6:43 | functio ... res) {} | RouteHandlerAndSetupPoI: $@ | tst.js:6:1:6:44 | app.get ... es) {}) | setup | tst.js:6:23:6:43 | functio ... res) {} | irrelevant |
3+
| tst.js:6:23:6:43 | functio ... res) {} | RouteHandlerPoI | tst.js:6:23:6:43 | functio ... res) {} | irrelevant | tst.js:6:23:6:43 | functio ... res) {} | irrelevant |
4+
| tst.js:8:10:8:30 | functio ... res) {} | RouteHandlerAndSetupPoI: $@ | tst.js:9:1:9:31 | app.get ... h", rh) | setup | tst.js:8:10:8:30 | functio ... res) {} | irrelevant |
5+
| tst.js:8:10:8:30 | functio ... res) {} | RouteHandlerPoI | tst.js:8:10:8:30 | functio ... res) {} | irrelevant | tst.js:8:10:8:30 | functio ... res) {} | irrelevant |
6+
| tst.js:9:1:9:31 | app.get ... h", rh) | RouteSetupAndRouterAndRouteHandlerPoI: $@ $@ | tst.js:2:11:2:19 | express() | router | tst.js:8:10:8:30 | functio ... res) {} | routehandler |
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @kind problem
3+
*/
4+
5+
import javascript
6+
import experimental.PoI.PoI::PoI
7+
import DataFlow
8+
9+
class RouteHandlerPoI extends PoI {
10+
RouteHandlerPoI() { this = "RouteHandlerPoI" }
11+
12+
override predicate is(Node l0) { l0 instanceof Express::RouteHandler }
13+
}
14+
15+
class RouteHandlerAndSetupPoI extends PoI {
16+
RouteHandlerAndSetupPoI() { this = "RouteHandlerAndSetupPoI" }
17+
18+
override predicate is(Node l0, Node l1, string t1) {
19+
l1.asExpr().(Express::RouteSetup).getARouteHandler() = l0 and t1 = "setup"
20+
}
21+
}
22+
23+
class RouteSetupAndRouterAndRouteHandlerPoI extends PoI {
24+
RouteSetupAndRouterAndRouteHandlerPoI() { this = "RouteSetupAndRouterAndRouteHandlerPoI" }
25+
26+
override predicate is(Node l0, Node l1, string t1, Node l2, string t2) {
27+
l0.asExpr().(Express::RouteSetup).getRouter().flow() = l1 and
28+
t1 = "router" and
29+
l0.asExpr().(Express::RouteSetup).getARouteHandler() = l2 and
30+
t2 = "routehandler"
31+
}
32+
}
33+
34+
query predicate problems = alertQuery/6;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| tst.js:4:1:4:16 | (req, res) => 42 | UnpromotedRouteHandlerPoI | tst.js:4:1:4:16 | (req, res) => 42 | irrelevant | tst.js:4:1:4:16 | (req, res) => 42 | irrelevant |
2+
| tst.js:4:1:4:16 | (req, res) => 42 | UnpromotedRouteHandlerWithFlowPoI: $@ | tst.js:4:1:4:16 | (req, res) => 42 | ends here | tst.js:4:1:4:16 | (req, res) => 42 | irrelevant |
3+
| tst.js:11:1:11:36 | otherAp ... h", rh) | UnpromotedRouteSetupPoI | tst.js:11:1:11:36 | otherAp ... h", rh) | irrelevant | tst.js:11:1:11:36 | otherAp ... h", rh) | irrelevant |
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @kind problem
3+
*/
4+
5+
import javascript
6+
import experimental.PoI.PoI::PoI
7+
8+
class Config extends StandardPoIConfigurations::ServerPoIConfiguration {
9+
Config() { this = "Config" }
10+
}
11+
12+
query predicate problems = alertQuery/6;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| tst.js:4:1:4:16 | (req, res) => 42 | UnpromotedRouteHandlerPoI | tst.js:4:1:4:16 | (req, res) => 42 | irrelevant | tst.js:4:1:4:16 | (req, res) => 42 | irrelevant |
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @kind problem
3+
*/
4+
5+
import javascript
6+
import experimental.PoI.PoI::PoI
7+
8+
class Config extends PoIConfiguration {
9+
Config() { this = "Config" }
10+
11+
override predicate enabled(PoI poi) { poi instanceof StandardPoIs::UnpromotedRouteHandlerPoI }
12+
}
13+
14+
query predicate problems = alertQuery/6;

0 commit comments

Comments
 (0)