Skip to content

Commit 5704ac3

Browse files
committed
Rework LDAP framework modeling
1 parent 13cfcec commit 5704ac3

File tree

1 file changed

+74
-50
lines changed
  • python/ql/src/experimental/semmle/python/frameworks

1 file changed

+74
-50
lines changed

python/ql/src/experimental/semmle/python/frameworks/LDAP.qll

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ private module LDAP {
1919
* See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html
2020
*/
2121
private module LDAP2 {
22+
/** Gets a reference to the `ldap` module. */
23+
API::Node ldap() { result = API::moduleImport("ldap") }
24+
25+
/** Returns a `ldap` module instance */
26+
API::Node ldapInitialize() { result = ldap().getMember("initialize") }
27+
28+
/** Gets a reference to a `ldap` operation. */
29+
private DataFlow::LocalSourceNode ldapOperation(DataFlow::TypeTracker t) {
30+
t.start() and
31+
result.(DataFlow::AttrRead).getObject().getALocalSource() = ldapInitialize().getACall()
32+
or
33+
exists(DataFlow::TypeTracker t2 | result = ldapOperation(t2).track(t2, t))
34+
}
35+
2236
/**
2337
* List of `ldap` methods used to execute a query.
2438
*
@@ -30,32 +44,45 @@ private module LDAP {
3044
}
3145
}
3246

47+
/** Gets a reference to a `ldap` operation. */
48+
private DataFlow::Node ldapOperation() {
49+
ldapOperation(DataFlow::TypeTracker::end()).flowsTo(result)
50+
}
51+
52+
/** Gets a reference to a `ldap` query. */
53+
private DataFlow::Node ldapQuery() {
54+
result = ldapOperation() and
55+
result.(DataFlow::AttrRead).getAttributeName() instanceof LDAP2QueryMethods
56+
}
57+
3358
/**
3459
* A class to find `ldap` methods executing a query.
3560
*
3661
* See `LDAP2QueryMethods`
3762
*/
3863
private class LDAP2Query extends DataFlow::CallCfgNode, LDAPQuery::Range {
39-
DataFlow::Node ldapQuery;
40-
41-
LDAP2Query() {
42-
exists(DataFlow::AttrRead searchMethod |
43-
this.getFunction() = searchMethod and
44-
API::moduleImport("ldap").getMember("initialize").getACall() =
45-
searchMethod.getObject().getALocalSource() and
46-
searchMethod.getAttributeName() instanceof LDAP2QueryMethods and
47-
(
48-
ldapQuery = this.getArg(0)
49-
or
50-
(
51-
ldapQuery = this.getArg(2) or
52-
ldapQuery = this.getArgByName("filterstr")
53-
)
54-
)
55-
)
64+
LDAP2Query() { this.getFunction() = ldapQuery() }
65+
66+
override DataFlow::Node getQuery() {
67+
result in [this.getArg(0), [this.getArg(2), this.getArgByName("filterstr")]]
5668
}
69+
}
5770

58-
override DataFlow::Node getQuery() { result = ldapQuery }
71+
/** Gets a reference to a `ldap` bind. */
72+
private DataFlow::Node ldapBind() {
73+
result = ldapOperation() and
74+
result.(DataFlow::AttrRead).getAttributeName().matches("%bind%")
75+
}
76+
77+
/**
78+
* A class to find `ldap` methods binding a connection.
79+
*
80+
* See `LDAP2QueryMethods`
81+
*/
82+
private class LDAP2Bind extends DataFlow::CallCfgNode, LDAPBind::Range {
83+
LDAP2Bind() { this.getFunction() = ldapBind() }
84+
85+
override DataFlow::Node getPassword() { result = this.getArg(1) }
5986
}
6087

6188
/**
@@ -64,9 +91,7 @@ private module LDAP {
6491
* See https://github.com/python-ldap/python-ldap/blob/7ce471e238cdd9a4dd8d17baccd1c9e05e6f894a/Lib/ldap/dn.py#L17
6592
*/
6693
private class LDAP2EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range {
67-
LDAP2EscapeDNCall() {
68-
this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall()
69-
}
94+
LDAP2EscapeDNCall() { this = ldap().getMember("dn").getMember("escape_dn_chars").getACall() }
7095

7196
override DataFlow::Node getAnInput() { result = this.getArg(0) }
7297
}
@@ -78,8 +103,7 @@ private module LDAP {
78103
*/
79104
private class LDAP2EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range {
80105
LDAP2EscapeFilterCall() {
81-
this =
82-
API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall()
106+
this = ldap().getMember("filter").getMember("escape_filter_chars").getACall()
83107
}
84108

85109
override DataFlow::Node getAnInput() { result = this.getArg(0) }
@@ -92,26 +116,38 @@ private module LDAP {
92116
* See https://pypi.org/project/ldap3/
93117
*/
94118
private module LDAP3 {
119+
/** Gets a reference to the `ldap3` module. */
120+
API::Node ldap3() { result = API::moduleImport("ldap3") }
121+
122+
/** Gets a reference to the `ldap3` `utils` module. */
123+
API::Node ldap3Utils() { result = ldap3().getMember("utils") }
124+
125+
/** Returns a `ldap3` module `Server` instance */
126+
API::Node ldap3Server() { result = ldap3().getMember("Server") }
127+
128+
/** Returns a `ldap3` module `Connection` instance */
129+
API::Node ldap3Connection() { result = ldap3().getMember("Connection") }
130+
95131
/**
96132
* A class to find `ldap3` methods executing a query.
97133
*/
98134
private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range {
99-
DataFlow::Node ldapQuery;
100-
101135
LDAP3Query() {
102-
exists(DataFlow::AttrRead searchMethod |
103-
this.getFunction() = searchMethod and
104-
API::moduleImport("ldap3").getMember("Connection").getACall() =
105-
searchMethod.getObject().getALocalSource() and
106-
searchMethod.getAttributeName() = "search" and
107-
(
108-
ldapQuery = this.getArg(0) or
109-
ldapQuery = this.getArg(1)
110-
)
111-
)
136+
this.getFunction().(DataFlow::AttrRead).getObject().getALocalSource() =
137+
ldap3Connection().getACall() and
138+
this.getFunction().(DataFlow::AttrRead).getAttributeName() = "search"
112139
}
113140

114-
override DataFlow::Node getQuery() { result = ldapQuery }
141+
override DataFlow::Node getQuery() { result in [this.getArg(0), this.getArg(1)] }
142+
}
143+
144+
/**
145+
* A class to find `ldap3` methods binding a connection.
146+
*/
147+
class LDAP3Bind extends DataFlow::CallCfgNode, LDAPBind::Range {
148+
LDAP3Bind() { this = ldap3Connection().getACall() }
149+
150+
override DataFlow::Node getPassword() { result = this.getArgByName("password") }
115151
}
116152

117153
/**
@@ -120,14 +156,7 @@ private module LDAP {
120156
* See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/dn.py#L390
121157
*/
122158
private class LDAP3EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range {
123-
LDAP3EscapeDNCall() {
124-
this =
125-
API::moduleImport("ldap3")
126-
.getMember("utils")
127-
.getMember("dn")
128-
.getMember("escape_rdn")
129-
.getACall()
130-
}
159+
LDAP3EscapeDNCall() { this = ldap3Utils().getMember("dn").getMember("escape_rdn").getACall() }
131160

132161
override DataFlow::Node getAnInput() { result = this.getArg(0) }
133162
}
@@ -139,12 +168,7 @@ private module LDAP {
139168
*/
140169
private class LDAP3EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range {
141170
LDAP3EscapeFilterCall() {
142-
this =
143-
API::moduleImport("ldap3")
144-
.getMember("utils")
145-
.getMember("conv")
146-
.getMember("escape_filter_chars")
147-
.getACall()
171+
this = ldap3Utils().getMember("conv").getMember("escape_filter_chars").getACall()
148172
}
149173

150174
override DataFlow::Node getAnInput() { result = this.getArg(0) }

0 commit comments

Comments
 (0)