Opened 12 years ago
Closed 11 years ago
#21668 closed Cleanup/optimization (fixed)
Invalid upload_to FileField attribute results in hard-to-debug "Bad Request" 400 error.
| Reported by: | Sam Thompson | Owned by: | ANUBHAV JOSHI | 
|---|---|---|---|
| Component: | Documentation | Version: | dev | 
| Severity: | Normal | Keywords: | |
| Cc: | Tim Graham | Triage Stage: | Accepted | 
| Has patch: | no | Needs documentation: | no | 
| Needs tests: | no | Patch needs improvement: | no | 
| Easy pickings: | no | UI/UX: | no | 
Description
It's admirable that django tries to prevent files from being uploaded outside of MEDIA_ROOT.  However, the documentation for file uploads (https://docs.djangoproject.com/en/dev/topics/http/file-uploads/) doesn't make this requirement clear, and getting it wrong results in several layers of useful error messages getting eaten by a generic message instead.
It starts with django.utils._os.safe_join raising a ValueError if the upload_to path isn't within MEDIA_ROOT.  This has a useful message payload showing both paths and why the exception happened.  This message isn't logged.
However, django.core.files.storage.FileSystemStorage.path immediately catches that exception, eats the message, and raises a much more generic SuspiciousFileOperation, containing the desired path but no explanation of what went wrong.
Finally, django.core.handlers.base.BaseHandler.get_response catches that SuspiciousOperation, logs the message (good if you have logging turned on, which many users do not), eats the exception and returns a 400 response instead.  
All the user sees is "400 Bad Request", no traceback and certainly not the original and useful ValueError message.
Attachments (1)
Change History (12)
comment:1 by , 12 years ago
| Summary: | Invalid upload_to attribute results in hard-to-debug "Bad Request" 400 error. → Invalid upload_to FileField attribute results in hard-to-debug "Bad Request" 400 error. | 
|---|
comment:2 by , 12 years ago
comment:3 by , 12 years ago
| Cc: | added | 
|---|---|
| Component: | Uncategorized → HTTP handling | 
| Type: | Bug → Cleanup/optimization | 
There may be some room for improvement here. How/why did you manage to attempt to upload a file outside of MEDIA_ROOT?
comment:4 by , 12 years ago
I was attempting to simulate the upload as part of a unit test.  As I didn't want the test polluting my actual uploads directory, I was attempting to use @override_settings to change the upload path.  The resulting error was very hard to diagnose.
comment:5 by , 12 years ago
| Component: | HTTP handling → Documentation | 
|---|---|
| Triage Stage: | Unreviewed → Accepted | 
I think improving the documentation is the best way forward.
comment:6 by , 11 years ago
| Owner: | changed from to | 
|---|---|
| Status: | new → assigned | 
Working on this in my GSoC project.
comment:7 by , 11 years ago
Actually, it appears that this raised a more informative error message in Django 1.3 (see #22706). We should see if that's possible to restore.
comment:8 by , 11 years ago
| Version: | 1.6 → master | 
|---|
Since this appears to be a problem in not getting the correct message, I am going to fix this alongwith #22058.
comment:9 by , 11 years ago
This regressed in 1.6 with this commit: https://github.com/django/django/commit/d228c1192ed59ab0114d9eba82ac99df611652d2#diff-dbd7d9159676b15fc9a096b0adb919e9R173
I think we should consider returning a technical_500_response in the handling of SuspiciousOperation as we did before if settings.DEBUG is True rather than invoking handler400.
by , 11 years ago
| Attachment: | 21668.diff added | 
|---|
comment:10 by , 11 years ago
Yes, that's the idea. Please add a test. A mention in the release notes also wouldn't hurt. I'd also consider adding a status_code kwarg to technical_500_response so we can continue returning a 400 in this case.
comment:11 by , 11 years ago
| Resolution: | → fixed | 
|---|---|
| Status: | assigned → closed | 
Potentially related #19866