fix(macos): stop XProtect's own rule loading from raising alerts - #2788
Merged
Merged
Conversation
"XProtect Evasion or Tampering Detected" excludes XProtect's own service
by name, but macOS logs it as XprotectService and the check was
case-sensitive, so every time the service loaded its rules ("Using
XProtect rules location: ... XProtect.yara") the rule alerted, and
groupBy stored a child alert for each record.
The service is now recognised whatever the letter case of its name, and
the rule raises one alert per Mac and process, dropping repeats for
seven days (deduplicateBy dataSource, adversary.process).
macos_alert_volume_test.go pins both; macCheckGrouping now also reads
deduplicateBy and accepts dataSource as the source identity.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
"XProtect Evasion or Tampering Detected" alerted every time XProtect loaded its own rules. It is meant to ignore XProtect's own service (
!equals("origin.process", "XProtectService")), but macOS logs the service asXprotectService(lower-case p) and the comparison is case-sensitive, so the service's routine "Using XProtect rules location: /var/protected/xprotect/XProtect.bundle/Contents/Resources/XProtect.yara" and "Using meta-plist from: …/XProtect.meta.plist" messages matched. These come in bursts when the service starts: between 2026-09-28 00:00 UTC and the afternoon of 2026-09-29 one v11 deployment stored 29 alerts, 23 of them in one hour, and another stored 35, 11 in one hour.groupByon process and host then stored a child alert for every record.Noise is reduced here by de-duplication and by making the rule's own XProtect service exclusion work; nothing else is excluded.
What changes
!equals("origin.process", "XProtectService")!equalsIgnoreCase("origin.process", "XProtectService")groupByadversary.process, adversary.hostdeduplicateBydataSource, adversary.process: one alert per Mac and process, repeats dropped for seven daysThe other five branches (framework bypass messages, writes under
XProtect.bundle, MRT stopped,gk.dbchanges, MRT subsystem failures) are unchanged, and a process other than XProtect's service that names XProtect's rule files still matches.Expected volume
Replaying 30 days of real records from the five v11 deployments that send macOS logs (96 records matched the shipped rule, all on three deployments):
The remaining matches are
osascriptandsandboxdnaming XProtect's script rules and app protection rules while loading them.Tests
plugins/alerts/macos_alert_volume_test.go(new) runs fabricated unified-log records through the macOS parser model and the pinned go-sdk v1.1.36 CEL. It pins the de-duplication keys and checks that XProtect's service loading its rules does not match in either spelling, while another process copying over the rules, a delete of the rules and MRT being terminated do. On the shipped rule theXprotectServicerecord matches.macCheckGroupinginmacos_contract_test.gonow also readsdeduplicateByand acceptsdataSourceas the source identity.go test ./...inplugins/alertspasses on this branch, which is based on currentv11(89cd26c4).8a3ade7, go-sdk v1.1.36, the production events and alerts plugins, OpenSearch 2.19.1, this branch's macOS filter from currentv11) with fabricated unified-log records in two runs: XProtect's service loading its rules and meta-plist, another process copying over the rules, and MRT being terminated on one Mac; then the copy and MRT again on that Mac and the copy on a second Mac. Both runs processed all of their events (one hung after writing its output, the engine's plugin restart under memory pressure on the test machine).🤖 Generated with Claude Code