mpfaffenberger / mpfaffenberger/code_puppy

save_session(): non-unique .tmp names race under concurrent saves, no fsync, and unsanitized session names escape base_dir

Open
#422 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
814
Forks
278
Avg merge
2d 5h
Merged PRs (30d)
76

Description

Problem

save_session() in code_puppy/session_storage.py:84-118 uses a temp-file-then-replace pattern for atomicity, but the temp filename collides across session names and breaks the metadata path:

pickle_data = pickle.dumps(history)
tmp_pickle = paths.pickle_path.with_suffix(".tmp")      # foo.pkl -> foo.tmp
...
tmp_metadata = paths.metadata_path.with_suffix(".tmp")  # foo_meta.json -> foo_meta.tmp

Issues:

  1. with_suffix(".tmp") replaces the extension instead of appending, so session.pkl and a hypothetical session.json would share session.tmp. Worse: for a session named auto_session_abc.v2 (dots are legal in session names — build_session_paths does no sanitization), Path("auto_session_abc.v2.pkl").with_suffix(".tmp") produces auto_session_abc.v2.tmp, while the metadata temp auto_session_abc.v2_meta.tmp is fine — but the pickle of a different session named auto_session_abc.v3 maps to a different name, OK; the real collision: concurrent autosaves of the same session (e.g., autosave timer + explicit /dump_context with same name, or two code-puppy instances in the same project) write to the identical .tmp path and interleave bytes before replace().
  2. No fsync before replace — on crash, POSIX allows the rename to survive while data blocks don't, leaving a truncated pickle that later fails to load (and per #421, the metadata read failure path then degrades silently).
  3. build_session_paths performs no filename sanitization: a session name like ../../evil from /dump_context ../../evil happily escapes the contexts dir (base_dir / f"{session_name}.pkl"). Low severity since the user controls their own machine, but it makes cleanup (list_sessions, cleanup_sessions) blind to those files.

Suggested fix

import os, uuid
tmp_pickle = paths.pickle_path.with_name(paths.pickle_path.name + f".{os.getpid()}.tmp")
with tmp_pickle.open("wb") as f:
    f.write(pickle_data)
    f.flush()
    os.fsync(f.fileno())
tmp_pickle.replace(paths.pickle_path)

And reject / and .. (or whitelist [A-Za-z0-9_.-]+) in build_session_paths/callers.

Filed by Zen Reviewer C (code-puppy-60635a)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in code_puppy/session_storage.py:84-118 and inspect build_session_paths plus their callers and existing tests, if present. Check concurrent same-name saves, crash-safe replacement, and names that attempt to leave base_dir. Done means the cases are covered and saved sessions remain isolated, durable, and confined to the contexts directory.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.