Skip to content

fix(o365): stop alert floods from nine Office 365 rules - #2776

Merged
kryonsx merged 2 commits into
utmstack:v11from
kryonsx:codex/o365-alert-noise-20260929
Oct 1, 2026
Merged

kryonsx merged 2 commits into
utmstack:v11from
kryonsx:codex/o365-alert-noise-20260929

Conversation

@kryonsx

@kryonsx kryonsx commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Why

Since v11.2.14 these Office 365 rules alert on routine Microsoft 365 activity, and the rule flood guard switches them off. Filter 1.4.1 now keeps OneDrive and SharePoint file access and download records, several rules count far below everyday volume, two rules added on 2026-09-25 have no volume threshold, and grouped rules still store one child alert for every matching event. The target is fewer than 10 alerts per hour per rule on any server.

Noise is reduced only by de-duplication, trigger thresholds, or removing a trigger or rule that does not belong. No account or application is excluded by name. Thresholds were set from 30 days of per-identity volumes on v11 deployments, far above everyday use.

What changes

Every changed rule raises one alert per identity (per IP address for password spraying) and drops repeats for seven days with deduplicateBy, instead of groupBy.

Rule Trigger before Trigger after
Microsoft 365 Mailbox Mass Item Access every MailItemsAccessed record 2,000 MailItemsAccessed records per identity within 1h
OneDrive Mass File Access Detected 67 per operation within 30m 1,000 per operation (FileAccessed, FileAccessedExtended, FilePreviewed) within 1h
SharePoint Mass File Download Detected 100 FileDownloaded within 30m 500 within 1h
Suspicious Teams Message Export Activity 20 Teams records within 1h 100 within 1h
Mass Email Deletion Detected 100 HardDelete or 100 SoftDelete within 15m 200 HardDelete or 5,000 SoftDelete within 1h
Potential Password Spraying of Microsoft 365 User Accounts any 5 records from the IP within 60s 50 UserLoginFailed records from the IP within 10m
O365 Audit Log Purge DSIPurgeStarted, AuditSearchDeleted, HardDelete DSIPurgeStarted, AuditSearchDeleted
O365 Admin Role Assignment Add member to role., Add member to group., Add delegated permission grant. Add member to role.
O365 Admin Role/Permission Granted Update user. with TargetId.UserType removed

Why each trigger means what it now claims, from Microsoft's audit log activities, Teams activities and MailItemsAccessed references:

  • MailItemsAccessed records every mail read by any client and groups the messages one client reads within two minutes into one record, so 2,000 records in an hour is bulk reading, not ordinary use.
  • FileAccessed is not logged again for the same user and file within five minutes, so 1,000 in an hour means at least 84 different files. FilePreviewed and FileAccessedExtended are counted separately, as before.
  • Downloading a folder as a ZIP file writes one FileDownloaded record per file.
  • MessagesListed ("Retrieved messages") is logged only for Microsoft Graph API calls, which backup and compliance applications make continuously.
  • SoftDelete removes a message from Deleted Items; emptying a large folder writes thousands of records. HardDelete purges a message from Recoverable Items.
  • HardDelete is a mailbox purge, not audit data, so it no longer triggers O365 Audit Log Purge; mass purges are Mass Email Deletion. DSIPurgeStarted (a Data Security Investigations purge job) and AuditSearchDeleted remain.
  • Password spraying now counts failed sign-ins only (the old history search counted any record from the address, including successful ones).
  • "Add member to group." and user consent grants ("Add delegated permission grant.") are not role assignments.
  • O365 Admin Role/Permission Granted described itself as a stand-in for "Add member to role." records; those are available and covered by O365 Admin Role Assignment, while its Update user. condition matches almost every user update. The startup definition sync deletes shipped rules whose names disappear, so deployments drop it.

Tests

  • plugins/alerts/o365_alert_volume_test.go (new) runs fabricated records through the checked-in O365 filter and the pinned go-sdk v1.1.36 CEL and history code. It pins every threshold, window, history scope and de-duplication key: below the threshold, at it, outside the window, a different identity, and the records that must no longer match.

  • o365_action_result_rules_test.go: the password spraying history case moves to 10m, 50 and the action term.

  • go test ./... in plugins/alerts: all O365, contract, grouping and placeholder tests pass. TestBitdefenderActionResultRaw fails in the same way on untouched v11 (dda9d45a) and is unrelated.

  • Local EventProcessor lab (engine 8a3ade7, go-sdk v1.1.36, production events and alerts plugins, OpenSearch 2.19.1), fabricated identities, seed events spread over several runs so the history counts are real:

    • 23,607 events over nine runs (every run processed all its events): one run older than the windows, six seeding runs, one trigger run, one repeat run.
    • Each identity at the threshold stored exactly one alert and every later repeat was dropped as a duplicate: mailbox (2,000), OneDrive FileAccessed and FilePreviewed (1,000 each), downloads (500), Teams (100), HardDelete (200), SoftDelete (5,000), password spraying (50 from one address), audit purge (one per operation), role assignment (one per actor and target).
    • Identities just below each threshold, and seeds older than each window, stored none. Group membership, consent grants and HardDelete no longer trigger the admin role and audit purge rules.
    • Same inputs with the rules as shipped: 21,593 alerts produced. With this branch: 71 produced, 32 stored.
    • Known engine limit, not changed here: 20 alerts for one identity that arrive at the same instant were all stored, because plugins/alerts checks for duplicates in parallel. A per-rule-and-key lock around the duplicate check and store would remove these copies for every rule.

Follow-up, not in this PR

"Office 365 App Consent Grants Detected" and "Office 365 OAuth Application Anomalous Activity" test Consent to application without the final period that Microsoft records (Consent to application.), so they never match. With consent grants out of O365 Admin Role Assignment, those two rules are where user consent should be detected, once fixed with their own volume check.

🤖 Generated with Claude Code

These rules alerted on routine Microsoft 365 activity once v11.2.14 kept
OneDrive file access and download records, and the rule flood guard then
switched them off. Each changed rule now raises one alert per identity and
suppresses repeats for seven days instead of grouping every event, and
triggers only on volumes far above everyday use:

- Microsoft 365 Mailbox Mass Item Access: 2,000 MailItemsAccessed records
  per identity within 1h (was every record). Exchange groups the messages
  one client reads within two minutes into one record.
- OneDrive Mass File Access: 1,000 FileAccessed, FileAccessedExtended or
  FilePreviewed records per operation and identity within 1h (was 67 in 30m).
- SharePoint Mass File Download: 500 FileDownloaded records per identity
  within 1h (was 100 in 30m).
- Suspicious Teams Message Export Activity: 100 Teams records per identity
  within 1h (was 20). MessagesListed is logged only for Graph API reads,
  which backup and compliance tools make continuously.
- Mass Email Deletion: 200 HardDelete or 5,000 SoftDelete records per user
  within 1h (was 100 of either in 15m).
- Potential Password Spraying: 50 UserLoginFailed records per IP address
  within 10m (was any 5 records from the address in 60s).
- O365 Audit Log Purge: HardDelete is a mailbox purge, not audit data, and
  no longer triggers it; mass purges are Mass Email Deletion.
- O365 Admin Role Assignment: only "Add member to role."; group membership
  and user consent grants are not role assignments.
- O365 Admin Role/Permission Granted is removed: its "Update user." proxy
  matched almost every user update, and real role assignments are covered
  by O365 Admin Role Assignment.

Operation meanings come from Microsoft's audit log activities reference.
o365_alert_volume_test.go pins the thresholds, windows, history scopes and
de-duplication keys with fabricated records.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@kryonsx
kryonsx marked this pull request as ready for review September 29, 2026 22:32
@kryonsx
kryonsx requested a review from a team September 29, 2026 22:32
Resolve the conflict in the password spraying rule: keep rule version
v1.1.0 from this branch and the failed/denied actionResult condition from v11.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@kryonsx
kryonsx merged commit 8d3becb into utmstack:v11 Oct 1, 2026
3 of 7 checks passed
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