# Version 1.1.2 Changelog

**Release theme:** Email Flood Fix  
**Release date:** 2026-02-21

This release fixes a critical bug where both notification email pathways (Security Alert and Granular Notification) could fire unlimited emails per hour during periods of high event activity, such as brute-force attacks or routine system operations. No features were added or removed.

---

## Root Cause

The `sentinel_log_event()` function in `sentinel.php` contains two email pathways:

- **Pathway A (Security Alert):** Fires for every event categorised as `security`, `system`, or with `critical` priority.
- **Pathway B (Granular Notification):** Fires for events matching enabled category/priority toggles in settings.

Neither pathway had any rate limiting. With 34+ event types matching Pathway A alone, normal site activity (option changes, cron runs, plugin updates) could generate dozens of emails per hour. During a brute-force attack (`auth_fail` events), this escalated to hundreds of emails per hour — confirmed by production mail logs showing rates exceeding 148 emails/day with entries every second.

v1.1.1 introduced mutual exclusion (only one pathway per event call) and digest deduplication, but did not address the fundamental issue: one email per qualifying event, with no throttle.

---

## Fix: Transient-Based Email Rate Limiter

A new `sentinel_email_rate_limiter($event_key)` function gates both pathways before any `wp_mail()` call. It enforces two independent limits:

### 1. Per-Event-Key Cooldown

- After sending a notification for a specific event type (e.g. `auth_fail`), that event type is suppressed for **30 minutes**.
- Implemented via a transient keyed on `md5($event_key)` with a configurable TTL.
- Default: 1,800 seconds (30 minutes).

### 2. Global Hourly Cap

- A maximum of **5 notification emails per hour** across all event types combined.
- Implemented via a single counter transient with a 1-hour TTL.
- Default: 5 emails/hour.

### Fail-Open Design

- If the transient system is unavailable, `get_transient()` returns `false`, which means the cooldown check passes and emails still send normally. Notifications are never silently dropped due to infrastructure issues.

### Configurable

Both limits can be adjusted via the `sentinel_security_settings` option:

| Setting Key               | Default | Description                                    |
|---------------------------|---------|------------------------------------------------|
| `email_per_event_cooldown`| 1800    | Seconds before the same event type can email again |
| `email_hourly_cap`        | 5       | Maximum notification emails per rolling hour   |

### What Is NOT Affected

- **Event logging:** All events are still recorded to the database regardless of email throttling.
- **Digest reports:** Daily and weekly digest emails are sent via separate cron-based pathways with their own deduplication guards (unchanged from v1.1.1).
- **Incident detection:** Security incident creation, escalation, and auto-resolution are unaffected.

---

## Files Touched

- `sentinel.php` — version bump (1.1.1 → 1.1.2), rate limiter call added to Pathway A (line 1094) and Pathway B (line 1138), new `sentinel_email_rate_limiter()` function added at end of file
- `readme.txt` — stable tag bump and v1.1.2 changelog entry

---

## Invariants After v1.1.2

1. A given event type can trigger at most **1 notification email per 30 minutes** (configurable).
2. The system sends at most **5 notification emails per hour** globally (configurable).
3. All events are still logged to the database regardless of email throttling.
4. The rate limiter is fail-open — transient failures do not suppress notifications.
5. Digest emails (daily/weekly) are not subject to the notification rate limiter.
6. All invariants from v1.1.1 remain in effect.
