-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python: Model FastAPI requests #18318
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
category: minorAnalysis | ||
--- | ||
* Added modeling of `fastapi.Request` and `starlette.requests.Request` as sources of untrusted input, | ||
and modeling of tainted data flow out of these request objects. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -187,3 +187,38 @@ async def websocket_test(websocket: WebSocket): # $ requestHandler routedParamet | |
|
||
async for data in websocket.iter_json(): | ||
ensure_tainted(data) # $ tainted | ||
|
||
|
||
# --- Request --- | ||
|
||
import starlette.requests | ||
from fastapi import Request | ||
|
||
|
||
assert Request == starlette.requests.Request | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this assert tell the analysis (type tracking?) that these types are equal? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, it's purely for humans reading the test-file to highlight the fact that they are indeed the same class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (we just adjusted a copy of the websocket modeling, which did the same) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking question: If we accidentally dropped either fastapi or starlette from the union in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yep. that
If we wanted 100% test-coverage of our modeling, then yes. My personal opinion is that we're getting into the realm of diminishing returns of doing so, so I didn't bother. |
||
|
||
@app.websocket("/req") # $ routeSetup="/req" | ||
async def request_test(request: Request): # $ requestHandler routedParameter=request | ||
ensure_tainted( | ||
request, # $ tainted | ||
|
||
await request.body(), # $ tainted | ||
|
||
await request.json(), # $ tainted | ||
await request.json()["key"], # $ tainted | ||
|
||
# form() returns a FormData (which is a starlette ImmutableMultiDict) | ||
await request.form(), # $ tainted | ||
await request.form()["key"], # $ tainted | ||
await request.form().getlist("key"), # $ MISSING: tainted | ||
await request.form().getlist("key")[0], # $ MISSING: tainted | ||
# data in the form could be an starlette.datastructures.UploadFile | ||
await request.form()["file"].filename, # $ MISSING: tainted | ||
await request.form().getlist("file")[0].filename, # $ MISSING: tainted | ||
Comment on lines
+213
to
+217
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For my understanding: These are missing because we don't track flow from the result of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we don't have taint-steps to attribute reads by default 👍 We would need to model the specific attribute-flow we wanted for multidict and uploaded files, which we haven't done yet. |
||
|
||
request.cookies, # $ tainted | ||
request.cookies["key"], # $ tainted | ||
) | ||
|
||
async for chunk in request.stream(): | ||
ensure_tainted(chunk) # $ tainted |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For my understanding: This would also apply to
fastapi.Request
but you're just choosing one qualified name to represent the class right?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, this class is like the old string-based dataflow configurations, so it just needs a unique string to not have cross-talk.