Skip to content

Sprint review: a read error no longer wipes an ini file; blank or relative path variables count as unset - #53

Merged
thorwhalen merged 3 commits into
masterfrom
sprint-review-fixes
Oct 13, 2026
Merged

thorwhalen merged 3 commits into
masterfrom
sprint-review-fixes

Conversation

@thorwhalen

Copy link
Copy Markdown
Member

Sprint review (independent of the sprint's authors) of #34, #36, #39, #42, #44, #46, #47 and #49. An Opus adversarial reviewer tried each attack on the secure-write code and none got through: permissions under umask 0, files planted by another user, symlinks, hard links, thread safety of the lazy instances. It did find gaps around them:

  • A read error wiped an ini file. ConfigStore treated any OSError as "file missing". A transient error (too many open files, permissions) made it read as empty, and the next write replaced the whole file. The review reproduced an api_key section lost this way. Now only a missing file, or a source that is no file at all, reads as empty, and real read errors raise. ConfigReader reports unusable sources per on_missing and never writes back, so it still tolerates them.
  • A blank or relative path variable rooted configs at the working directory, often a project checkout. This applied to XDG_* and CONFIG2PY_*_DIR. Such a value now counts as unset, as the XDG spec says (Empty XDG_* env var resolves app folders relative to the cwd #38), and a non-empty one that is ignored warns.
  • An unencodable value's error quoted the secret's character. Both SecureTextFiles and ConfigStore.persist now report the position only, and the exception types are unchanged.
  • Repeated checks could be slow. check_folder_is_private no longer re-checks a folder it already warned about; the check can call pwd.getpwall() on every store construction. A bad on_unsafe value is now reported under its own name.

Filed: #52, secret writes are not atomic, so a crash mid-write leaves an empty value file. This predates the sprint.

Dependents: no dependent constructs ConfigStore over an unreadable file, matches on the error texts, or sets a relative CONFIG2PY_* (checked with rg across the fleet). The only override in the fleet, in oa's tests, uses an absolute path.

Tests: 250 before, 254 after, run as CI does (--doctest-modules, sandboxed HOME/XDG). An independent review of the fix found no blocker; its two should-fixes and two nits are in the second commit.

🤖 Generated with Claude Code

thorwhalen and others added 3 commits October 13, 2026 07:26
…ative XDG paths count as unset

From an independent adversarial review of #34 #36 #39 #42 #44 #46 #47 #49.

- ConfigStore read any OSError opening its file as 'missing', so a transient error (too
  many open files, permissions) made it read as empty and the next write replaced the
  whole file. Only a missing file is skipped now; ConfigReader, which reports unusable
  sources per on_missing and never writes back, still tolerates them.
- A blank or relative XDG_* (or CONFIG2PY_*_DIR) value rooted config folders, secrets
  included, at the cwd. As the XDG spec says, such a value now counts as unset (#38).
- A value that can't be encoded raised a KeyError quoting the offending character of
  the (possibly secret) value; it now names only the error type and position.
- check_folder_is_private skips the folder check once a folder was warned about (it
  could call pwd.getpwall on every store construction), and a bad on_unsafe value is
  reported under its own name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…empty; ini persist doesn't quote the character

From the independent review of the previous commit.

- A non-empty XDG_* or CONFIG2PY_*_DIR value that is ignored (relative) now warns, so
  secrets never land somewhere the user didn't choose in silence.
- ConfigStore again reads a folder, a path through a file, or a content string too long
  to be a name as empty (none is a file a write could lose); only real read errors
  (permissions, too many open files) raise.
- ConfigStore.persist raised a UnicodeEncodeError quoting the character it can't encode;
  it still raises that type, with the character replaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Windows reports a folder as PermissionError and an over-long name as EINVAL, so matching
error types was not portable. The rule is now the risk itself: only an existing regular
file that a store failed to read raises, since its next write would replace it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thorwhalen
thorwhalen merged commit 7ac8726 into master Oct 13, 2026
12 checks passed
@thorwhalen
thorwhalen deleted the sprint-review-fixes branch October 13, 2026 07:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant