From a65f663d2e38d5b71f6990862a1fb400b4f0da75 Mon Sep 17 00:00:00 2001 From: Curryraj Date: Thu, 30 Jul 2026 01:44:18 +0800 Subject: [PATCH] fix: stop SessionStore treating ":memory:" as a file path (#684) ``SessionStore.__init__`` passed its ``db_path`` straight to ``secure_create()``, which touches the path and chmods it. ``:memory:`` is a SQLite sentinel, not a filename, so this tried to create a file literally named ``:memory:``. On Windows ``:`` is illegal in a filename, so construction raised ``OSError: [Errno 22] Invalid argument: ':memory:'`` and the two ``tests/server/test_channel_bridge_deep_research.py`` tests failed there. Elsewhere it succeeds and is merely wrong: it leaves a stray ``:memory:`` file in the working directory, and because ``Path(":memory:").parent`` is ``.``, ``secure_mkdir`` chmods the working directory itself to 0o700. ``KnowledgeStore``, ``TelemetryStore`` and ``TraceStore`` already guard this exact case; ``SessionStore`` was the one store missing the check. Apply the same guard, with the same comment. Adds two regression tests: one that an in-memory store is usable, one that constructing it creates no file. The second fails on every platform without the fix, so the bug cannot silently return on Linux or macOS. Co-authored-by: Claude Opus 5 --- src/openjarvis/server/session_store.py | 6 ++++-- tests/server/test_session_store.py | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/openjarvis/server/session_store.py b/src/openjarvis/server/session_store.py index 107f0052..7dd829cd 100644 --- a/src/openjarvis/server/session_store.py +++ b/src/openjarvis/server/session_store.py @@ -25,9 +25,11 @@ class SessionStore: def __init__(self, db_path: str = "") -> None: if not db_path: db_path = str(get_config_dir() / "sessions.db") - from openjarvis.security.file_utils import secure_create + # Ensure the parent directory exists (skip for :memory:) + if db_path != ":memory:": + from openjarvis.security.file_utils import secure_create - secure_create(Path(db_path)) + secure_create(Path(db_path)) self._db = sqlite3.connect(db_path, check_same_thread=False) self._db.row_factory = sqlite3.Row self._create_tables() diff --git a/tests/server/test_session_store.py b/tests/server/test_session_store.py index c078b25e..6f637035 100644 --- a/tests/server/test_session_store.py +++ b/tests/server/test_session_store.py @@ -115,3 +115,29 @@ class TestLastActiveChannel: def test_returns_none_for_unknown_user(self, store): assert store.get_last_active_channel("nobody") is None + + +class TestInMemoryDatabase: + """``:memory:`` is a SQLite sentinel, not a path — it must not be created. + + ``secure_create`` treats it as a filename, which fails outright on Windows + (``:`` is illegal there) and litters the working directory elsewhere. + """ + + def test_in_memory_store_is_usable(self): + s = SessionStore(db_path=":memory:") + try: + session = s.get_or_create("user1", "twilio") + assert session["sender_id"] == "user1" + finally: + s.close() + + def test_in_memory_store_creates_no_file(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + before = set(tmp_path.iterdir()) + s = SessionStore(db_path=":memory:") + try: + assert set(tmp_path.iterdir()) == before + assert not (tmp_path / ":memory:").exists() + finally: + s.close()