Engineering Case Study
When the Calendar Becomes the Attack Surface, Part 2
Part 1 ended with a deployed plugin and a design argument. Part 2 is what happened next.
The plugin went live on a small nonprofit site — an animal shelter with an event calendar that sees light traffic and holds mostly adoption events and vaccine clinics. Off-season for the calendar. Low stakes if something went wrong. A good place to find out what the plugin did under real conditions before putting it on sites where a mistake would matter.
At 5am the next morning, it caught something.
The First Real Catch
MJ12bot — a Majestic SEO crawler — was systematically enumerating every combination of event tag and date on the site's calendar. The pattern looked like this:
/events/tag/vaccine/day/2022-03-12/ /events/tag/vaccine/day/2022-03-13/ /events/tag/vaccine/day/2022-03-14/
Every day that had a vaccine clinic tagged, stepping forward through every date in the archive. Then it moved to a different tag and did it again. A Cartesian product of tags and dates, one request every two seconds.
Each of those requests hit The Events Calendar's archive rendering engine, which builds a page for that date, queries the database, generates schema output through the SEO plugin, renders excerpts for every visible event, runs theme filters. The computational cost of a single request is orders of magnitude higher than the cost to send it.
The plugin counted 150 requests in a five-minute window. Tier 2 activated. Calendar archive pages started returning the maintenance message. An email arrived at Zee Creative with the triggering IP, the bot name, the URL that crossed the threshold, and a ready-to-paste Cloudflare WAF rule.
By the time anyone read the email, the site had been handling it automatically for hours. The log showed a clean line of 503 responses stretching back to the middle of the night:
54.37.252.229 - "GET /events/tag/vaccine/day/2022-03-12/" 503 54.37.252.229 - "GET /events/tag/vaccine/day/2022-03-13/" 503 54.37.252.229 - "GET /events/tag/vaccine/day/2022-03-14/" 503
Individual event pages remained accessible throughout. Adoption event pages, vaccine clinic sign-ups, the homepage — all returning 200. Only the archive traversal was blocked.
The plugin didn't prevent an attack from starting. It prevented an attack from mattering.
What the Logs Revealed
Looking at the full log, ChatGPT noticed something. MJ12bot had been blocked cleanly. But another crawler — PetalBot — was still getting through on a different pattern:
?tribe-bar-date=2026-06-01&ical=1 outlook-ical=1
The iCal export endpoints. Calendar Defender v1 was watching for archive URL patterns and query string traversal, but the export endpoints — designed to let visitors subscribe to calendars in Outlook or Apple Calendar — were a separate expensive behavior that hadn't been added to the detection set yet.
The same architectural problem. A public endpoint with asymmetric cost. The plugin knew about it in theory — iCal protection was already in the settings as a configurable option — but outlook-ical=1 specifically wasn't being caught.
v1.3.0 added it. The pattern registry grew.
The Registry Idea
After the first live deployment generated real telemetry, ChatGPT proposed formalizing what had been an ad hoc list of URL patterns into an explicit Endpoint Cost Registry — a table mapping each pattern to its cost level, its protection status, and the incident that surfaced it.
The registry as it stands after the first week of deployments:
/day/ High Protected v1.0 Festival + nonprofit attack /week/ High Protected v1.0 Festival + nonprofit attack /tag/ High Protected v1.1 Animal shelter logs /category/ High Protected v1.1 Animal shelter logs tribe-bar-date High Protected v1.0 Festival WAF rule ical=1 High Protected v1.1 Hosting platform recommendation outlook-ical=1 High Protected v1.3 PetalBot pattern, July 2026 eventDisplay= High Protected v1.3 PetalBot pattern, July 2026
Each row is a thing that happened. The registry is an operational knowledge base, not a spec. It grows with incidents.
The registry is what the plugin learned. Each deployment teaches it about another class of expensive behavior, and the protection becomes broader without becoming more complicated.
The Alert Email
The first alert email was functional but thin. It told you a threshold was crossed. It didn't tell you what crossed it.
v1.3.0 added situational awareness to the alert. The email now includes the triggering IP, the bot name extracted from the user agent, and the specific URL pattern that pushed the counter over the threshold. You open the email and immediately know:
Triggering Request
IP: 54.37.252.229
Bot: MJ12bot/v1.4.8
Pattern: /events/tag/vaccine/day/2022-09-28/
The Cloudflare WAF rule is still in the email, ready to paste. But now you also have the context to understand what you're looking at before you apply it.
The Architecture Completes
While reviewing the live logs, Sabrina made an observation: if the system can detect threatening behavior and shut down a subsystem automatically, it can also detect when conditions have cleared and restore it automatically.
That observation completed the architecture.
Calendar Defender v1.0 was an emergency switch. It detected an attack, isolated the expensive endpoint, and waited for a human to come along with a Cloudflare rule and click Restore. That worked. But it required someone to be available — or the calendar stayed down until they were.
The self-healing version adds the other half. After a lockdown activates:
PROTECTED → traffic continues to be counted separately → minimum lockdown duration elapses (10 min) → quiet window counter begins → 3 consecutive 5-minute windows below restore threshold (20 req/window) RECOVERY → final health check → if quiet: clear lockdown, send restoration notice, return to NORMAL → if traffic resurges: drop back to PROTECTED NORMAL
The gap between activation threshold (150 requests per window) and restore threshold (20 requests per window) is intentional. It prevents the system from flapping on and off if bot traffic is intermittent. The minimum lockdown duration prevents a brief dip from causing premature reopening. The RECOVERY state is a provisional check before fully committing to NORMAL — a way of saying "I think the threat has passed, but I'm still watching."
Automatic isolation without automatic restoration is only half of graceful degradation. A resilient subsystem should recover itself once the conditions that required protection have cleared.
Human intervention is now reserved for: applying a permanent Cloudflare rule, reviewing repeated incidents, adjusting thresholds, investigating new endpoint patterns. Everything else the system handles.
What a Week of Deployments Taught
Calendar Defender went from concept to v1.4.0 in a week, driven almost entirely by what real deployments revealed. The original design was right about the core problem — asymmetric cost between requesting and serving calendar archive pages — but wrong about the scope. iCal exports were the same problem at a different endpoint. The tag and category traversal patterns weren't in the initial detection set. PetalBot used different query string combinations than MJ12bot.
Each gap was a real request that got through on a real site before the pattern was added. Each addition to the registry is documentation of something that happened, not something that was anticipated.
That's the right way for this kind of system to evolve. You can't enumerate every possible attack pattern in advance. You build the detection architecture correctly, deploy it, and let real traffic teach you what belongs in the registry.
The principle that came out of the Oracle campus isolation project applies here too: when the software assumes one thing and reality is another, the fix belongs at the architecture level. Calendar plugins assume cooperative traffic. Reality contains adversarial traffic optimizing for cost. The fix is a layer that intercepts between the request and the expensive operation — not a list of blocked URLs, but a model of asymmetric cost that generalizes to whatever the next expensive endpoint turns out to be.
The animal shelter's vaccine clinic calendar is fully accessible. MJ12bot is getting 503s. The plugin is watching the quiet period and will restore automatically when it's safe.
The first deployment worked. The second deployment will be informed by the first. That's how it's supposed to go.