TASK-016: Validate delete_log_path query parameter with allowlist

- Extract path validation logic into shared helper function in
  backend/app/utils/path_utils.py (validate_log_path)
- Refactor AddLogPathRequest to use the helper function
- Apply the same validation to DELETE /api/config/jails/{name}/logpath
  endpoint by validating the log_path query parameter
- Return HTTP 422 with descriptive error if validation fails
- Add comprehensive unit tests for path validation
- Update Backend-Development.md with usage examples

This prevents path-traversal attacks on the delete_log_path endpoint
by ensuring all log paths are within allowlisted directories.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-26 14:04:21 +02:00
parent d66493f135
commit 94bdabe622
7 changed files with 236 additions and 67 deletions

View File

@@ -10,6 +10,7 @@ from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator
from app.config import get_settings
from app.utils.path_utils import validate_log_path
DNSMode = Literal["yes", "warn", "no", "raw"]
LogEncoding = Literal["auto", "ascii", "utf-8", "UTF-8", "latin-1"]
@@ -295,12 +296,9 @@ class AddLogPathRequest(BaseModel):
@field_validator("log_path", mode="after")
@classmethod
def validate_log_path(cls, value: str) -> str:
def validate_log_path_field(cls, value: str) -> str:
"""Validate that the log path is within allowed directories.
Resolves the path to its canonical form (resolving symlinks) and checks
that it is relative to one of the allowed log directories from settings.
Args:
value: The log path to validate.
@@ -310,24 +308,7 @@ class AddLogPathRequest(BaseModel):
Raises:
ValueError: If the path is outside allowed log directories.
"""
settings = get_settings()
try:
resolved_path = Path(value).resolve()
except (OSError, RuntimeError) as e:
raise ValueError(f"Cannot resolve path {value!r}: {e}") from e
for allowed_dir in settings.allowed_log_dirs:
allowed_path = Path(allowed_dir).resolve()
try:
resolved_path.relative_to(allowed_path)
return value
except ValueError:
continue
allowed_dirs_str = ", ".join(settings.allowed_log_dirs)
raise ValueError(
f"Log path {value!r} is outside allowed directories: {allowed_dirs_str}"
)
return validate_log_path(value)