Repository navigation
fix(security): create the sync log and profile directories owner-only (0700) - #104
dash0-dev[bot] wants to merge 1 commit into
Conversation
… (0700) The sync logs name every synced file and the profile JSON names remotes, local paths and extra rclone flags, but ~/.local/log and ~/.config/synctray/profiles were created with the default mode (0755). On a Mac whose home folder other accounts can traverse, another local user could read them (CWE-276). Create both directories 0700 at every creation site, and tighten an existing install once per launch (a real directory owned by this user, group/other bits dropped, never followed through a symlink, nothing created), mirroring tightenConfigPermissionsIfLoose. Covered by ConfigSelfTest AC-SEC5. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
✅ No issues foundCreates ~/.local/log and the profiles directory 0700 at all nine creation sites and tightens existing installs at launch. Correct as written; one non-blocking test-coverage suggestion. 1 advisory finding below the confidence bar — see Less certain below. Checked: 9 of 9 changed files read · 2 possible issues → 0 confirmed → 0 posted Less certain (1) — advisory, below the confidence bar
Review details
Found Quality — produced 2 → posted inline 0 · cleared 0 · carried forward 0 · deferred 0 · below-bar 1 Run full · 139 lines in delta · tier deep · depth checkout
Nothing to report — severity, 0 files skipped.
|
Summary
The per-profile sync logs (
~/.local/log/synctray-sync-{shortId}.log) and the profile JSON (~/.config/synctray/profiles/) live in directories SyncTray creates with the default mode,0755. On a Mac where other accounts can traverse the user's home folder, another local user can list and read them: the name of every synced file, remote names, local paths, and anything typed intoadditionalRcloneFlags. This change creates both directories0700and tightens them on existing installs once per launch.Finding
staffread+execute by default, so~/Publicworks, and every standard user is instaff. File contents andrclone.confare not exposed;rclone.confis already0600.sync-tray/SyncTray/Models/SyncProfile.swift
Lines 172 to 175 in 13bfbd5
sync-tray/SyncTray/Services/SyncSetupService.swift
Lines 2545 to 2555 in 13bfbd5
sync-tray/SyncTray/Services/LogWatcher.swift
Lines 41 to 47 in 13bfbd5
sync-tray/SyncTray/Services/ProfileStore.swift
Lines 125 to 127 in 13bfbd5
sync-tray/SyncTray/Services/ProfileStore.swift
Lines 158 to 160 in 13bfbd5
sync-tray/SyncTray/Services/MigrationRunner.swift
Lines 320 to 321 in 13bfbd5
sync-tray/SyncTray/Views/Settings/ProfileDetailView.swift
Lines 3286 to 3288 in 13bfbd5
Evidence
0755(SyncSetupService.swift#L2545-L2555):LogWatcher.startWatching(L41-L47),ProfileStore(L125-L127, L158-L160), the v3 migration (MigrationRunner.swift#L320-L321) and three manual-run paths inProfileDetailView. The log file itself is created with the default0644, both bycreateFileand by the launchd agent'sStandardOutPath.--verbose(SyncSetupService.swift#L2210) and appends all of its output to the log (L2309,"${RCLONE_CMD[@]}" 2>&1 | tee -a "$LOG_FILE"). That records one line for every transferred or deleted path, plus lines such asUsing primary remote: $REMOTE_NAME(L1474). The profile JSON holds remote names, local paths andadditionalRcloneFlags.No scanner rule; found by manual review.
Reproduction
stat -f '%Sp %N' ~/.local/log ~/.local/log/synctray-sync-*.log ~/.config/synctray/profilesprintsdrwxr-xr-x,-rw-r--r--,drwxr-xr-x.ls -ld /Users/Ashowsr-xfor groupstaff, andhead /Users/A/.local/log/synctray-sync-*.logprints A's synced file names.With this change, step 2 shows
drwx------for both directories, and step 3 fails with "Permission denied".Fix
~/.local/logor~/.config/synctray/profilesnow passesattributes: [.posixPermissions: 0o700]:SyncSetupService.createDirectories,LogWatcher, bothProfileStorewriters, the v3 migration, and the threeProfileDetailViewlog paths. A0700directory protects the files inside regardless of their own mode, so the log files and profile JSON need no per-file change. Foundation also applies these attributes to any intermediate directory it creates, such as a missing~/.local.SyncSetupService.tightenPrivateDirectoriesIfLoose()runs once per launch fromSyncTrayApp, next to the existingtightenConfigPermissionsIfLoose(), and uses the same approach. For each of the two directories it checks four things: the path exists, it is a real directory (attributesOfItemdoes not follow a final symlink), it is owned bygetuid(), and it has group or other bits set. If all four hold, it sets the mode tomode & 0o700. It never creates anything and never recurses.SyncProfile.logDirectoryis now the single source of the log directory;logPathbuilds on it.umaskthere would also change the permissions rclone gives the user's synced files.ConfigSelfTestAC-SEC5 (log-and-profile-dirs-owner-only) checks five cases. A0755directory is tightened to0700. An already-0700directory is left unchanged. A symlink and its0755target are left alone. A regular file and a missing path are left alone, and nothing is created.ProfileStore.writeProfileFilecreates a new directory0700. A source pin also asserts the launch-time call is present.CLAUDE.md's Generated Files table documents the invariant.Verification
git diff --check: clean.rgoverSyncTray/forcreateDirectoryon the log and profile directories: all 9 sites pass0o700, and none remain without attributes.SyncTray.app/Contents/MacOS/SyncTray --self-testprintingAC-SEC5 log-and-profile-dirs-owner-only: PASSwith every other AC still passing. A manual check on a real install would confirm that an upgraded~/.local/logand~/.config/synctray/profilesreaddrwx------after one launch.Opened by the weekly security audit automation. It stays a draft until a maintainer reviews it.