-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Python: support os.path.basename as a sanitizer in py/path-injection
#21719
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
base: main
Are you sure you want to change the base?
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 | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -150,3 +150,20 @@ def safe_set_of_files(): | |||||||||||||||||||||||||||||||||||||||||||||||||
| if filename in SAFE_FILES: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| path = os.path.join(STATIC_DIR, filename) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| f = open(path) # $ SPURIOUS: Alert | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| @app.route("/basename-sanitizer") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| def basename_sanitizer(): | ||||||||||||||||||||||||||||||||||||||||||||||||||
| filename = request.args.get('filename', '') | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Secure mitigation pattern: os.path.basename strips all directory components, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # preventing path traversal attacks. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| path = os.path.join(STATIC_DIR, os.path.basename(filename)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| f = open(path) # $ result=OK | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| @app.route("/basename-no-join") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| def basename_no_join(): | ||||||||||||||||||||||||||||||||||||||||||||||||||
| filename = request.args.get('filename', '') | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # basename alone also prevents directory traversal | ||||||||||||||||||||||||||||||||||||||||||||||||||
| path = os.path.basename(filename) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| f = open(path) # $ result=OK | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+158
to
+169
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Secure mitigation pattern: os.path.basename strips all directory components, | |
| # preventing path traversal attacks. | |
| path = os.path.join(STATIC_DIR, os.path.basename(filename)) | |
| f = open(path) # $ result=OK | |
| @app.route("/basename-no-join") | |
| def basename_no_join(): | |
| filename = request.args.get('filename', '') | |
| # basename alone also prevents directory traversal | |
| path = os.path.basename(filename) | |
| f = open(path) # $ result=OK | |
| # `os.path.basename` alone is not a complete mitigation because it can return | |
| # `.` or `..`, which may still escape `STATIC_DIR` after the join is resolved. | |
| path = os.path.join(STATIC_DIR, os.path.basename(filename)) | |
| f = open(path) # $ Alert | |
| @app.route("/basename-no-join") | |
| def basename_no_join(): | |
| filename = request.args.get('filename', '') | |
| # `os.path.basename` alone does not make user-controlled paths safe. | |
| path = os.path.basename(filename) | |
| f = open(path) # $ Alert |
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.
This doc comment overstates the guarantees of
os.path.basename: it does strip leading directory components, but the result can still be a relative path component such as".."or".", so it does not inherently prevent traversal when the value is later joined/resolved. Please adjust the wording to avoid claiming it cannot contain relative path components, and (if it remains a sanitizer) document the limitation/assumptions explicitly.