Security & Privacy
Categories:
Security & Privacy
RawCull is built from the ground up with macOS security and user privacy as first-class concerns. The following is a precise and detailed account of every security mechanism the application employs, derived directly from the source code and project configuration.
App Sandbox
RawCull runs inside the macOS App Sandbox (com.apple.security.app-sandbox = true), enforced by the operating system. The entitlements file contains only this single declaration — no additional entitlements are granted. This means:
- The app cannot access any file or folder without explicit user consent
- The app cannot launch arbitrary processes outside its declared capabilities
- The app cannot read or write to system directories or other applications’ data
- Network access is blocked at the OS level (no network entitlements are declared)
File: RawCull.entitlements
Code Signing & Notarization
RawCull is digitally signed with an Apple Developer certificate (Team ID 93M47F4H9T) and notarized by Apple. This means:
- Apple has scanned the binary for known malware
- The code signature guarantees the binary has not been tampered with since signing
- macOS Gatekeeper verifies the signature and notarization ticket on first launch
- Swift symbols are stripped from the release build (
stripSwiftSymbols = true) to reduce the binary’s attack surface
Build configuration: exportOptions.plist
File System Access — Security-Scoped Bookmarks
Because the app is sandboxed, it cannot freely read the file system. All folder and file access follows Apple’s security-scoped bookmark model:
User-initiated selection: The user selects a source folder or destination folder via the standard macOS file picker (
fileImporter). macOS grants the app a temporary, user-approved URL.Bookmark creation: Immediately after selection, the app serializes the approved URL into a persistent security-scoped bookmark using:
try url.bookmarkData(options: .withSecurityScope, ...)The bookmark data is stored in
UserDefaultsunder the keyssourceBookmarkanddestBookmark.Bookmark resolution on subsequent launches: On the next launch, the app reads the stored bookmark data and resolves it back to a URL:
URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, ...)A staleness flag (
bookmarkDataIsStale) is checked — if the folder was moved, the user is prompted to re-select.Access lifecycle: Every file operation that requires access to an out-of-sandbox URL calls:
url.startAccessingSecurityScopedResource() // ... file operations ... url.stopAccessingSecurityScopedResource()The
deferpattern is used in actors to guarantee cleanup even on errors. The main view model also tracks all active security-scoped URLs in aSet<URL>and releases all of them indeinit.
Files involved:
RawCull/Views/CopyFiles/OpencatalogView.swift— initial user selection and bookmark creationRawCull/Model/ParametersRsync/ExecuteCopyFiles.swift— bookmark resolution for rsync operationsRawCull/Actors/ScanFiles.swift— scoped access during directory scanningRawCull/Model/ViewModels/RawCullViewModel.swift— lifecycle tracking and cleanup
See also: Security-Scoped URLs — Technical Deep Dive
File Operations — What RawCull Does and Does Not Do
| Operation | Status | Notes |
|---|---|---|
| Read ARW source files | Read-only | Metadata and embedded JPEG extraction only |
| Write to source folder | Never | RawCull never modifies source files |
| Copy files to destination | Yes | Via /usr/bin/rsync — append-only, non-destructive |
| Delete files | Never | No delete or overwrite operations |
| Write saved selections | Yes | savedfiles.json in the user’s Documents folder |
| Write thumbnail cache | Yes | JPEG thumbnails in ~/Library/Caches/no.blogspot.RawCull/Thumbnails/ |
| Write rsync filter list | Yes | copyfilelist.txt in the user’s Documents folder |
All writes to the Documents folder and cache directory use atomic file writes (Data.write(to:options:.atomic)) to prevent partial writes corrupting stored data.
Process Execution — rsync
RawCull uses /usr/bin/rsync (Apple’s system-provided rsync binary) to copy selected files from source to destination. Key security properties of this integration:
- Fixed binary path: The rsync binary path is hardcoded to
/usr/bin/rsync— it is never resolved from a user-supplied path or environment variable - Argument list, not shell string: Arguments are passed as a Swift
[String]array to the process API, not as a shell command string. This eliminates any risk of shell injection - Controlled environment: The process is launched with a minimal, explicitly specified environment
- No elevated privileges: rsync runs as the current user — no
sudo, nosetuid, no privilege escalation - Dry-run support: A
--dry-run(simulate) mode is supported, allowing users to preview what will be copied before committing - Streaming output: Process output is captured line-by-line and parsed by the
ParseRsyncOutputpackage — no raw shell output is ever executed or evaluated - Non-destructive: rsync arguments never include
--deleteor--remove-source-files— source files are never deleted
rsync-related SPM packages used:
RsyncProcessStreaming— process lifecycle managementRsyncArguments— argument constructionParseRsyncOutput— output parsingRsyncAnalyse— transfer statistics
Data Storage & Persistence
RawCull stores only the following data locally on the user’s Mac. No data ever leaves the device.
| Data | Location | Format | Purpose |
|---|---|---|---|
| Security-scoped bookmarks | UserDefaults (sourceBookmark, destBookmark) | Binary bookmark data | Persistent folder access across launches |
| File ratings and tags | ~/Documents/savedfiles.json | JSON | User’s culling decisions |
| Rsync include list | ~/Documents/copyfilelist.txt | Plain text | Filter list for rsync |
| Thumbnail cache | ~/Library/Caches/no.blogspot.RawCull/Thumbnails/ | JPEG | Performance cache |
| App preferences | UserDefaults (standard domain) | Key-value | Application settings |
No data is written to iCloud Drive, Dropbox, or any networked location.
The thumbnail cache is automatically pruned: files older than 30 days are removed on each launch. Cache filenames are derived from source file paths using an MD5 hash (see Cryptography section below).
Cryptography
RawCull imports Apple’s CryptoKit framework exclusively to compute MD5 hashes for naming cached thumbnail files:
import CryptoKit
let digest = Insecure.MD5.hash(data: Data(sourceURL.standardized.path.utf8))
let hash = digest.map { String(format: "%02x", $0) }.joined()
// Result: a filename like "a1b2c3d4...jpg"
MD5 is used here solely as a deterministic, fast, content-addressable key for the on-disk cache — not for any security or integrity purpose. No passwords, credentials, or sensitive data are hashed or encrypted.
RawCull does not use the Keychain. It does not perform any encryption of user data.
Network Access
RawCull has no network access. No network entitlements are declared in the sandbox, so the OS prevents any outbound connections at the system level. Specifically:
- No
URLSessionorURLRequestcalls exist anywhere in the codebase - No telemetry, analytics, or crash reporting frameworks are included
- No update checking or license verification calls are made
- No third-party SDKs with network components are included
- The app functions entirely offline
Privacy Manifest
RawCull includes a PrivacyInfo.xcprivacy manifest as required by Apple for App Store distribution. The declared privacy properties are:
| Property | Value |
|---|---|
NSPrivacyTracking | false — no tracking |
NSPrivacyTrackingDomains | Empty — no tracking domains |
NSPrivacyAccessedAPIType: File API | Reason 3B52A1FF.1 — accessing files the user has opened |
NSPrivacyAccessedAPIType: Disk Space | Reason E174B746.1 — managing the thumbnail cache |
No camera, microphone, location, contacts, calendar, health, or Photos library access is declared or used.
Permissions — What RawCull Never Requests
RawCull does not request and will never request the following permissions:
- Camera or microphone access
- Location services
- Contacts, Calendar, or Reminders access
- Photos library access
- Full Disk Access
- Network access (inbound or outbound)
- iCloud or CloudKit access
- Bluetooth or USB device access
- Screen recording or accessibility permissions
Third-Party Dependencies
RawCull uses the following Swift Package Manager dependencies:
| Package | Purpose | Network? | Executable code? |
|---|---|---|---|
RsyncProcessStreaming | Process lifecycle for rsync | No | No — manages Foundation.Process |
RsyncArguments | Builds rsync argument arrays | No | No — pure argument construction |
ParseRsyncOutput | Parses rsync output lines | No | No — pure string parsing |
RsyncAnalyse | Analyses rsync transfer statistics | No | No — pure data analysis |
DecodeEncodeGeneric | Generic JSON Codable helpers | No | No — pure value types |
All packages are authored by Thomas Evensen. No third-party analytics, advertising, or telemetry libraries are included.
Concurrency & Memory Safety
RawCull targets Swift 6 (.swift-version pinned to 6.0.3) with strict concurrency checking enabled. Security-relevant implications:
- File scanning and thumbnail generation run in Swift Actors, providing compile-time data-race safety
- UI state is confined to
@MainActor, ensuring UI updates happen only on the main thread - Memory pressure is handled via
NSCachein theSharedMemoryCacheactor — thumbnails are automatically discarded when the system is under memory pressure - All security-scoped URL tracking uses actor-isolated state, preventing concurrent access/release races
Summary
| Security Property | Status |
|---|---|
| App Sandbox | ✅ Enabled — minimal entitlements |
| Code Signed | ✅ Apple Developer certificate |
| Notarized | ✅ Apple notarization |
| Network access | ✅ None — offline only |
| Telemetry / analytics | ✅ None |
| Security-scoped file access | ✅ Properly implemented with lifecycle management |
| Atomic file writes | ✅ Used for all local data persistence |
| Process injection risk | ✅ None — argument list, fixed binary path |
| Keychain / encryption | ✅ Not needed — no secrets handled |
| Privacy manifest | ✅ Accurate and complete |
| Data leaving the device | ✅ Never |
| Swift 6 concurrency | ✅ Strict checking enabled |
Your photos, culling decisions, and file paths remain entirely on your Mac, under your control, at all times.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.