Skip to content

Commit 48c1c59

Browse files
committed
Python: Write DjangoRegexRoute in more modern way
That is, assigning to fields instead of repeatedly using helper predicate
1 parent ed9aa7d commit 48c1c59

File tree

1 file changed

+42
-37
lines changed

1 file changed

+42
-37
lines changed

python/ql/src/semmle/python/web/django/General.qll

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,67 +6,72 @@ import semmle.python.web.Http
66
// a FunctionValue, so we can't use `FunctionValue.getArgumentForCall`
77
// https://github.com/django/django/blob/master/django/urls/conf.py#L76
88

9-
private predicate django_regex_route(CallNode call, ControlFlowNode regex, FunctionValue view) {
10-
exists(Value route_maker |
11-
(
9+
abstract class DjangoRoute extends CallNode {
10+
11+
abstract FunctionValue getViewFunction();
12+
13+
abstract string getANamedArgument();
14+
15+
/**
16+
* Get the number of positional arguments that will be passed to the view.
17+
* Will only return a result if there are no named arguments.
18+
*/
19+
abstract int getNumPositionalArguments();
20+
}
21+
22+
// We need this "dummy" class, since otherwise the regex argument would not be considered a regex (RegexString is abstract)
23+
class DjangoRouteRegex extends RegexString {
24+
DjangoRouteRegex() {
25+
exists(DjangoRegexRoute route | route.getRouteArg() = this.getAFlowNode())
26+
}
27+
}
28+
29+
class DjangoRegexRoute extends DjangoRoute {
30+
31+
ControlFlowNode route;
32+
FunctionValue view;
33+
34+
DjangoRegexRoute() {
35+
36+
exists(FunctionValue route_maker |
1237
// Django 1.x
1338
Value::named("django.conf.urls.url") = route_maker and
14-
route_maker.(FunctionValue).getArgumentForCall(call, 0) = regex and
15-
route_maker.(FunctionValue).getArgumentForCall(call, 1).pointsTo(view)
39+
route_maker.getArgumentForCall(this, 0) = route and
40+
route_maker.getArgumentForCall(this, 1).pointsTo(view)
1641
)
1742
or
1843
(
1944
// Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#re-path
20-
Value::named("django.urls.re_path") = route_maker and
21-
route_maker.getACall() = call and
45+
this = Value::named("django.urls.re_path").getACall() and
2246
(
23-
call.getArg(0) = regex
47+
route = this.getArg(0)
2448
or
25-
call.getArgByName("route") = regex
49+
route = this.getArgByName("route")
2650

2751
) and
2852
(
29-
call.getArg(1).pointsTo(view)
53+
this.getArg(1).pointsTo(view)
3054
or
31-
call.getArgByName("view").pointsTo(view)
55+
this.getArgByName("view").pointsTo(view)
3256
)
3357
)
34-
)
35-
}
36-
37-
class DjangoRouteRegex extends RegexString {
38-
DjangoRouteRegex() { django_regex_route(_, this.getAFlowNode(), _) }
39-
}
40-
41-
abstract class DjangoRoute extends CallNode {
42-
43-
abstract FunctionValue getViewFunction();
44-
45-
abstract string getANamedArgument();
46-
47-
/**
48-
* Get the number of positional arguments that will be passed to the view.
49-
* Will only return a result if there are no named arguments.
50-
*/
51-
abstract int getNumPositionalArguments();
52-
}
58+
}
5359

54-
class DjangoRegexRoute extends DjangoRoute {
55-
DjangoRegexRoute() { django_regex_route(this, _, _) }
60+
override FunctionValue getViewFunction() { result = view }
5661

57-
override FunctionValue getViewFunction() { django_regex_route(this, _, result) }
62+
ControlFlowNode getRouteArg() { result = route }
5863

5964
override string getANamedArgument() {
6065
exists(DjangoRouteRegex regex |
61-
django_regex_route(this, regex.getAFlowNode(), _) and
62-
regex.getGroupName(_, _) = result
66+
regex.getAFlowNode() = route |
67+
result = regex.getGroupName(_, _)
6368
)
6469
}
6570

6671
override int getNumPositionalArguments() {
72+
not exists(this.getANamedArgument()) and
6773
exists(DjangoRouteRegex regex |
68-
not exists(this.getANamedArgument()) and
69-
django_regex_route(this, regex.getAFlowNode(), _) and
74+
regex.getAFlowNode() = route |
7075
result = count(regex.getGroupNumber(_, _))
7176
)
7277
}

0 commit comments

Comments
 (0)