Motivation
App Sandbox improves security regardless of App Store plans — it limits blast radius if the app is ever compromised. Also a prerequisite for Mac App Store submission.
Sandbox Audit
The codebase is already close to sandbox-compatible. Only 3 things break:
1. com.apple.screencapture defaults read (PreferencesManager.swift:34)
Currently auto-detects the screenshot folder by reading another app's UserDefaults domain. Blocked in sandbox — can't read other apps' preferences.
Fix: Replace with a first-launch folder picker. The existing "add folder" UI (GeneralSettingsView.swift) already uses NSOpenPanel, which is sandbox-friendly.
2. Directory access needs security-scoped bookmarks
DirectoryWatcher.swift uses open(directory, O_EVTONLY) (line 21) and contentsOfDirectory(atPath:) (line 54). In sandbox, these only work on paths granted by NSOpenPanel — but that access is temporary (per-launch only).
Fix: When the user picks a folder via NSOpenPanel, persist a security-scoped bookmark. On launch, resolve bookmarks and call startAccessingSecurityScopedResource() before passing paths to DirectoryWatcher. Call stopAccessingSecurityScopedResource() on teardown.
Key files to change:
PreferencesManager.swift — store/resolve bookmark Data alongside path strings
WatcherManager.swift — manage startAccessingSecurityScopedResource() lifecycle
GeneralSettingsView.swift — save bookmark when user picks a folder via NSOpenPanel
3. Sparkle is incompatible with sandbox
Sparkle downloads and installs updates, which the sandbox blocks.
Fix: Wrap Sparkle in #if !APP_STORE conditional compilation flag. This lets us maintain both distribution channels:
- Direct distribution: Sparkle updates (current behavior)
- App Store: Apple handles updates
Files to change: Package.swift, UpdaterManager.swift, StatusBarController.swift, AppDelegate.swift
Everything else works in sandbox
NSPasteboard.general (clipboard) — works
UNUserNotificationCenter (notifications) — works
SMAppService.mainApp (login item) — works
NSWorkspace.shared.open(url) (opening System Settings) — works
Required entitlements
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
UX impact
Minimal. The only user-visible change is that first launch must prompt the user to pick their screenshot folder instead of auto-detecting it. The existing preferences UI already supports adding/removing folders — it just needs to persist bookmarks behind the scenes.
Implementation plan
- Create
AutoClip.entitlements with the three entitlements above
- Add bookmark persistence to
PreferencesManager (save Data, resolve on launch)
- Update
WatcherManager to call startAccessingSecurityScopedResource() before creating watchers
- Replace
com.apple.screencapture fallback with first-launch folder picker
- Wrap Sparkle in
#if !APP_STORE
- Update
Makefile to pass entitlements to codesign and support APP_STORE=1 flag
- Test: sandbox-on with folder picker, sandbox-off with current behavior
Motivation
App Sandbox improves security regardless of App Store plans — it limits blast radius if the app is ever compromised. Also a prerequisite for Mac App Store submission.
Sandbox Audit
The codebase is already close to sandbox-compatible. Only 3 things break:
1.
com.apple.screencapturedefaults read (PreferencesManager.swift:34)Currently auto-detects the screenshot folder by reading another app's UserDefaults domain. Blocked in sandbox — can't read other apps' preferences.
Fix: Replace with a first-launch folder picker. The existing "add folder" UI (
GeneralSettingsView.swift) already usesNSOpenPanel, which is sandbox-friendly.2. Directory access needs security-scoped bookmarks
DirectoryWatcher.swiftusesopen(directory, O_EVTONLY)(line 21) andcontentsOfDirectory(atPath:)(line 54). In sandbox, these only work on paths granted byNSOpenPanel— but that access is temporary (per-launch only).Fix: When the user picks a folder via NSOpenPanel, persist a security-scoped bookmark. On launch, resolve bookmarks and call
startAccessingSecurityScopedResource()before passing paths toDirectoryWatcher. CallstopAccessingSecurityScopedResource()on teardown.Key files to change:
PreferencesManager.swift— store/resolve bookmarkDataalongside path stringsWatcherManager.swift— managestartAccessingSecurityScopedResource()lifecycleGeneralSettingsView.swift— save bookmark when user picks a folder via NSOpenPanel3. Sparkle is incompatible with sandbox
Sparkle downloads and installs updates, which the sandbox blocks.
Fix: Wrap Sparkle in
#if !APP_STOREconditional compilation flag. This lets us maintain both distribution channels:Files to change:
Package.swift,UpdaterManager.swift,StatusBarController.swift,AppDelegate.swiftEverything else works in sandbox
NSPasteboard.general(clipboard) — worksUNUserNotificationCenter(notifications) — worksSMAppService.mainApp(login item) — worksNSWorkspace.shared.open(url)(opening System Settings) — worksRequired entitlements
UX impact
Minimal. The only user-visible change is that first launch must prompt the user to pick their screenshot folder instead of auto-detecting it. The existing preferences UI already supports adding/removing folders — it just needs to persist bookmarks behind the scenes.
Implementation plan
AutoClip.entitlementswith the three entitlements abovePreferencesManager(saveData, resolve on launch)WatcherManagerto callstartAccessingSecurityScopedResource()before creating watcherscom.apple.screencapturefallback with first-launch folder picker#if !APP_STOREMakefileto pass entitlements tocodesignand supportAPP_STORE=1flag