🛡️ Sentinel: [CRITICAL] Fix path traversal and validation bypass#100
🛡️ Sentinel: [CRITICAL] Fix path traversal and validation bypass#100socialawy-dev wants to merge 1 commit into
Conversation
Co-authored-by: socialawy <24765060+socialawy@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
|
||
| return False | ||
| except (ValueError, RuntimeError, OSError): | ||
| resolved_root = root.resolve() |
| return False | ||
| except (ValueError, RuntimeError, OSError): | ||
| resolved_root = root.resolve() | ||
| resolved_path = path.resolve() |
There was a problem hiding this comment.
Code Review
This pull request enhances security by documenting path traversal vulnerabilities, adding a null check to the static file response handler, and refactoring path validation to use more robust pathlib methods. Feedback identifies a regression in the path validation utility regarding string input handling and points out an invalid version specifier for the ruff dependency in the lock file.
| resolved_root = root.resolve() | ||
| resolved_path = path.resolve() | ||
| return resolved_path.is_relative_to(resolved_root) | ||
| except (TypeError, ValueError, RuntimeError, AttributeError, OSError): |
There was a problem hiding this comment.
The updated implementation of validate_path_within introduces a regression for callers passing path strings instead of Path objects. While the type hints specify Path, the previous implementation used os.path.abspath(str(path)), which supported both types. In the current version, passing a string will cause root.resolve() to raise an AttributeError, which is caught by the except block, resulting in a False return value even for valid paths.
Casting the inputs to Path ensures robustness and maintains backward compatibility with string-based calls.
| resolved_root = root.resolve() | |
| resolved_path = path.resolve() | |
| return resolved_path.is_relative_to(resolved_root) | |
| except (TypeError, ValueError, RuntimeError, AttributeError, OSError): | |
| resolved_root = Path(root).resolve() | |
| resolved_path = Path(path).resolve() | |
| return resolved_path.is_relative_to(resolved_root) | |
| except (TypeError, ValueError, RuntimeError, AttributeError, OSError): |
| { name = "python-dotenv", marker = "extra == 'cloud'", specifier = ">=1.0,<2" }, | ||
| { name = "python-multipart", marker = "extra == 'server'", specifier = ">=0.0.27,<1" }, | ||
| { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.9" }, | ||
| { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.1" }, |
There was a problem hiding this comment.
🚨 Severity: CRITICAL
💡 Vulnerability: A path traversal logic bypass existed in
validate_path_withinwhich used a string-basedstartswithcheck onos.path.abspathbefore resolving symlinks, allowing bypasses in specific edge cases. Also,SafeStaticFiles.get_responseinsrc/audioformation/server/app.pylacked aNonecheck for paths.🎯 Impact: Attackers could potentially bypass validation rules in specific configurations where paths match as a prefix string but aren't actually contained.
🔧 Fix: Replaced
startswithlogic withPath.resolve().is_relative_to()to strictly validate path boundaries using thePathliblibrary, ensuring canonical resolution of all symlinks.✅ Verification: Ran
pytestsuite ensuring all tests pass, including the security and unit tests.PR created automatically by Jules for task 16345357079722916730 started by @socialawy