From 79647d896249537eaf2275a6ca5a197325172b58 Mon Sep 17 00:00:00 2001 From: Tomas Kracmar Date: Fri, 29 May 2026 06:44:36 +0200 Subject: [PATCH] Release v1.7.17: Alpine.js CSP build, O365 API window clamping --- RELEASE_NOTES_v1.7.17.md | 38 ++++++++++++++++++++++++++++++++ VERSION | 2 +- backend/frontend/index.html | 2 +- backend/sources/unified_audit.py | 14 +++++++++++- 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 RELEASE_NOTES_v1.7.17.md diff --git a/RELEASE_NOTES_v1.7.17.md b/RELEASE_NOTES_v1.7.17.md new file mode 100644 index 0000000..2ba759e --- /dev/null +++ b/RELEASE_NOTES_v1.7.17.md @@ -0,0 +1,38 @@ +# AOC v1.7.17 Release Notes + +**Release Date:** 2026-05-29 + +## Security & Hardening + +### Alpine.js CSP Build + +The frontend now loads the **Alpine.js CSP build** (`@alpinejs/csp@3.15.12`) instead of the standard distribution. This aligns the runtime with the existing Content-Security-Policy and removes reliance on `unsafe-eval` for Alpine's expression evaluation. + +- **File:** `backend/frontend/index.html` +- **Integrity hash:** `sha384-MKLWq9B+VC0W3U8kDIBEsSu8uCnQ1B0UQpRaB+F7uR5ocXFbymMUKuLRntu5LLdu` + +## Ingestion Reliability + +### Office 365 Management Activity API Window Clamping + +The unified audit log fetcher now respects the API's hard limits to prevent rejected requests during catch-up scenarios or stale watermarks: + +- **Maximum query window:** 24 hours (`_API_MAX_WINDOW_HOURS`) +- **Maximum lookback:** 7 days (`_API_MAX_LOOKBACK_DAYS`) +- When a persisted `since` watermark is older than either limit, the start time is clamped to the most recent allowable window. Subsequent fetches continue catching up normally. + +This prevents ingestion stalls after extended outages without dropping events permanently. + +## Files Changed + +| File | Change | +|------|--------| +| `backend/frontend/index.html` | Switched Alpine.js to CSP build with updated SRI hash | +| `backend/sources/unified_audit.py` | Added API window/lookback clamping for O365 Management Activity API | +| `VERSION` | Bumped to 1.7.17 | + +## Docker Image + +``` +git.cqre.net/cqrenet/aoc-backend:v1.7.17 +``` diff --git a/VERSION b/VERSION index 15421b3..63c16ce 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.7.16 +1.7.17 diff --git a/backend/frontend/index.html b/backend/frontend/index.html index d14f787..25e67a6 100644 --- a/backend/frontend/index.html +++ b/backend/frontend/index.html @@ -6,7 +6,7 @@ Admin Operations Center - + diff --git a/backend/sources/unified_audit.py b/backend/sources/unified_audit.py index 3dc51e5..02510c5 100644 --- a/backend/sources/unified_audit.py +++ b/backend/sources/unified_audit.py @@ -11,13 +11,25 @@ AUDIT_CONTENT_TYPES = { } +# Office 365 Management Activity API hard limits +_API_MAX_WINDOW_HOURS = 24 +_API_MAX_LOOKBACK_DAYS = 7 + + def _time_window(hours: int, since: str | None = None): end = datetime.utcnow() + earliest_allowed = end - timedelta(days=_API_MAX_LOOKBACK_DAYS) + max_window_start = end - timedelta(hours=_API_MAX_WINDOW_HOURS) + if since: # Office 365 API expects format without Z start = datetime.fromisoformat(since.replace("Z", "+00:00")).replace(tzinfo=None) + # Clamp: the API rejects windows > 24 h or start times > 7 days in the past. + # If the watermark is stale (e.g. after a long outage), cap to the most recent + # 24-hour window so the API accepts the request; subsequent fetches catch up. + start = max(start, earliest_allowed, max_window_start) else: - start = end - timedelta(hours=hours) + start = max(end - timedelta(hours=min(hours, _API_MAX_WINDOW_HOURS)), earliest_allowed) return start.strftime("%Y-%m-%dT%H:%M:%S"), end.strftime("%Y-%m-%dT%H:%M:%S")