{"record":{"id":"2bfe44bfd344dbe9","repo":"unslothai/unsloth","slug":"path-may-not-contain-null-bytes","errorCode":null,"errorMessage":"path may not contain null bytes","messagePattern":"path may not contain null bytes","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/utils/paths/storage_roots.py","lineNumber":409,"sourceCode":"\n\ndef resolve_under_root(\n    path_value: str | None,\n    *,\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)","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/paths/storage_roots.py#L391-L427","documentation":"Raised by resolve_under_root in storage_roots.py:409-410 when the user-supplied path string contains a NUL byte (\\x00). NUL cannot appear in valid filesystem paths on POSIX or Windows and is a common marker of injection attempts or corrupted binary data, so it is rejected before any Path construction.","triggerScenarios":"resolve_under_root(\"file.bin\\x00.txt\") — any NUL in the raw string after strip(). Typical sources: binary data decoded as text, crafted API payloads, or truncated buffers leaking NULs into path fields.","commonSituations":"Fuzzing/security testing of the API; upstream components passing bytes that were decoded with errors='ignore'; copy-pasting from terminals that embed NUL; log/message data accidentally used as filenames.","solutions":["Reject or sanitize NUL bytes at the API boundary before paths reach the resolver","If binary-sourced names are legitimate, strip \\x00 and re-validate the result as a safe filename","Investigate where the NUL entered — usually a decode bug upstream","Catch ValueError and return a 400 to the client with a clear message"],"exampleFix":"# before\nname = binary_blob.decode('utf-8', errors='ignore')  # may contain \\x00\nresolve_under_root(name, root=root)\n\n# after\nname = binary_blob.decode('utf-8', errors='strict').replace('\\x00', '')\nassert name and '/' not in name\nresolve_under_root(name, root=root)","handlingStrategy":"validation","validationCode":"def path_has_no_nul(path_str) -> bool:\n    return \"\\x00\" not in (path_str or \"\")","typeGuard":"def is_nul_free_path(v: object) -> bool:\n    return isinstance(v, str) and \"\\x00\" not in v","tryCatchPattern":"try:\n    path = resolve_under_root(value, root=root)\nexcept ValueError as exc:\n    if \"null bytes\" in str(exc):\n        raise HTTPException(400, \"path contains invalid characters\") from exc\n    raise","preventionTips":["Reject NUL and other control chars in path fields at the API edge","Decode binary data strictly (errors='strict') before deriving names","Fuzz path endpoints; assert they answer 400, not 500"],"tags":["security","path-validation","input-sanitization"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}