Skip to content

Improve asset file retrieval#14383

Merged
etj merged 1 commit into
masterfrom
rvm8-wxp4
Jun 26, 2026
Merged

Improve asset file retrieval#14383
etj merged 1 commit into
masterfrom
rvm8-wxp4

Conversation

@etj

@etj etj commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Checklist

Reviewing is a process done by project maintainers, mostly on a volunteer basis. We try to keep the overhead as small as possible and appreciate if you help us to do so by completing the following items. Feel free to ask in a comment if you have troubles with any of them.

For all pull requests:

  • Confirm you have read the contribution guidelines
  • You have sent a Contribution Licence Agreement (CLA) as necessary (not required for small changes, e.g., fixing typos in the documentation)
  • Make sure the first PR targets the master branch, eventual backports will be managed later. This can be ignored if the PR is fixing an issue that only happens in a specific branch, but not in newer ones.

The following are required only for core and extension modules (they are welcomed, but not required, for contrib modules):

  • There is a ticket in https://github.com/GeoNode/geonode/issues describing the issue/improvement/feature (a notable exemption is, changes not visible to end-users)
  • The issue connected to the PR must have Labels and Milestone assigned
  • PR for bug fixes and small new features are presented as a single commit
  • PR title must be in the form "[Fixes #<issue_number>] Title of the PR"
  • New unit tests have been added covering the changes, unless there is an explanation on why the tests are not necessary/implemented

Submitting the PR does not require you to check all items, but by the time it gets merged, they should be either satisfied or inapplicable.

@etj etj requested a review from mattiagiupponi June 26, 2026 12:50
@etj etj self-assigned this Jun 26, 2026
@etj etj added the 5.1.x label Jun 26, 2026
@cla-bot cla-bot Bot added the cla-signed CLA Bot: community license agreement signed label Jun 26, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances path traversal protection when downloading assets by resolving paths with os.path.realpath and checking if the requested file is within the asset's directory, supported by a new unit test. It also simplifies admin authorization logic and adjusts logging levels. However, using startswith for path validation is vulnerable to partial directory name matching (e.g., /app/dir and /app/dir_fake). It is recommended to use os.path.commonpath to ensure robust path component comparison.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread geonode/assets/local.py
Comment on lines +280 to +284
localfile = os.path.realpath(localfile)
realassetpath = os.path.realpath(dir0)
if not localfile.startswith(realassetpath):
logger.error(f"Tentative path traversal for asset {asset.id} on path [{path}]")
return HttpResponse(f"File not found for asset {asset.id}", status=400)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Using startswith on resolved paths is vulnerable to path traversal if one directory name is a prefix of another (e.g., /app/dir and /app/dir_fake). Since startswith performs a simple string prefix check, /app/dir_fake would be considered inside /app/dir because /app/dir_fake.startswith(/app/dir) is True.

To prevent this, use os.path.commonpath which compares path components rather than raw strings.

Suggested change
localfile = os.path.realpath(localfile)
realassetpath = os.path.realpath(dir0)
if not localfile.startswith(realassetpath):
logger.error(f"Tentative path traversal for asset {asset.id} on path [{path}]")
return HttpResponse(f"File not found for asset {asset.id}", status=400)
localfile = os.path.realpath(localfile)
realassetpath = os.path.realpath(dir0)
if os.path.commonpath([realassetpath, localfile]) != realassetpath:
logger.error(f"Tentative path traversal for asset {asset.id} on path [{path}]")
return HttpResponse(f"File not found for asset {asset.id}", status=400)

@etj etj merged commit 51d19cc into master Jun 26, 2026
17 checks passed
@etj etj deleted the rvm8-wxp4 branch June 26, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport 5.0.x backport 5.1.x cla-signed CLA Bot: community license agreement signed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants