{"record":{"id":"65e161f78869b125","repo":"odysseus-dev/odysseus","slug":"unsafe-gallery-filename","errorCode":null,"errorMessage":"Unsafe gallery filename","messagePattern":"Unsafe gallery filename","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/gallery/gallery_routes.py","lineNumber":235,"sourceCode":"    except Exception:\n        return False\n\n\ndef _sanitize_gallery_filename(filename: str) -> str:\n    \"\"\"Return a local filename safe to join under generated_images.\"\"\"\n    safe_name = re.sub(r\"[^A-Za-z0-9._-]\", \"_\", Path(str(filename or \"\")).name)[:128]\n    if not safe_name or safe_name in {\".\", \"..\"}:\n        safe_name = uuid.uuid4().hex[:12]\n    return safe_name\n\n\nGALLERY_IMAGE_DIR = Path(GENERATED_IMAGES_DIR)\n\n\ndef _gallery_image_path(filename: str) -> Path:\n    \"\"\"Resolve a stored gallery filename without leaving generated_images.\"\"\"\n    if not isinstance(filename, str):\n        raise HTTPException(400, \"Unsafe gallery filename\")\n    safe_name = _sanitize_gallery_filename(filename)\n    original = str(filename or \"\")\n    root = GALLERY_IMAGE_DIR.resolve()\n    path = (GALLERY_IMAGE_DIR / safe_name).resolve()\n    try:\n        if os.path.commonpath([str(root), str(path)]) != str(root):\n            raise ValueError\n    except Exception:\n        raise HTTPException(400, \"Unsafe gallery filename\")\n    if safe_name != original:\n        raise HTTPException(400, \"Unsafe gallery filename\")\n    return path\n\n\ndef _normalize_image_endpoint_base(url: str) -> str:\n    base = (url or \"\").strip().rstrip(\"/\")\n    if base.endswith(\"/v1\"):\n        base = base[:-3].rstrip(\"/\")","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/gallery/gallery_routes.py#L217-L253","documentation":"Error \"Unsafe gallery filename\" thrown in odysseus-dev/odysseus.","triggerScenarios":"Triggered when the corresponding server-side validation or runtime check at the recorded location rejects the request or operation and returns this error message to the caller.","commonSituations":"See trigger scenarios.","solutions":["Use a plain filename without path separators or traversal segments.","Rename the file to letters, numbers, dashes, and underscores only."],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}