Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

security: arbitrary file download by authenticated user #2867

Merged
merged 2 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion mealie/routes/recipe/bulk_actions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from functools import cached_property
from pathlib import Path

from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException

from mealie.core.dependencies.dependencies import temporary_zip_path
from mealie.core.security import create_file_token
Expand Down Expand Up @@ -50,6 +50,10 @@ def bulk_export_recipes(self, export_recipes: ExportRecipes, temp_path=Depends(t
@router.get("/export/download")
def get_exported_data_token(self, path: Path):
"""Returns a token to download a file"""
path = Path(path).resolve()

if not path.is_relative_to(self.folders.DATA_DIR):
raise HTTPException(400, "path must be relative to data directory")

return {"fileToken": create_file_token(path)}

Expand Down
9 changes: 9 additions & 0 deletions mealie/routes/utility_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from fastapi import APIRouter, Depends, HTTPException, status
from starlette.responses import FileResponse

from mealie.core.config import get_app_dirs
from mealie.core.dependencies import validate_file_token

router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
Expand All @@ -12,6 +13,14 @@
async def download_file(file_path: Path = Depends(validate_file_token)):
"""Uses a file token obtained by an active user to retrieve a file from the operating
system."""

file_path = Path(file_path).resolve()

dirs = get_app_dirs()

if not file_path.is_relative_to(dirs.DATA_DIR):
raise HTTPException(status.HTTP_400_BAD_REQUEST)

if not file_path.is_file():
raise HTTPException(status.HTTP_400_BAD_REQUEST)

Expand Down
Loading