diff --git a/gateway/session.py b/gateway/session.py index e7f4f47d35e..941722e4d96 100644 --- a/gateway/session.py +++ b/gateway/session.py @@ -12,6 +12,7 @@ import hashlib import logging import os import json +import re import threading import uuid from pathlib import Path @@ -66,6 +67,11 @@ from .whatsapp_identity import ( ) from utils import atomic_replace +# Matches any value that could escape the sessions directory as a file path. +# Covers: directory traversal (..), Unix/Windows absolute paths (/ \), +# and Windows drive-letter paths (C:/ D:\\ etc.). +_TRAVERSAL_RE = re.compile(r'\.\.|^[/\\]|^[A-Za-z]:') + @dataclass class SessionSource: @@ -578,7 +584,7 @@ class SessionEntry: # Validate path-sensitive fields to prevent directory traversal (CWE-22) for _field, _val in (("session_key", session_key), ("session_id", session_id)): - if _val and (".." in str(_val) or str(_val).startswith(("/", "\\"))): + if _val and _TRAVERSAL_RE.search(str(_val)): raise ValueError( f"Invalid {_field}: potential directory traversal detected" ) diff --git a/tests/gateway/test_session.py b/tests/gateway/test_session.py index d42a3be4e70..55611b8c0c5 100644 --- a/tests/gateway/test_session.py +++ b/tests/gateway/test_session.py @@ -1085,6 +1085,16 @@ class TestSessionEntryFromDictTraversalValidation: with pytest.raises(ValueError, match="session_id"): SessionEntry.from_dict(self._entry(session_id="\\windows\\system32\\config")) + def test_session_id_windows_drive_letter_raises(self): + from gateway.session import SessionEntry + with pytest.raises(ValueError, match="session_id"): + SessionEntry.from_dict(self._entry(session_id="C:/windows/system32")) + + def test_session_id_windows_drive_backslash_raises(self): + from gateway.session import SessionEntry + with pytest.raises(ValueError, match="session_id"): + SessionEntry.from_dict(self._entry(session_id="D:\\path\\to\\file")) + class TestEnsureLoadedSkipsInvalidEntries: """Regression: one bad sessions.json entry must not block valid entries from loading."""