fix(security): reduce code duplication and fix security issues in OAuth and file handling

- Extract common OAuth token exchange logic to shared utility (oauth_helper.py)
- Remove sensitive data logging (client_secret, authorization codes)
- Add path traversal validation in resolve_file_path()
- Add input validation for rclone destination parameter
- Replace bare Exception catches with specific exception types
- Use RuntimeError instead of generic Exception for better error handling

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-08 08:26:10 +00:00
parent 6ec4e2b8a7
commit 551b23a80c
6 changed files with 240 additions and 257 deletions
+38 -8
View File
@@ -3,8 +3,9 @@ Common utilities for API routes
"""
import logging
import os
from pathlib import Path
from sqlalchemy.orm import Session
from fastapi import Depends
from fastapi import Depends, HTTPException, status
from app.database import SessionLocal
from app.config import settings
@@ -22,15 +23,44 @@ def get_db():
def resolve_file_path(file_path: str, subfolder: str = None) -> str:
"""
Resolves a file path to an absolute path.
Resolves a file path to an absolute path with path traversal protection.
If the path is not absolute, it will be joined with the workdir path.
Optionally, can include a subfolder like 'processed'.
Returns the absolute file path.
Security: Validates that the resolved path stays within the workdir
to prevent path traversal attacks (e.g., ../../etc/passwd).
Args:
file_path: The file path to resolve
subfolder: Optional subfolder within workdir
Returns:
The validated absolute file path
Raises:
HTTPException: If the path attempts to escape the workdir
"""
# Build the base directory
if subfolder:
base_dir = Path(settings.workdir) / subfolder
else:
base_dir = Path(settings.workdir)
# Resolve the file path
if not os.path.isabs(file_path):
if subfolder:
file_path = os.path.join(settings.workdir, subfolder, file_path)
else:
file_path = os.path.join(settings.workdir, file_path)
return file_path
resolved_path = (base_dir / file_path).resolve()
else:
resolved_path = Path(file_path).resolve()
# Ensure the resolved path is within the base directory (path traversal protection)
try:
resolved_path.relative_to(base_dir.resolve())
except ValueError:
# Path is outside the base directory - potential path traversal attack
logger.warning(f"Path traversal attempt detected: {file_path} -> {resolved_path}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid file path: path traversal not allowed"
)
return str(resolved_path)