-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Expected Behavior
When uploading datasets, thumbnails should be uploaded, processed and later shown correctly.
Actual Behavior
The Thumbnail cannot be created, which leads to it not being shown/rendered later on.
Steps to Reproduce the Problem
- (Optional) use relative paths for STATIC ROOT etc.
- Create/Upload a dataset
- See if a thumbnail is present
Specifications
- GeoNode version: 4.4.3 (also master)
- Installation type (vanilla, geonode-project): geonode-project
- Installation method (manual, docker): docker
- Platform: Azure Container Apps
- Additional details: Stacktrace, Links, Related Tickets/Issues
Example Stack Trace taken from Celery
Detected path traversal attempt in '/usr/src/odp/volumes/statics/uploaded/thumbs/dataset-92530cb7-36a2-4c4e-be54-73d6292d292c-thumb-185ffc02-3e51-4b53-9ddc-3dde178a1309.jpg'
Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/geonode/base/models.py", line 1570, in save_thumbnail
storage_manager.save(storage_manager.path(upload_path), img)
File "/usr/local/lib/python3.10/dist-packages/geonode/storage/manager.py", line 161, in save
return self._concrete_storage_manager.save(name, content, max_length=max_length)
File "/usr/local/lib/python3.10/dist-packages/geonode/storage/manager.py", line 300, in save
return self._fsm.save(name, content, max_length=max_length)
File "/usr/local/lib/python3.10/dist-packages/django/core/files/storage/base.py", line 41, in save
validate_file_name(name, allow_relative_path=True)
File "/usr/local/lib/python3.10/dist-packages/django/core/files/utils.py", line 17, in validate_file_name
raise SuspiciousFileOperation(
[2025-05-27 12:35:54,568: ERROR/ForkPoolWorker-230] Detected path traversal attempt in '/usr/src/odp/volumes/statics/uploaded/thumbs/dataset-92530cb7-36a2-4c4e-be54-73d6292d292c-thumb-185ffc02-3e51-4b53-9ddc-3dde178a1309.jpg'
Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/geonode/base/models.py", line 1570, in save_thumbnail
storage_manager.save(storage_manager.path(upload_path), img)
File "/usr/local/lib/python3.10/dist-packages/geonode/storage/manager.py", line 161, in save
return self._concrete_storage_manager.save(name, content, max_length=max_length)
File "/usr/local/lib/python3.10/dist-packages/geonode/storage/manager.py", line 300, in save
return self._fsm.save(name, content, max_length=max_length)
File "/usr/local/lib/python3.10/dist-packages/django/core/files/storage/base.py", line 41, in save
validate_file_name(name, allow_relative_path=True)
django.core.exceptions.SuspiciousFileOperation: Detected path traversal attempt in '/usr/src/odp/volumes/statics/uploaded/thumbs/dataset-92530cb7-36a2-4c4e-be54-73d6292d292c-thumb-185ffc02-3e51-4b53-9ddc-3dde178a1309.jpg'
File "/usr/local/lib/python3.10/dist-packages/django/core/files/utils.py", line 17, in validate_file_name
raise SuspiciousFileOperation(
django.core.exceptions.SuspiciousFileOperation: Detected path traversal attempt in '/usr/src/odp/volumes/statics/uploaded/thumbs/dataset-92530cb7-36a2-4c4e-be54-73d6292d292c-thumb-185ffc02-3e51-4b53-9ddc-3dde178a1309.jpg'
Relevant code fragments
geonode/geonode/base/models.py
Line 1564 in 03a6578
storage_manager.save(storage_manager.path(upload_path), img) |
geonode/geonode/storage/manager.py
Line 161 in 03a6578
return self._concrete_storage_manager.save(name, content, max_length=max_length) |
geonode/geonode/storage/manager.py
Line 300 in 03a6578
return self._fsm.save(name, content, max_length=max_length) |
https://github.com/django/django/blob/ad28db666e0884b658f5e240c3d7ee1362688ba1/django/core/files/storage/base.py#L41
https://github.com/django/django/blob/ad28db666e0884b658f5e240c3d7ee1362688ba1/django/core/files/utils.py#L18
First Analysis on possible causes
Exception is thrown in the Django Package here: https://github.com/django/django/blob/ad28db666e0884b658f5e240c3d7ee1362688ba1/django/core/files/utils.py#L18 because it gets passed an absolute path.
To fix it, we moved to using relative paths for STATIC_ROOT, MEDIA_ROOT etc.:
STATIC_ROOT=volumes/statics/static/
MEDIA_ROOT=volumes/statics/uploaded/
and mounted to the relative path in the working directory. (Works fine)
However, we still get some of those exceptions.
Could it be related to the path being converted to an absolute one during processing around here?:
geonode/geonode/base/models.py
Lines 1546 to 1564 in 03a6578
if image: | |
actual_name = storage_manager.save(upload_path, ContentFile(image)) | |
actual_file_name = os.path.basename(actual_name) | |
if filename != actual_file_name: | |
upload_path = upload_path.replace(filename, actual_file_name) | |
url = storage_manager.url(upload_path) | |
try: | |
# Optimize the Thumbnail size and resolution | |
im = Image.open(storage_manager.open(actual_name)) | |
im = thumbnail_algorithm(im, **kwargs) | |
# Saving the thumb into a temporary directory on file system | |
tmp_location = os.path.abspath(f"{settings.MEDIA_ROOT}/{upload_path}") | |
im.save(tmp_location, quality="high") | |
with open(tmp_location, "rb+") as img: | |
# Saving the img via storage manager | |
storage_manager.save(storage_manager.path(upload_path), img) |