Release v1.7.17: Alpine.js CSP build, O365 API window clamping
Release / build-and-push (push) Successful in 1m49s
CI / lint-and-test (push) Successful in 2m6s

This commit is contained in:
2026-05-29 06:44:36 +02:00
parent ad5816dc2d
commit 79647d8962
4 changed files with 53 additions and 3 deletions
+38
View File
@@ -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
```
+1 -1
View File
@@ -1 +1 @@
1.7.16
1.7.17
+1 -1
View File
@@ -6,7 +6,7 @@
<title>Admin Operations Center</title>
<link rel="stylesheet" href="/style.css?v=15" />
<script src="/app.js?v=1"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" integrity="sha384-WPtu0YHhJ3arcykfnv1JgUffWDSKRnqnDeTpJUbOc2os2moEmLkIdaeR0trPN4be" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/csp@3.15.12/dist/cdn.min.js" integrity="sha384-MKLWq9B+VC0W3U8kDIBEsSu8uCnQ1B0UQpRaB+F7uR5ocXFbymMUKuLRntu5LLdu" crossorigin="anonymous"></script>
<script src="https://alcdn.msauth.net/browser/2.37.0/js/msal-browser.min.js" integrity="sha384-DUSOaqAzlZRiZxkDi8hL7hXJDZ+X39ZOAYV9ZDx44gUv9pozmcunJH02tjSFLPnW" crossorigin="anonymous"></script>
</head>
<body>
+13 -1
View File
@@ -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")