From d3565012bb5efe7a0b354c231761d9644a02d5bc Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sun, 7 Jun 2026 03:27:28 +0900 Subject: [PATCH] Fix suggestion store file permissions (#3267) --- DEVELOPER_GUIDE.md | 2 ++ changelog.d/unreleased/3267.security.md | 17 +++++++++++++++++ src/CodeIndex/Cli/SuggestionStore.cs | 2 +- tests/CodeIndex.Tests/SuggestionStoreTests.cs | 13 +++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changelog.d/unreleased/3267.security.md diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 006cfba5d5..18d044f378 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -57,6 +57,7 @@ Development contracts: |---|---| | `.cdidx/` | Created with mode `0700`. | | `codeindex.db` plus WAL/SHM sidecars | Mode `0600` is applied when the files exist. | +| `suggestions-*.json` suggestion stores | Written atomically with owner-only mode `0600` on POSIX. | | Index lock metadata sidecars and active workspace `active.json` | Written as owner-only files and read through small bounded buffers so stale or corrupted diagnostics cannot expose local paths more broadly or force unbounded allocation. | | Checkpoint roots, snapshot directories, manifest files, copied DB/WAL/SHM snapshots, and restore staging/backup directories | Forced owner-only on POSIX. | | `status --json` | Reports `data_dir_mode` and `db_file_mode` when the platform exposes Unix file modes. | @@ -2164,6 +2165,7 @@ net9 CI lane に合わせる場合は `FRAMEWORK=net9.0 make test` を使いま |---|---| | `.cdidx/` | mode `0700` で作成。 | | `codeindex.db` と WAL/SHM sidecar | ファイルが存在する場合は mode `0600` を適用。 | +| `suggestions-*.json` suggestion store | POSIX では owner-only の mode `0600` で atomic write します。 | | index lock metadata sidecar と active workspace の `active.json` | owner-only file として書き、stale / corrupt diagnostic が local path を広く漏らしたり unbounded allocation を強制したりしないよう小さな bounded buffer で読みます。 | | database checkpoint root、snapshot directory、manifest file、copy された DB/WAL/SHM snapshot、restore staging/backup directory | POSIX では owner-only に固定。 | | `status --json` | platform が Unix file mode を公開する場合、`data_dir_mode` と `db_file_mode` を報告。 | diff --git a/changelog.d/unreleased/3267.security.md b/changelog.d/unreleased/3267.security.md new file mode 100644 index 0000000000..e150b22f00 --- /dev/null +++ b/changelog.d/unreleased/3267.security.md @@ -0,0 +1,17 @@ +--- +category: security +issues: + - 3267 +affected: + - src/CodeIndex/Cli/SuggestionStore.cs + - tests/CodeIndex.Tests/SuggestionStoreTests.cs + - DEVELOPER_GUIDE.md +--- + +## English + +- **Suggestion store JSON files now use private POSIX permissions (#3267)** — primary `suggestions-*.json` stores are written through the atomic private-file mode path so persisted triage context stays owner-only on POSIX systems. + +## 日本語 + +- **suggestion store JSON file が POSIX の private permission を使うようになりました (#3267)** — primary `suggestions-*.json` store は atomic な private-file mode 経路で書き込まれ、永続化された triage context が POSIX で owner-only のままになります。 diff --git a/src/CodeIndex/Cli/SuggestionStore.cs b/src/CodeIndex/Cli/SuggestionStore.cs index 4b8b06e2ad..948123d6e8 100644 --- a/src/CodeIndex/Cli/SuggestionStore.cs +++ b/src/CodeIndex/Cli/SuggestionStore.cs @@ -764,7 +764,7 @@ private void SaveUnlocked(List records) Directory.CreateDirectory(dir); NormalizeRecordDefaults(records); - AtomicFileWriter.WriteJson(_filePath, records, s_jsonOptions); + AtomicFileWriter.WriteJson(_filePath, records, s_jsonOptions, DataDirectorySecurity.ApplyPrivateFileMode); } private static bool HasUpstreamSubmission(SuggestionRecord record) => diff --git a/tests/CodeIndex.Tests/SuggestionStoreTests.cs b/tests/CodeIndex.Tests/SuggestionStoreTests.cs index 143d9be6c0..156c54407a 100644 --- a/tests/CodeIndex.Tests/SuggestionStoreTests.cs +++ b/tests/CodeIndex.Tests/SuggestionStoreTests.cs @@ -304,6 +304,19 @@ public void TryAdd_CreatesFile() Assert.True(File.Exists(filePath)); } + [Fact] + public void TryAdd_OnPosixCreatesPrivateStoreFile() + { + if (OperatingSystem.IsWindows()) + return; + + var filePath = Path.Combine(_tempDir, "suggestions-codeindex.json"); + + Assert.True(_store.TryAdd(MakeRecord("other", null, "Private suggestion store"))); + + AssertPrivateFileMode(filePath); + } + // --- LoadAll tests / LoadAll テスト --- [Fact]