Security & Privacy

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:

  1. 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.

  2. 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 UserDefaults under the keys sourceBookmark and destBookmark.

  3. 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.

  4. Access lifecycle: Every file operation that requires access to an out-of-sandbox URL calls:

    url.startAccessingSecurityScopedResource()
    // ... file operations ...
    url.stopAccessingSecurityScopedResource()
    

    The defer pattern is used in actors to guarantee cleanup even on errors. The main view model also tracks all active security-scoped URLs in a Set<URL> and releases all of them in deinit.

Files involved:

  • RawCull/Views/CopyFiles/OpencatalogView.swift — initial user selection and bookmark creation
  • RawCull/Model/ParametersRsync/ExecuteCopyFiles.swift — bookmark resolution for rsync operations
  • RawCull/Actors/ScanFiles.swift — scoped access during directory scanning
  • RawCull/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

OperationStatusNotes
Read ARW source filesRead-onlyMetadata and embedded JPEG extraction only
Write to source folderNeverRawCull never modifies source files
Copy files to destinationYesVia /usr/bin/rsync — append-only, non-destructive
Delete filesNeverNo delete or overwrite operations
Write saved selectionsYessavedfiles.json in the user’s Documents folder
Write thumbnail cacheYesJPEG thumbnails in ~/Library/Caches/no.blogspot.RawCull/Thumbnails/
Write rsync filter listYescopyfilelist.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, no setuid, 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 ParseRsyncOutput package — no raw shell output is ever executed or evaluated
  • Non-destructive: rsync arguments never include --delete or --remove-source-files — source files are never deleted

rsync-related SPM packages used:

  • RsyncProcessStreaming — process lifecycle management
  • RsyncArguments — argument construction
  • ParseRsyncOutput — output parsing
  • RsyncAnalyse — transfer statistics

Data Storage & Persistence

RawCull stores only the following data locally on the user’s Mac. No data ever leaves the device.

DataLocationFormatPurpose
Security-scoped bookmarksUserDefaults (sourceBookmark, destBookmark)Binary bookmark dataPersistent folder access across launches
File ratings and tags~/Documents/savedfiles.jsonJSONUser’s culling decisions
Rsync include list~/Documents/copyfilelist.txtPlain textFilter list for rsync
Thumbnail cache~/Library/Caches/no.blogspot.RawCull/Thumbnails/JPEGPerformance cache
App preferencesUserDefaults (standard domain)Key-valueApplication 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 URLSession or URLRequest calls 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:

PropertyValue
NSPrivacyTrackingfalse — no tracking
NSPrivacyTrackingDomainsEmpty — no tracking domains
NSPrivacyAccessedAPIType: File APIReason 3B52A1FF.1 — accessing files the user has opened
NSPrivacyAccessedAPIType: Disk SpaceReason 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:

PackagePurposeNetwork?Executable code?
RsyncProcessStreamingProcess lifecycle for rsyncNoNo — manages Foundation.Process
RsyncArgumentsBuilds rsync argument arraysNoNo — pure argument construction
ParseRsyncOutputParses rsync output linesNoNo — pure string parsing
RsyncAnalyseAnalyses rsync transfer statisticsNoNo — pure data analysis
DecodeEncodeGenericGeneric JSON Codable helpersNoNo — 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 NSCache in the SharedMemoryCache actor — 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 PropertyStatus
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.


Last modified March 27, 2026: update (b3d199b)