Skip to content

Commit dac71de

Browse files
committed
Python: Add Authorization modeling in Flask
1 parent 1336321 commit dac71de

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

python/ql/src/semmle/python/frameworks/Flask.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,13 @@ module Flask {
439439
}
440440
}
441441

442+
/** An `Authorization` instance that originates from a flask request. */
443+
private class FlaskRequestAuthorizationInstances extends Werkzeug::Authorization::InstanceSource {
444+
FlaskRequestAuthorizationInstances() {
445+
this.(DataFlow::AttrRead).accesses(request().getAUse(), "authorization")
446+
}
447+
}
448+
442449
// ---------------------------------------------------------------------------
443450
// Implicit response from returns of flask request handlers
444451
// ---------------------------------------------------------------------------

python/ql/src/semmle/python/frameworks/Werkzeug.qll

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,50 @@ module Werkzeug {
169169
}
170170
}
171171

172+
/**
173+
* Provides models for the `werkzeug.datastructures.Authorization` class
174+
*
175+
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization.
176+
*/
177+
module Authorization {
178+
/**
179+
* A source of instances of `werkzeug.datastructures.Authorization`, extend this class to model new instances.
180+
*
181+
* This can include instantiations of the class, return values from function
182+
* calls, or a special parameter that will be set when functions are called by an external
183+
* library.
184+
*
185+
* Use the predicate `Authorization::instance()` to get references to instances of `werkzeug.datastructures.Authorization`.
186+
*/
187+
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
188+
189+
/** Gets a reference to an instance of `werkzeug.datastructures.Authorization`. */
190+
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
191+
t.start() and
192+
result instanceof InstanceSource
193+
or
194+
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
195+
}
196+
197+
/** Gets a reference to an instance of `werkzeug.datastructures.Authorization`. */
198+
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
199+
200+
/**
201+
* Taint propagation for `werkzeug.datastructures.Authorization`.
202+
*/
203+
class AuthorizationAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
204+
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
205+
// Attributes
206+
nodeFrom = instance() and
207+
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
208+
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
209+
"username", "password", "realm", "nonce", "uri", "nc", "cnonce", "response", "opaque",
210+
"qop"
211+
]
212+
}
213+
}
214+
}
215+
172216
import WerkzeugOld
173217
}
174218

python/ql/test/library-tests/frameworks/flask/taint_test.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,16 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
4444
# werkzeug.datastructures.Authorization (a dict, with some properties)
4545
request.authorization, # $ tainted
4646
request.authorization['username'], # $ tainted
47-
request.authorization.username, # $ MISSING: tainted
47+
request.authorization.username, # $ tainted
48+
request.authorization.password, # $ tainted
49+
request.authorization.realm, # $ tainted
50+
request.authorization.nonce, # $ tainted
51+
request.authorization.uri, # $ tainted
52+
request.authorization.nc, # $ tainted
53+
request.authorization.cnonce, # $ tainted
54+
request.authorization.response, # $ tainted
55+
request.authorization.opaque, # $ tainted
56+
request.authorization.qop, # $ tainted
4857

4958
# werkzeug.datastructures.RequestCacheControl
5059
request.cache_control, # $ tainted

0 commit comments

Comments
 (0)