Skip to content

Fix embedded macOS app bundles stealing software title names#46366

Draft
sharon-fdm wants to merge 1 commit into
mainfrom
fix-44199-embedded-app-bundles
Draft

Fix embedded macOS app bundles stealing software title names#46366
sharon-fdm wants to merge 1 commit into
mainfrom
fix-44199-embedded-app-bundles

Conversation

@sharon-fdm
Copy link
Copy Markdown
Collaborator

Closes #44199

Summary

  • Filters out embedded macOS app bundles (login helpers, framework helpers, XPC services, etc.) from software inventory
  • These bundles live inside another .app's Contents/ directory (e.g., Amphetamine.app/Contents/Library/LoginItems/AmphetamineLoginHelper.app) and often share the parent app's bundle_identifier
  • When ingested before the main app, they could claim the software title name -- e.g., "AmphetamineLoginHelper" instead of "Amphetamine"

How it was reproduced

Reproduced locally on a MacBook running Fleet's orbit agent. No VPP or test instance needed.

Step 1 -- osquery proof of duplicate bundle IDs on this machine:

$ osqueryd -S "SELECT name, bundle_identifier, path FROM apps
  WHERE bundle_identifier IN (
    SELECT bundle_identifier FROM apps WHERE bundle_identifier != ''
    GROUP BY bundle_identifier HAVING COUNT(*) > 1
  ) ORDER BY bundle_identifier, name;"

This showed 68 embedded app bundles on this Mac, including real instances of the bug pattern:

  • Claude: com.anthropic.claudefordesktop.helper shared by Claude Helper.app and Claude Helper (Plugin).app (both inside Contents/Frameworks/)
  • VS Code: com.microsoft.VSCode.helper shared by Code Helper.app, Code Helper (GPU).app, Code Helper (Plugin).app
  • Grammarly: embedded helpers at Contents/Library/LoginItems/ and Contents/Library/LaunchAgents/

Step 2 -- logic reproduction: A standalone Go script simulating Fleet's bestTitleNames logic confirmed:

  • When both main app and helper are ingested simultaneously (first time), longestCommonPrefix picks the correct name
  • When the helper is ingested first (from Host A), the title is created as "AmphetamineLoginHelper". When Host B later reports both, Fleet finds the existing title by bundle_identifier and reuses it -- the name is never corrected

Step 3 -- SQL filter verification:

BEFORE:  427 apps
AFTER:   359 apps (with WHERE path NOT LIKE '%.app/Contents/%')
FILTERED: 68 embedded bundles removed

Changes

Two-layer filter:

  1. osquery query (softwareMacOS in queries.go): Added WHERE path NOT LIKE '%.app/Contents/%' to the FROM apps query, preventing embedded bundles from being collected at the source

  2. Go ingestion (directIngestSoftware in queries.go): Added isEmbeddedMacOSAppBundle() check that skips any macOS app whose installed_path contains .app/Contents/. This is defense-in-depth in case data arrives from other paths

Test plan

  • TestIsEmbeddedMacOSAppBundle -- unit test covering main apps, login helpers, framework helpers, launch agents, system apps, user library apps, empty paths
  • TestDirectIngestSoftware/embedded_macOS_app_bundles_are_filtered_out -- integration test simulating the exact Amphetamine scenario (main app + login helper with same bundle ID; only main app should be ingested)
  • Existing TestShouldRemoveSoftware and TestDirectIngestSoftware subtests still pass
  • go vet and go build clean
  • Smoke test: deploy to a test Fleet instance and verify embedded bundles no longer appear in software inventory
  • Verify VPP apps with embedded helpers (e.g., Amphetamine) show the correct name in self-service

Embedded app bundles (login helpers, framework helpers, etc.) nested
inside another .app's Contents/ directory often share the parent app's
bundle_identifier. When these were ingested before the main app, they
could claim the software title name (e.g., "AmphetamineLoginHelper"
instead of "Amphetamine").

Filter at two layers:
- osquery query: WHERE path NOT LIKE '%.app/Contents/%'
- Go ingestion: isEmbeddedMacOSAppBundle() check in directIngestSoftware
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.90%. Comparing base (e76f289) to head (4fd2327).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #46366   +/-   ##
=======================================
  Coverage   66.89%   66.90%           
=======================================
  Files        2783     2784    +1     
  Lines      221736   221794   +58     
  Branches    11356    11356           
=======================================
+ Hits       148335   148391   +56     
+ Misses      60000    59997    -3     
- Partials    13401    13406    +5     
Flag Coverage Δ
backend 68.67% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

Amphetamine app display name is obtained from LoginHelper instead of correct app name

1 participant