fix(paths): accept persisted separators on every host
This commit is contained in:
@@ -29,6 +29,7 @@ the frozen-backend fallback mirror it for their toolchains.
|
||||
- The OmniVoice guide now covers combining style attributes with a reference clip (consistent instruct stabilizes cloning; the reference wins conflicts), inline pronunciation control (pinyin / CMU phonemes), and corrects the claim that the default engine can't do voice design — it can, from attributes (#1565)
|
||||
|
||||
### Fixed
|
||||
- Stored artifact subpaths now resolve after moving a data directory between Windows, macOS, Linux, and Docker, while traversal and symlink escapes remain blocked (#1559) — thanks @Eman-Yousaf!
|
||||
- A remote browser hitting an API-key-configured server's admin 403 now gets the API-key login form instead of endless console 403s, while desktop and PIN-only/no-key servers keep the plain loopback error so guests are never offered a login no key can satisfy (#1568) — thanks @paoloantinori!
|
||||
- The crash-isolated ASR sidecar and its download preflight now agree on which model to load — setting the shared faster-whisper model variable applies to both variants instead of the sidecar quietly using a different one (#1556)
|
||||
- "Ready" now requires the deep health probe (a working database-backed route), not just the identity probe — a backend whose install broke underneath can no longer be announced up while every real request fails (#1548)
|
||||
|
||||
@@ -59,11 +59,10 @@ def resolve_within(root: os.PathLike[str] | str, value: os.PathLike[str] | str)
|
||||
raw = os.fspath(value) if value is not None else ""
|
||||
if not isinstance(raw, str) or not raw:
|
||||
raise UnsafePath("path is empty")
|
||||
# Treat both separator families as structural on every host. Otherwise a
|
||||
# Windows traversal string is an innocent-looking filename when validated
|
||||
# on Linux (and can become dangerous after persisted data is moved).
|
||||
if os.sep != "\\" and ("\\" in raw or bool(ntpath.splitdrive(raw)[0])):
|
||||
raise UnsafePath("path uses a foreign separator or drive")
|
||||
# Treat both separator families as structural on every host while still
|
||||
# rejecting Windows drive paths before rebuilding relative components.
|
||||
if os.sep != "\\" and bool(ntpath.splitdrive(raw)[0]):
|
||||
raise UnsafePath("path uses a drive")
|
||||
root_path = Path(root).expanduser().resolve(strict=False)
|
||||
root_text = str(root_path)
|
||||
if os.path.isabs(raw):
|
||||
|
||||
@@ -65,17 +65,17 @@ def test_stored_subpaths_split_on_both_separator_families():
|
||||
assert _PATH_SEPARATORS.split(r"sub\voice.wav") == ["sub", "voice.wav"]
|
||||
|
||||
|
||||
def test_resolve_within_reads_a_stored_subpath(tmp_path):
|
||||
@pytest.mark.parametrize("stored_path", ["sub/voice.wav", r"sub\voice.wav"])
|
||||
def test_resolve_within_reads_a_stored_subpath(tmp_path, stored_path):
|
||||
"""A persisted sub-path resolves to the same file on Windows and POSIX.
|
||||
|
||||
Windows accepts ``/`` as a real separator, so a row written by a Linux
|
||||
host (or a Docker deployment) must resolve there exactly as it does on
|
||||
POSIX instead of being rejected as one unsafe component.
|
||||
Rows written on Windows, POSIX, or Docker must resolve identically after
|
||||
the same data directory is opened on another supported host.
|
||||
"""
|
||||
from core.path_security import resolve_within
|
||||
root = tmp_path / "root"
|
||||
(root / "sub").mkdir(parents=True)
|
||||
assert resolve_within(root, "sub/voice.wav") == root / "sub" / "voice.wav"
|
||||
assert resolve_within(root, stored_path) == root / "sub" / "voice.wav"
|
||||
|
||||
|
||||
def test_resolve_within_rejects_traversal_through_either_separator(tmp_path):
|
||||
|
||||
Reference in New Issue
Block a user