From aeab4b25e50dfe8a87e5e346836b00f3514ceacf Mon Sep 17 00:00:00 2001 From: neeraj netrakar Date: Thu, 22 Oct 2026 23:38:42 +0530 Subject: [PATCH] Create FileHistory file with owner-only permissions --- src/prompt_toolkit/history.py | 10 ++++++++-- tests/test_history.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/prompt_toolkit/history.py b/src/prompt_toolkit/history.py index 662e10460..fbbbec222 100644 --- a/src/prompt_toolkit/history.py +++ b/src/prompt_toolkit/history.py @@ -296,8 +296,14 @@ def add() -> None: return reversed(strings) def store_string(self, string: str) -> None: - # Save to file. - with open(self.filename, "ab") as f: + # Save to file. The history can contain anything the user typed, so + # create the file readable by the owner only (like readline does). + fd = os.open( + self.filename, + os.O_WRONLY | os.O_APPEND | os.O_CREAT | getattr(os, "O_BINARY", 0), + 0o600, + ) + with open(fd, "ab") as f: def write(t: str) -> None: f.write(t.encode("utf-8", errors="replace")) diff --git a/tests/test_history.py b/tests/test_history.py index 500b7f119..3820945a2 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -1,7 +1,12 @@ from __future__ import annotations +import os +import stat +import sys from asyncio import run +import pytest + from prompt_toolkit.history import FileHistory, InMemoryHistory, ThreadedHistory @@ -60,6 +65,18 @@ def test_file_history(tmpdir): assert _call_history_load(history2) == ["test3", "world", "hello"] +@pytest.mark.skipif(sys.platform == "win32", reason="POSIX file modes") +def test_file_history_permissions(tmpdir): + histfile = tmpdir.join("history") + old_umask = os.umask(0o022) + try: + FileHistory(histfile).append_string("hello") + finally: + os.umask(old_umask) + + assert stat.S_IMODE(os.stat(histfile).st_mode) == 0o600 + + def test_threaded_file_history(tmpdir): histfile = tmpdir.join("history")