Conversation
…lueprint, unused imports Co-authored-by: amitdevx <110670491+amitdevx@users.noreply.github.com>
…dd SECRET_KEY warning Co-authored-by: amitdevx <110670491+amitdevx@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses security vulnerabilities in archive extraction, adds missing blueprint registration, and cleans up various code quality issues. The changes focus on preventing path traversal attacks, improving input validation, and fixing HTML/JavaScript issues.
Changes:
- Added path traversal protection to compression service for zip, tar, and 7z extraction
- Implemented input validation and sanitization for file renaming operations
- Registered missing compression_bp blueprint in app.py
- Fixed shell scripts to use dynamic SCRIPT_DIR instead of hardcoded paths
- Removed duplicate READMED.md file and empty auth.js file
- Added missing aria attributes and removed stray form tags in dashboard.html
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| FileFlow/backend/services/compression_service.py | Added security validation methods to prevent path traversal in archive extraction |
| FileFlow/backend/api/files.py | Added filename validation and sanitization to rename_file endpoint |
| FileFlow/backend/config.py | Updated SECRET_KEY to read from environment variable with warning for fallback |
| FileFlow/backend/app.py | Registered compression_bp blueprint and removed unused datetime import |
| FileFlow/frontend/js/file_management.js | Moved file from incorrect nested directory to proper location |
| FileFlow/frontend/templates/dashboard.html | Removed stray closing form tags and added aria attributes for accessibility |
| FileFlow/start.sh | Updated to use dynamic SCRIPT_DIR instead of hardcoded workspace path |
| start.sh | Updated to use dynamic SCRIPT_DIR for better portability |
| READMED.md | Removed duplicate file with typo in name |
| FileFlow/frontend/js/app.js/auth.js | Removed empty file |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for member in zipf.namelist(): | ||
| CompressionService._safe_extract_member(archive_path, member, extract_to) | ||
| zipf.extractall(extract_to) | ||
|
|
||
| @staticmethod | ||
| def extract_tar(archive_path, extract_to): | ||
| with tarfile.open(archive_path, 'r:*') as tarf: | ||
| tarf.extractall(extract_to) | ||
| # Validate all members before extraction to prevent path traversal | ||
| for member in tarf.getmembers(): | ||
| CompressionService._safe_extract_member(archive_path, member.name, extract_to) | ||
| # Use data filter for Python 3.11.4+ or validate manually for earlier versions | ||
| try: | ||
| tarf.extractall(extract_to, filter='data') | ||
| except TypeError: | ||
| # Python < 3.11.4 doesn't support filter parameter, but we already validated | ||
| tarf.extractall(extract_to) | ||
|
|
||
| @staticmethod | ||
| def extract_7z(archive_path, extract_to, password=None): | ||
| with py7zr.SevenZipFile(archive_path, 'r', password=password) as szf: | ||
| # Validate all members before extraction | ||
| for member in szf.getnames(): | ||
| CompressionService._safe_extract_member(archive_path, member, extract_to) |
There was a problem hiding this comment.
The calls to _safe_extract_member pass archive_path as the first parameter, but this parameter is unused in the function. All three extraction methods (extract_zip, extract_tar, extract_7z) should be updated to remove this unnecessary parameter.
| Note: This validates the path string without resolving symlinks to prevent | ||
| symlink-based attacks. The check uses string comparison after normalization. | ||
| """ | ||
| try: | ||
| base = Path(base_path).resolve() | ||
| # Normalize target without fully resolving to prevent symlink attacks | ||
| # Join base with target and check if it stays within base |
There was a problem hiding this comment.
The comment states "Normalize target without fully resolving to prevent symlink attacks" but the code then calls .resolve() on the full path on line 18, which does resolve symlinks. This creates a contradiction between the comment and the implementation. Either the comment should be updated to reflect that symlinks are resolved, or the implementation should be changed to avoid resolving symlinks (though resolving is actually the safer approach).
| Note: This validates the path string without resolving symlinks to prevent | |
| symlink-based attacks. The check uses string comparison after normalization. | |
| """ | |
| try: | |
| base = Path(base_path).resolve() | |
| # Normalize target without fully resolving to prevent symlink attacks | |
| # Join base with target and check if it stays within base | |
| Note: This resolves both the base path and the combined target path to | |
| absolute, symlink-resolved paths and then verifies the target remains | |
| within the base directory. | |
| """ | |
| try: | |
| base = Path(base_path).resolve() | |
| # Resolve the combined path and ensure it stays within the resolved base |
| SECRET_KEY = os.environ.get('SECRET_KEY') | ||
| if not SECRET_KEY: | ||
| warnings.warn( | ||
| "SECRET_KEY not set in environment. Using random key. " | ||
| "Sessions will be invalidated on restart. " | ||
| "Set SECRET_KEY environment variable for production use.", | ||
| RuntimeWarning | ||
| ) | ||
| SECRET_KEY = os.urandom(24) |
There was a problem hiding this comment.
The SECRET_KEY is regenerated on every restart when not set via environment variable, which will invalidate all existing user sessions. While the warning message mentions this, the implementation stores the random key in a class variable that persists for the app lifetime but is lost on restart. Consider using a persistent fallback (like storing in a file) for development environments, or make the warning more prominent to ensure production deployments always set the environment variable.
| # Join base with target and check if it stays within base | ||
| full_path = (base / target_path).resolve() | ||
| return full_path.is_relative_to(base) | ||
| except (ValueError, RuntimeError): |
There was a problem hiding this comment.
The exception handler on line 20 catches ValueError and RuntimeError but not AttributeError, which will be raised on Python versions before 3.9 when calling is_relative_to(). This method was only added in Python 3.9. The exception tuple should include AttributeError to handle older Python versions gracefully: except (ValueError, RuntimeError, AttributeError):
| except (ValueError, RuntimeError): | |
| except (ValueError, RuntimeError, AttributeError): |
| return False | ||
|
|
||
| @staticmethod | ||
| def _safe_extract_member(archive_path, member_name, extract_to): |
There was a problem hiding this comment.
The first parameter archive_path is not used in the function body. This parameter should be removed from the function signature as it serves no purpose in the validation logic.
| # Normalize target without fully resolving to prevent symlink attacks | ||
| # Join base with target and check if it stays within base | ||
| full_path = (base / target_path).resolve() | ||
| return full_path.is_relative_to(base) |
There was a problem hiding this comment.
The is_relative_to() method was added in Python 3.9. For compatibility with earlier Python versions, this will raise an AttributeError. Consider adding a compatibility check or documenting the minimum Python version requirement in requirements.txt or README.
| return full_path.is_relative_to(base) | |
| try: | |
| # If full_path is not within base, this will raise ValueError | |
| full_path.relative_to(base) | |
| return True | |
| except ValueError: | |
| return False |
Repository audit identified security vulnerabilities in archive extraction, missing blueprint registration, and various code quality issues.
Security Fixes
zip,tar,7z) now validates all member paths before extraction..referencestarfile.extractall(filter='data')on Python 3.11.4+Validators.is_valid_filename()andsanitize_filename()RuntimeWarningwhen falling back to random keyBug Fixes
compression_bpblueprint inapp.pydatetimeimport</form>tags indashboard.htmlfile_management.jslocation (was nested injs/app.js/directory)Cleanup
auth.jsfileREADMED.md(typo)start.shscripts withSCRIPT_DIRaria-pressedattribute on grid-view buttonOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.