mpfaffenberger / mpfaffenberger/code_puppy
save_session(): non-unique .tmp names race under concurrent saves, no fsync, and unsanitized session names escape base_dir
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:
with_suffix(".tmp")replaces the extension instead of appending, sosession.pkland a hypotheticalsession.jsonwould sharesession.tmp. Worse: for a session namedauto_session_abc.v2(dots are legal in session names —build_session_pathsdoes no sanitization),Path("auto_session_abc.v2.pkl").with_suffix(".tmp")producesauto_session_abc.v2.tmp, while the metadata tempauto_session_abc.v2_meta.tmpis fine — but the pickle of a different session namedauto_session_abc.v3maps to a different name, OK; the real collision: concurrent autosaves of the same session (e.g., autosave timer + explicit/dump_contextwith same name, or two code-puppy instances in the same project) write to the identical.tmppath and interleave bytes beforereplace().- No
fsyncbeforereplace— 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). build_session_pathsperforms no filename sanitization: a session name like../../evilfrom/dump_context ../../evilhappily 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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