Create the offline event cache with owner-only (0600) permissions#1506
Open
bmehta001 wants to merge 4 commits into
Open
Create the offline event cache with owner-only (0600) permissions#1506bmehta001 wants to merge 4 commits into
bmehta001 wants to merge 4 commits into
Conversation
The SQLite offline cache buffers pending telemetry events (tenant ids, user identifiers, serialized event payloads) but was created with SQLite's default file permissions (0644 -- world-readable), letting any co-located user on a POSIX system read the buffered event stream or tamper with pending events. Restrict the database file to 0600 immediately after opening it in SQLiteWrapper::open. This runs before WAL is enabled, so the -wal/-journal companion files inherit 0600 from the main database file (SQLite's findCreateFileMode derives their mode from the main db). The chmod is best-effort (a failure, e.g. an in-memory ":memory:" database, does not fail the open) and POSIX-only -- on Windows the Unix mode bits are meaningless (NTFS ACLs govern access). Adds a POSIX unit test asserting the cache and its companions are not group/world accessible. Verified on Linux (umask 022): a bare SQLite db is created 0644; with this change the cache and its -wal/-journal are 0600. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens the SQLite offline cache used by OfflineStorage_SQLite to reduce exposure of buffered telemetry/audit events on POSIX systems by ensuring the cache database file is created with owner-only permissions.
Changes:
- Add a POSIX-only
chmod(0600)step after opening the SQLite DB to restrict cache file access. - Add a POSIX unit test verifying the cache DB file permissions are
0600and that any companion files do not grant group/other access.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/unittests/OfflineStorageTests_SQLite.cpp | Adds a POSIX-only unit test asserting the cache DB is owner-read/write only and companions don’t grant group/other access. |
| lib/offline/SQLiteWrapper.hpp | Adds POSIX-only permission tightening (chmod) immediately after opening the SQLite DB. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+317
to
+321
| #if !defined(_WIN32) | ||
| if (::chmod(filename.c_str(), S_IRUSR | S_IWUSR) != 0) { | ||
| LOG_WARN("Could not restrict database file permissions to 0600 (errno %d)", errno); | ||
| } | ||
| #endif |
Address Copilot review: only the main database was chmod'd, so a cache created by an older SDK (or a companion left behind after a crash) could retain the old world-readable 0644 mode on its -wal/-shm/-journal files. Best-effort chmod any pre-existing companions to 0600 on open, ignoring ENOENT. Add ExistingFilesAreTightenedOnOpen covering the migration path (loosen db + plant a leftover -wal, then verify reopen re-tightens both). Files: - lib/offline/SQLiteWrapper.hpp - tests/unittests/OfflineStorageTests_SQLite.cpp Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines
+320
to
+330
| #if !defined(_WIN32) | ||
| if (::chmod(filename.c_str(), S_IRUSR | S_IWUSR) != 0) { | ||
| LOG_WARN("Could not restrict database file permissions to 0600 (errno %d)", errno); | ||
| } | ||
| for (const char* suffix : { "-wal", "-shm", "-journal" }) { | ||
| std::string companion = filename + suffix; | ||
| if (::chmod(companion.c_str(), S_IRUSR | S_IWUSR) != 0 && errno != ENOENT) { | ||
| LOG_WARN("Could not restrict %s file permissions to 0600 (errno %d)", suffix, errno); | ||
| } | ||
| } | ||
| #endif |
Comment on lines
+900
to
+906
| struct stat wst; | ||
| if (::stat(wal.c_str(), &wst) == 0) | ||
| { | ||
| EXPECT_EQ(0, static_cast<int>(wst.st_mode & (S_IRWXG | S_IRWXO))) | ||
| << "pre-existing -wal companion must be tightened to 0600"; | ||
| } | ||
| } |
Address Copilot review round 2:
- The main-database chmod now ignores ENOENT, so opening an in-memory
(":memory:") database -- which LogManagerImpl uses when no tenant token is
configured -- no longer logs a spurious permission-tightening warning on
every open (there is no file to secure).
- ExistingFilesAreTightenedOnOpen now removes the database and its companion
files (including the planted -wal) at the end, so they don't leak into other
tests that reuse the same storage filename.
Files:
- lib/offline/SQLiteWrapper.hpp
- tests/unittests/OfflineStorageTests_SQLite.cpp
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| << "companion file " << suffix << " must not be group/world accessible"; | ||
| } | ||
| } | ||
| } |
Address Copilot review round 3: CacheFileCreatedOwnerReadWriteOnly (and any other WAL-mode test) relied on shutdownAndRemoveFile(), which only removed the main database and could leave -wal/-shm/-journal companions behind to pollute the temp dir. Remove the companions in the shared teardown helper so every test is covered, and drop the now-redundant explicit cleanup from ExistingFilesAreTightenedOnOpen. Files: - tests/unittests/OfflineStorageTests_SQLite.cpp Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The SQLite offline cache (
OfflineStorage_SQLite/SQLiteWrapper) buffers pending telemetry/audit events — tenant ids, user identifiers, and serialized event payloads — awaiting upload. It is opened via:with no permission handling anywhere in the storage layer, so SQLite's compiled-in default applies —
SQLITE_DEFAULT_FILE_PERMISSIONSis 0644 (world-readable). On a POSIX system with a shared or world-accessible profile directory, any co-located user can read the buffered event stream, and any same-uid process can tamper with pending events before upload.Fix
Restrict the cache database to owner read/write only (0600) immediately after opening it, in
SQLiteWrapper::open:-wal/-journalcompanion files inherit0600from the main database file — SQLite'sfindCreateFileModederives their mode from the main db'sst_mode, so no per-companion handling is needed.":memory:"database, or a filesystem that ignoreschmod) logs a warning but does not fail the open.#if !defined(_WIN32)): on Windows the Unix mode bits are meaningless — access is governed by NTFS ACLs.The brief window between create (0644) and
chmod(0600) is on a freshly-created empty database (events are only inserted after initialization completes), so it exposes no event data.Test
Adds a POSIX unit test (
OfflineStorageTests_SQLite.CacheFileCreatedOwnerReadWriteOnly) asserting the cache is0600and that any-wal/-journal/-shmcompanion grants no group/other access.Verified on Linux (umask 022): a bare SQLite db is created
0644; with this change the cache and its-wal/-journalare0600. All 32OfflineStorageTests_SQLitepass.