""" ID validators for path-building in the scene pipeline. Lives in its own leaf module so the validators can be imported (and tested) without dragging in the rest of the pipeline (which pulls in ffmpeg helpers, imagehash, transformers, etc.). """ from __future__ import annotations import re # JW.org natural keys are letters/digits/underscore/hyphen/period; language # codes are letters only. Anything outside these regexes is rejected before # os.path.join sees it, blocking traversal via ``..`` or absolute paths. # # The ID pattern additionally requires the first character to be alphanumeric # or underscore so a sneaky ``..`` or ``-foo`` is rejected even though dot # and hyphen are allowed mid-identifier. _SAFE_ID_PATTERN = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$") _SAFE_LANG_PATTERN = re.compile(r"^[A-Za-z]{1,8}$") def require_safe_id(name: str, value: str) -> str: if ( not isinstance(value, str) or not _SAFE_ID_PATTERN.match(value) or ".." in value ): raise ValueError( f"Refusing unsafe {name}={value!r}: must match {_SAFE_ID_PATTERN.pattern} " f"and contain no '..'" ) return value def require_safe_language(value: str) -> str: if not isinstance(value, str) or not _SAFE_LANG_PATTERN.match(value): raise ValueError( f"Refusing unsafe language={value!r}: must match {_SAFE_LANG_PATTERN.pattern}" ) return value