{"record":{"id":"387293534ea8947d","repo":"unslothai/unsloth","slug":"path-may-not-contain-segments-raw-r","errorCode":null,"errorMessage":"path may not contain '..' segments: {raw!r}","messagePattern":"path may not contain '\\.\\.' segments: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/utils/paths/storage_roots.py","lineNumber":413,"sourceCode":"    *,\n    root: Path,\n    strip_prefixes: tuple[str, ...] = (),\n) -> Path:\n    \"\"\"Resolve ``path_value`` and assert the result is under ``root``.\n\n    Absolutes are accepted only if already contained (so pre-resolved\n    internal paths re-enter idempotently); schemas reject absolutes upstream.\n    \"\"\"\n    if not path_value or not str(path_value).strip():\n        return root\n\n    raw = str(path_value).strip()\n    if \"\\x00\" in raw:\n        raise ValueError(\"path may not contain null bytes\")\n\n    path = Path(raw).expanduser()\n    if _has_parent_segment(raw, path):\n        raise ValueError(f\"path may not contain '..' segments: {raw!r}\")\n\n    if _is_absolute_user_path(path):\n        _assert_contained(path, root)\n        return path\n\n    cleaned = _clean_relative_path(raw, strip_prefixes = strip_prefixes)\n    candidate = root / cleaned\n    _assert_contained(candidate, root)\n    return candidate\n\n\ndef default_run_dir_name(model_name: str) -> str:\n    # Folder-safe run name for an auto-created output dir. Repo ids keep their\n    # namespace (org/model -> org_model); local paths (incl. G:\\dir\\model)\n    # collapse to their final component so an absolute source can't escape\n    # outputs_root. Length-capped to stay under the filesystem name limit.\n    raw = str(model_name or \"\").strip()\n    is_path = (","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/paths/storage_roots.py#L395-L431","documentation":"Raised by resolve_under_root in storage_roots.py:412-414 when the path contains a '..' parent segment (checked textually on the raw string and via the expanded Path). This blocks trivial traversal like '../../etc/passwd' before any root joining; on Windows both '..' and '..'-style backslash forms are caught by the segment check.","triggerScenarios":"resolve_under_root(\"../../etc/passwd\", root=...) or \"models/../..\" — any '..' segment in the stripped raw string or the expandeduser path. Note the check is on segments, so a filename literally containing '..' as a whole segment (\"..\") triggers, while '..hidden' does not.","commonSituations":"Clients sending relative paths built by naive joining that walk up; security probing; user-typed paths in an export/save dialog containing '..'; reusing external relative paths (e.g. from a zip or git) that assume a different working root.","solutions":["Normalize client-side first: send a clean relative path without traversal segments","If you must accept user trees, use pathlib's Path.parts to filter out '..' before calling","Pass absolute paths only if they are already known to be contained (the resolver accepts contained absolutes idempotently)","Catch ValueError and map it to a validation error message for the user"],"exampleFix":"# before\nresolve_under_root(\"../../secret.txt\", root=root)\n\n# after\nfrom pathlib import PurePosixPath\nrel = PurePosixPath(user_path)\nif '..' in rel.parts:\n    raise HTTPException(400, 'relative paths may not contain ..')\nresolve_under_root(str(rel), root=root)","handlingStrategy":"validation","validationCode":"from pathlib import PurePath\n\ndef has_parent_segment(path_str: str) -> bool:\n    return \"..\" in PurePath(path_str.strip()).parts or \"..\" in path_str.split(\"/\")","typeGuard":"def is_traversal_free_path(v: object) -> bool:\n    if not isinstance(v, str):\n        return False\n    return \"..\" not in PurePath(v.strip()).parts","tryCatchPattern":"try:\n    path = resolve_under_root(value, root=root)\nexcept ValueError as exc:\n    if \"'..' segments\" in str(exc):\n        raise HTTPException(400, \"relative paths may not contain ..\") from exc\n    raise","preventionTips":["Filter '..' out of Path.parts on untrusted paths before submission","Send canonical relative paths (no up-walking) from clients","Reject traversal at request-schema validation, before filesystem code"],"tags":["security","path-traversal","validation"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}