Coverage for src / marimushka / validators.py: 100%
34 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-28 17:41 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-28 17:41 +0000
1"""Input validation for paths, templates, and configuration.
3This module provides validation functions for template files and other inputs
4used in the export process. It ensures security by checking for path traversal,
5file existence, and size limits.
6"""
8import stat
9from pathlib import Path
11from loguru import logger
13from .audit import AuditLogger
14from .exceptions import TemplateInvalidError, TemplateNotFoundError
15from .security import sanitize_error_message, validate_file_size, validate_path_traversal
18def validate_template(template_path: Path, audit_logger: AuditLogger) -> None:
19 """Validate the template file exists and has correct extension.
21 Args:
22 template_path: Path to the template file.
23 audit_logger: Logger for audit events.
25 Raises:
26 TemplateNotFoundError: If the template file does not exist.
27 TemplateInvalidError: If the template path is not a file.
29 """
30 # Validate path traversal
31 try:
32 validate_path_traversal(template_path)
33 audit_logger.log_path_validation(template_path, "traversal", True)
34 except ValueError as e:
35 audit_logger.log_path_validation(template_path, "traversal", False, str(e))
36 sanitized_msg = sanitize_error_message(str(e))
37 raise TemplateInvalidError(template_path, reason=f"path traversal detected: {sanitized_msg}") from e
39 # Check existence (avoid TOCTOU by using stat)
40 try:
41 stat_result = template_path.stat()
42 except FileNotFoundError:
43 audit_logger.log_path_validation(template_path, "existence", False, "file not found")
44 raise TemplateNotFoundError(template_path) from None
45 except OSError as e:
46 audit_logger.log_path_validation(template_path, "existence", False, str(e))
47 raise TemplateInvalidError(template_path, reason=f"cannot access file: {e}") from e
49 # Check if it's a regular file
50 if not stat.S_ISREG(stat_result.st_mode):
51 audit_logger.log_path_validation(template_path, "file_type", False, "not a regular file")
52 raise TemplateInvalidError(template_path, reason="path is not a file")
54 # Check file size to prevent DoS
55 try:
56 validate_file_size(template_path, max_size_bytes=10 * 1024 * 1024) # 10MB limit
57 except ValueError as e:
58 audit_logger.log_path_validation(template_path, "size", False, str(e))
59 sanitized_msg = sanitize_error_message(str(e))
60 raise TemplateInvalidError(template_path, reason=f"file size limit exceeded: {sanitized_msg}") from e
62 if template_path.suffix not in (".j2", ".jinja2"):
63 logger.warning(f"Template file '{template_path}' does not have .j2 or .jinja2 extension")
65 audit_logger.log_path_validation(template_path, "complete", True)