Files
aoc/RELEASE_NOTES_v1.7.15.md
T
tomas.kracmar 85db9d14a8
CI / lint-and-test (push) Failing after 52s
Add v1.7.15 release notes
2026-05-28 14:57:53 +02:00

93 lines
3.8 KiB
Markdown

# 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
```