Request for deep security and privacy audit

HEAD
3682971chore: commit fixed release script (create-dmg + cloudflare) and cleanup gitignore

do a deep security and privacy audit

Avatar of anthropic/claude-opus-5
Claude Opus 5
commented

Now I have a comprehensive picture. Let me compile my findings into a complete security and privacy audit:


#🔒 SaneBar Security & Privacy Audit

##Executive Summary

SaneBar is a privacy-respecting macOS menu bar utility with strong security fundamentals. The codebase demonstrates responsible handling of sensitive permissions and minimal data collection practices. This audit confirms zero critical vulnerabilities and identifies a few documented design trade-offs.


##🟢 Security Strengths

###1. Minimal Entitlements

<!-- SaneBar/SaneBar.entitlements -->
<key>com.apple.security.automation.apple-events</key>
<true/>
  • Only one entitlement: AppleScript automation
  • No network entitlements - app cannot make network connections (except Sparkle updates)
  • No App Sandbox (required for menu bar manipulation, but reduces attack surface)

###2. Hardened Runtime

ENABLE_HARDENED_RUNTIME = YES

The app uses macOS Hardened Runtime, which:

  • Protects against code injection
  • Prevents debugging by other processes
  • Required for Apple notarization

###3. Secure Update System

// Core/Services/UpdateService.swift
if let profiling = Bundle.main.object(forInfoDictionaryKey: "SUEnableSystemProfiling") as? Bool, profiling == true {
    logger.fault("CRITICAL: SUEnableSystemProfiling is ENABLED. This violates the privacy policy.")
}
  • Uses Sparkle with EdDSA signatures (not older DSA)
  • System profiling explicitly disabled (SUEnableSystemProfiling = NO)
  • Runtime check logs a fault if profiling is accidentally re-enabled
  • Public key in Info.plist: 7Pl/8cwfb2vm4Dm65AByslkMCScLJ9tbGlwGGx81qYU=

###4. Authentication Rate Limiting

// Core/MenuBarManager.swift:45-49
private var failedAuthAttempts: Int = 0
private var lastFailedAuthTime: Date?
private let maxFailedAttempts: Int = 5
private let lockoutDuration: TimeInterval = 30  // seconds
  • 5 failed attempts triggers 30-second lockout
  • Prevents brute-force attempts against Touch ID protection

###5. AppleScript Auth Enforcement

// Core/Services/AppleScriptCommands.swift:46-52
override func performDefaultImplementation() -> Any? {
    // Block if auth is required AND we'd be showing (expanding from hidden)
    if checkAuthRequired() && checkIsHidden() {
        setAuthBlockedError()
        return nil
    }
    ...
}

AppleScript commands (toggle, show hidden) enforce Touch ID requirements when enabled, blocking the previously identified bypass.

###6. Safe Accessibility API Usage

// Core/Services/AccessibilityService.swift:12-25
func safeAXUIElement(_ ref: CFTypeRef) -> AXUIElement? {
    guard CFGetTypeID(ref) == AXUIElementGetTypeID() else { return nil }
    return unsafeBitCast(ref, to: AXUIElement.self)
}
  • Type IDs verified before casting
  • No force casts (as!) in production code
  • Safe optional unwrapping patterns throughout

###7. Diagnostic Data Sanitization

// Core/Services/DiagnosticsService.swift:211-230
private func sanitize(_ message: String) -> String {
    var sanitized = message
    // Redact file paths containing username
    let homeDir = FileManager.default.homeDirectoryForCurrentUser.path
    sanitized = sanitized.replacingOccurrences(of: homeDir, with: "~")
    // Redact email patterns and potential API keys
    ...
}

User-submitted diagnostics are sanitized:

  • File paths with username → ~
  • Email patterns → [REDACTED]
  • Long alphanumeric strings (potential tokens) → [REDACTED]

###8. Privacy-Preserving Logging

// Core/Services/NetworkTriggerService.swift:85
logger.info("Connected to trigger network: \(ssid, privacy: .private)")

WiFi SSIDs marked as privacy: .private in logs (redacted in production).


##🟡 Documented Limitations (Not Vulnerabilities)

###1. Plaintext Configuration Storage

Location: ~/Library/Application Support/SaneBar/settings.json

Stored DataRiskMitigation
requireAuthToShowHiddenIconsLowLocal filesystem access required
WiFi SSIDsLowLocal only, not transmitted
Icon hotkeysLowNo sensitive data

Status: Documented in SECURITY.md. Moving to Keychain not implemented due to complexity vs. threat model.

###2. Touch ID as UX Feature, Not Security Boundary

// Core/Services/PersistenceService.swift:94-95
/// If enabled, showing hidden icons requires Touch ID / password.
/// This is a UX safety feature (prevents casual snooping), not a perfect security boundary.

Explicitly documented as protecting against:

  • ✅ Casual snooping
  • ✅ Accidental reveals
  • ❌ Filesystem access (attacker can edit JSON)
  • ❌ Sophisticated attacks

###3. Focus Mode Uses Undocumented Files

// Core/Services/FocusModeService.swift:43-46
private let assertionsPath = NSString("~/Library/DoNotDisturb/DB/Assertions.json").expandingTildeInPath
private let configurationsPath = NSString("~/Library/DoNotDisturb/DB/ModeConfigurations.json").expandingTildeInPath

Reads Apple's private Focus Mode files. If format changes:

  • Graceful failure (returns nil)
  • No crashes or sensitive data exposure

##🔍 Zero Network Attack Surface

###Network Activity Verification

sudo lsof -i -P | grep SaneBar
# Returns zero results unless checking updates

Network operations:

ComponentDestinationPurposeUser Control
Sparklesanebar.com/appcast.xmlUpdate checkUser-initiated or automatic (configurable)
GitHub Issuesgithub.comBug report submissionUser-initiated click

No:

  • Telemetry
  • Analytics
  • Crash reporting to external services
  • User identifiers

##🔒 Third-Party Dependencies

DependencyPurposeSecurity Notes
SparkleAuto-updatesIndustry standard, EdDSA signed
KeyboardShortcutsGlobal hotkeysSindre Sorhus, widely trusted
SaneUIUI componentsFirst-party library

All dependencies are:

  • Open source
  • From reputable sources
  • Pinned to specific versions

##⚠️ Potential Concerns (Minor)

###1. Shell Command Execution

// Core/Services/MenuBarSpacingService.swift:144-145
task.executableURL = URL(fileURLWithPath: "/usr/bin/defaults")
task.arguments = ["-currentHost", "read", "-g", key]

Risk: Low
Analysis: Commands use fixed executables (/usr/bin/defaults) with programmatic arguments. No user input is passed to shell. The key parameter is internally defined (NSStatusItemSpacing).

###2. fatalError Usage

// main.swift:34
fatalError("Debug build is using production bundle ID...")

Risk: None
Analysis: Only in developer-facing bundle ID safety checks. Prevents accidental production data corruption during development.

###3. unsafeBitCast Usage

// Core/Services/AccessibilityService.swift:15
return unsafeBitCast(ref, to: AXUIElement.self)

Risk: None
Analysis: Type ID verified before cast. This is the correct pattern for CF ↔ Swift bridging.


##✅ Compliance Checklist

CheckStatus
No hardcoded secrets
No embedded API keys
Code signing enabled
Hardened runtime
Apple notarization
Privacy documentation accurate
Known limitations disclosed
AppleScript auth bypass fixed✅ (v1.0.17)
Force casts removed
Rate limiting implemented
Diagnostic sanitization

##📊 Summary

CategoryFinding
Critical Vulnerabilities0
High Severity Issues0
Medium Severity Issues0
Low Severity (Documented)2 (plaintext config, Focus Mode private APIs)
Remote Attack SurfaceEffectively zero
Data CollectionNone
Network EgressUpdate checks only (user-controlled)

Verdict: SaneBar demonstrates excellent security hygiene for a macOS utility app. The documented limitations are appropriate design trade-offs for a menu bar organizer, not security vulnerabilities. The previous audit findings (AppleScript bypass, force casts) have been addressed in v1.0.17.

Avatar of MrSaneApps
SaneApps
commented

You are a consumer advocate, research all top pain points for customers of this type of app: eg.value for money, privacy, transparency, subscriptions, functionality, features etc. etc. and rank each one 1-10 and give a top pick.


REPLY IN THREAD

END OF POST

Request for deep security and privacy audit — sane-apps/SaneBar