The Problem

macOS has 22+ persistence mechanisms (LaunchAgents, kernel extensions, TCC, dylib hijacking, MDM profiles, etc.), and most security tools either ignore them or present flat lists with no context. Analysts end up manually triaging hundreds of items, unable to distinguish a signed Apple binary from a signed-but-dangerous third-party one, or to understand why something runs at login.

What This Does

MacPersistenceChecker is a native macOS SwiftUI app that scans all major persistence mechanisms, verifies code signatures, and builds a knowledge graph of what runs automatically. It scores risk using multiple signals: signature trust, behavior, intent mismatch, binary age, and LOLBins detection.

The app includes optional AI analysis via Anthropic's Claude API. Concept extraction is deterministic (six extractors in Services/AI/ConceptGraph/Extractors/), but classification, threat hunting, and snapshot-diff explanations use AI. All analysis is local by default; AI is opt-in with a user-supplied API key.

How It Is Wired

Execution starts in MacPersistenceCheckerApp.swiftAppState.swift, which orchestrates the scan lifecycle. The scanner layer (Services/Scanners/) has 20+ individual scanners, each targeting one mechanism — LaunchAgentScanner, KextScanner, TCCAccessibilityScanner, etc. — all conforming to PersistenceScanner and routed through CommandRunner for shell execution.

The scan pipeline feeds DatabaseManager (SQLite persistence), then RiskScorer and AppInvasivenessAnalyzer compute risk scores. The knowledge graph (Services/AI/KnowledgeGraph/) stores concepts and fingerprints, enabling verdict propagation across related items. The UI layer (Views/) is substantial — ItemDetailView.swift alone is 1,695 lines — and includes charts, snapshots, and a graph visualization.

Key hub: AppState.swift depends on nearly every service. The AI analyst layer (Services/AI/Analyst/) has five tools: per-item analysis, cluster triage, health report, threat hunt, and snapshot diff. The concept graph (Services/AI/ConceptGraph/) is the largest subsystem with 12 files.

How To Use It

Build with Swift Package Manager:

git clone https://github.com/moses-y/MacPersistenceChecker
cd MacPersistenceChecker
swift build

The app requires Full Disk Access to scan system paths. For AI features, add an Anthropic API key in Settings → AI (stored in macOS Keychain). The README documents a pre-built DMG for macOS 13+, with xattr -cr needed if Gatekeeper blocks it.

Real-World Use

After installing suspicious software, run a scan, take a snapshot, then use the snapshot-diff feature after a reboot to see exactly what persistence items were added. The AI triage tool classifies thousands of items into ~280 concept clusters, letting you approve or remove entire software families in one action rather than item-by-item.

Code Health & Issues

Static analysis found 113 issues: 27 high, 86 medium. Three kinds dominate:

  • High — Deep nesting (57 occurrences): Max indentation depth of 9 in AppState.swift, ConceptResolver.swift, and AppInvasivenessAnalyzer.swift. Control flow is hard to follow; fix with early returns and extracted inner blocks.
  • High — Duplicated code (50 files): 216 repeated 6-line blocks across the codebase, notably in AppState.swift, HealthReportGenerator.swift, and MonitoringSettingsView.swift. Extract shared helpers.
  • High — Oversized files (2): ItemDetailView.swift (1,695 lines) and GraphView.swift are too large to reason about safely. Split by responsibility.

Two SDLC gaps: no test suite exists (112 source files, zero tests), and no CI workflow is configured. Both are high-severity risks — regressions ship undetected.

The Bottom Line

The tool is well-architected for its domain: comprehensive scanner coverage, local-first design, and a smart concept-graph approach to AI triage. The codebase is young and carries real maintainability debt — deep nesting, duplication, and no tests or CI. For a security analyst wanting broad macOS persistence visibility with AI-assisted triage, it's worth evaluating; for a team planning to extend it, budget time for refactoring and test infrastructure first.