From 85db9d14a8aa130d55ded517b1f7718cd4d3760d Mon Sep 17 00:00:00 2001 From: Tomas Kracmar Date: Thu, 28 May 2026 14:57:53 +0200 Subject: [PATCH] Add v1.7.15 release notes --- RELEASE_NOTES_v1.7.15.md | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 RELEASE_NOTES_v1.7.15.md diff --git a/RELEASE_NOTES_v1.7.15.md b/RELEASE_NOTES_v1.7.15.md new file mode 100644 index 0000000..6204530 --- /dev/null +++ b/RELEASE_NOTES_v1.7.15.md @@ -0,0 +1,92 @@ +# AOC v1.7.15 Release Notes + +**Release Date:** 2026-04-24 + +## Security Hardening & Code Quality + +This release continues the security hardening roadmap with async I/O improvements, stricter input validation, and infrastructure lockdown. + +### Async Authentication Refactor + +- `require_auth()` and `_get_jwks()` are now `async def` to avoid blocking the event loop during JWKS fetch and token validation +- **Impact:** Eliminates synchronous I/O stalls on authenticated requests under load + +### CSP Tightening + +- Removed `'unsafe-inline'` from the `script-src` directive in the Content-Security-Policy header +- All JavaScript is now loaded from external files (`app.js`) or trusted CDNs with SRI hashes +- `'unsafe-eval'` is retained for Alpine.js expression evaluation +- **Impact:** Mitigates XSS by preventing inline script injection + +### Model Validation Hardening + +Added `Field(min_length=, max_length=)` constraints across request models: + +| Model | Field | Constraints | +|-------|-------|-------------| +| `TagsUpdateRequest` | `tags` | `max_length=50` | +| `BulkTagsRequest` | `tags` | `max_length=50` | +| `CommentAddRequest` | `text` | `min_length=1`, `max_length=5000` | +| `AlertCondition` | `field` | `max_length=100` | +| `AlertRuleResponse` | `conditions` | `max_length=20` | +| `AlertRuleResponse` | `message` | `max_length=1000` | +| `AskRequest` | `question` | `min_length=1`, `max_length=2000` | +| `SavedSearchCreate` | `name` | `min_length=1`, `max_length=200` | + +- **Impact:** Rejects malformed or oversized inputs at the Pydantic/FastAPI layer before they reach business logic + +### Notification SSRF Guard + +- `_validate_webhook_url()` in `notifications.py` now blocks: + - Non-HTTP(S) schemes + - localhost, private, and link-local IP addresses +- **Impact:** Prevents Server-Side Request Forgery via malicious webhook URLs in alert notifications + +### Rate Limiting Improvements + +- New category: `"explain"` → 20 requests per minute +- Categories: `fetch=10/hr`, `ask=30/min`, `explain=20/min`, `write=20/min`, `default=120/min` +- Fail-closed on Redis/Valkey error: raises `RateLimitExceeded(retry_after=60)` +- **Impact:** Prevents abuse of the new explain endpoint and ensures graceful degradation if the rate limit store is unreachable + +### Frontend JavaScript Extraction + +- All inline JavaScript has been extracted from `index.html` into `backend/frontend/app.js` +- Alpine.js SPA loads `/app.js?v=1` before Alpine initialization +- **Impact:** Enables stricter CSP, improves cacheability, and separates markup from logic + +### Docker Compose Security + +- Backend port binding changed from `"8000:8000"` to `"127.0.0.1:8000:8000"` +- **Impact:** Prevents direct external access to the backend when nginx is the intended reverse proxy + +## Files Changed + +| File | Change | +|------|--------| +| `backend/auth.py` | `require_auth()` and `_get_jwks()` made async | +| `backend/main.py` | CSP tightened; startup warnings | +| `backend/models/api.py` | Added `Field` validation constraints | +| `backend/notifications.py` | SSRF guard for webhook URLs | +| `backend/rate_limiter.py` | Added `"explain"` rate limit category | +| `backend/routes/saved_searches.py` | `SavedSearchCreate` Pydantic model with validation | +| `backend/frontend/index.html` | Extracted inline JS to `app.js` | +| `backend/frontend/app.js` | New — extracted frontend JavaScript | +| `docker-compose.yml` | Backend port bound to `127.0.0.1` only | +| `nginx/nginx.conf` | Security headers alignment | +| `backend/tests/test_auth.py` | Updated for async `require_auth()` | +| `backend/tests/test_api.py` | Updated saved searches validation test | +| `backend/tests/test_ask.py` | Updated empty question test for 422 | +| `.gitignore` | Added `memory/` | +| `VERSION` | Bumped to 1.7.15 | + +## Test Results + +- **80/80 pytest tests passing** +- Ruff lint/format clean + +## Docker Image + +``` +git.cqre.net/cqrenet/aoc-backend:v1.7.15 +```