Skip to content

feat: scan CLI, orchestrator, and unified library scanner#4

Closed
wgordon17 wants to merge 17 commits intogordon-code:mainfrom
wgordon17:feat/scanner-coverage-gaps
Closed

feat: scan CLI, orchestrator, and unified library scanner#4
wgordon17 wants to merge 17 commits intogordon-code:mainfrom
wgordon17:feat/scanner-coverage-gaps

Conversation

@wgordon17
Copy link
Member

Summary

  • Adds mac2nix scan CLI with async orchestrator dispatching all 18 scanners concurrently
  • Pre-fetch injection shares system_profiler and launchd data across dependent scanners
  • Merges library_audit + app_config scanners into unified library scanner (19 → 18 scanners)
  • Rich progress bar, --output file/stdout, --scanner filter
  • 811 tests passing, ruff clean, pyright clean

Implement all scanner coverage gaps from the comprehensive plan:

- 17 new model classes, 19 modified models
- New library_audit scanner (~/Library + /Library audit)
- Enhanced all 15 existing scanners with expanded discovery
- 273 new tests (219→492), all passing

Key additions per scanner:
- preferences: SyncedPreferences, cfprefsd-only domains, source tracking
- dotfiles: discovery mode, XDG support, sensitive paths, manager detection
- shell: conf.d, completions, source following, frameworks, eval detection
- app_config: Containers, recursive os.walk, volume safeguards
- applications: PATH binaries, dev tool versions, Xcode/CLT
- launch_agents: raw plist capture, sensitive env redaction, typed fields
- homebrew: services, pinned formulae, prefix
- cron: schedule parsing with trigger types, env vars
- network: IPv6, VPN, SOCKS/FTP proxy, WiFi list, locations
- security: firewall rules, Touch ID sudo, custom certificates
- system: macOS version, hardware, Time Machine, sleep, printers, etc.
- display: Night Shift, refresh rate, color profile, True Tone
- audio: full volume settings parsing
- fonts: font collections
- library_audit: directory audit, content capture, workflow/keybinding handlers
- Rename _convert_datetimes → convert_datetimes (used cross-module)
- Use brew services list --json instead of fragile text parsing
- Tighten _classify_binary_source path matching to avoid false positives
- Remove unused brew_names parameter from _get_path_binaries
- Consolidate dirnames pruning in library_audit _capture_uncovered_dir
- Remove unused _SYSTEM_COVERED_DIRS constant
- Clarify Night Shift UUID-keyed plist fallback with comment
- Fix extra blank line in preferences.py
Implement 4 new scanners detecting non-macOS-native package management:

- nix_state: Nix installation, profiles, nix-darwin, home-manager,
  channels, flakes, registries, config, devbox/devenv/direnv
- version_managers: asdf, mise, nvm, pyenv, rbenv, jenv, sdkman
- package_managers: MacPorts, Conda/Mamba
- containers: Docker, Podman, Colima, OrbStack, Lima

Also extends existing scanners:
- system_scanner: Rosetta 2, System Extensions, iCloud, MDM detection
- applications: 9 new BinarySource values + 19 path patterns

New models in package_managers.py (27 models). SystemConfig extended
with rosetta_installed, system_extensions, icloud, mdm_enrolled.
SystemState wired with 4 new domain fields. 19 total scanners.

242 new tests (734 total), ruff clean, pyright 0 errors.
Crashes fixed:
- preferences: catch AttributeError from malformed plist dates
- library_audit: guard _redact_sensitive_keys with isinstance(dict)
- _utils: handle plistlib.UID in convert_datetimes, fix return type

Wrong data fixed:
- display: retina detection via spdisplays_pixelresolution + display_type
- display: parse refresh_rate from resolution string as fallback
- system: system_extensions parser no longer skips *-prefixed data lines
- system: parse [activated enabled] bracket state format
- homebrew: parse stdout even on non-zero exit (broken cask refs)
- shell: extract only path arg from fish_add_path, skip flags
- containers: validate podman --version output isnt Docker shim

Silently missing data fixed:
- system: NTP fallback via launchctl + /etc/ntp.conf
- system: sleep_settings fallback via pmset -g
- version_managers: require binary for pyenv/rbenv/jenv detection
- applications: detect Homebrew cask apps via Caskroom directory
- display: night shift/true tone fallback via defaults export
- network: VPN regex now matches real scutil --nc list format with
  colons/dots in protocol field and optional service type between
  UUID and name
- network: is_active uses ifconfig status: line instead of UP flag,
  correctly marks disconnected interfaces as inactive
- system: NTP detection uses pgrep timed instead of launchctl user
  domain check (timed runs in system domain)
- shell: sourced files now parsed for aliases/env vars (one level),
  previously only tracked as file paths without reading content
- homebrew: cask versions populated from Caskroom directory structure,
  previously only queried formula versions via brew list --versions
- applications: check Wrapper/App.app/Info.plist for iOS apps that lack
  Contents/Info.plist (Apollo, Authy, WiFiman now have versions)
- security: touch_id_sudo returns False (not None) when pam files were
  readable but pam_tid.so was not configured
TCC permissions require Full Disk Access to read and cannot be set via
nix-darwin configuration. The field was always empty in practice and
provided no actionable data for config generation.
TOML single-quoted strings are literal — '\\.py$' produced the regex
\\.py$ (literal backslash + any char + py) instead of \.py$ (dot + py).
The Python lint and format hooks have been silently skipping since the
project was created.
- Remove hardware_serial capture (PII with no migration value)
- Wrap progress callback in try/except (broken terminal can't crash scan)
- deepcopy raw_plist to protect shared prefetch data from mutation
- Expand nix_state _PRUNE_DIRS to skip macOS non-project directories
- Rename convert_datetimes → sanitize_plist_values (handles bytes, UIDs too)
@wgordon17
Copy link
Member Author

Reopening with rebased branch (upstream squash-merged PR #3, causing conflicts).

@wgordon17 wgordon17 closed this Mar 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant