diff --git a/CHANGELOG.md b/CHANGELOG.md index cd03067..814388f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,55 @@ Based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). --- +## [2.5.2] — 2026-06-07 + +### Fixed + +- **Admin console — space creation no longer fails with "Empty response".** + - **Root cause (infrastructure, not application code).** The admin console + proxies every tool call through `POST /api/tool`, which is routed + **through** the Coraza WAF (OWASP CRS). When creating a space with + Markdown rules, that content travels in the request body and trips CRS + XSS/SQLi heuristics — notably the `<` characters in the default rules + (`activeContext.md < 8 KB`, `< 15 KB`) and the word `delete` + (`delete sections superseded…`). The cumulative anomaly score crossed + the threshold (5), Coraza returned a **403 with an empty body**, and the + web UI surfaced it as `space_create` failing with *"Empty response"*. + This is why `space_list` worked but `space_create` (with rules) did not. + - **Fix.** `waf/Caddyfile` now excludes the **request body** of + `/api/tool` from CRS inspection + (`ctl:requestBodyAccess=Off` scoped to `REQUEST_URI @beginsWith + /api/tool`). The endpoint is already gated by an HttpOnly cookie **and** + the `write` permission and only proxies structured `{tool, arguments}` + calls, so this mirrors the trust rationale already applied to `/mcp*`. + Rate-limiting, body-size limits, and URI/header inspection (path + traversal, scanners) remain active. + - ⚠️ **Requires restarting/recreating the WAF container** for the change + to take effect (`docker compose restart waf`). The Caddyfile is + bind-mounted (`./waf/Caddyfile:/etc/caddy/Caddyfile:ro`), so **no image + rebuild is needed** — Caddy re-reads it on restart. + +### Added + +- **Admin console — Owner field on "Create Space" is now a suggestion list.** + - The free-text `Owner` input is paired with a `` populated from + **active token holders** (names deduplicated and sorted). Free-text entry + is still allowed for owners that have no token. + - Stored value is the **token name** (consistent with the Owner column + rendering). `owner` remains optional and purely informational + server-side — it has never been able to cause a creation failure. + +### Changed + +- `waf/Caddyfile` — new Coraza rule `id:900500` scoping + `requestBodyAccess=Off` to `/api/tool`. +- `src/live_mem/static/js/admin-app.js` — new `ownerOptionsHtml()` helper; + `showCreateSpace()` renders the Owner ``. +- `DESIGN/live-mem/DEPLOIEMENT_PRODUCTION.md` — §5 "WAF Routes" documents the + `/api/tool` body-inspection exception. + +--- + ## [2.5.1] — 2026-06-05 ### Added diff --git a/DESIGN/live-mem/DEPLOIEMENT_PRODUCTION.md b/DESIGN/live-mem/DEPLOIEMENT_PRODUCTION.md index f90f852..5e359a7 100644 --- a/DESIGN/live-mem/DEPLOIEMENT_PRODUCTION.md +++ b/DESIGN/live-mem/DEPLOIEMENT_PRODUCTION.md @@ -128,12 +128,27 @@ networks: | Route | WAF Coraza | Timeout | Usage | |---|---|---|---| | `/mcp*` | ❌ (bypass) | Unlimited | Single MCP Streamable HTTP endpoint (POST/GET/DELETE) | -| `/api/*` | ✅ | 5min | REST API (web interface) | +| `/api/tool` | ⚠️ (headers/URI only, body not inspected) | 5min | Admin console tool proxy | +| `/api/*` (other) | ✅ | 5min | REST API (web interface) | | `/live`, `/static/*` | ✅ | Standard | Web interface | | Everything else | ✅ | Standard | Health (`/health`), etc. | > **Note v0.5.0**: The former `/sse*` and `/messages/*` routes (SSE transport) have been removed and unified into `/mcp*` (Streamable HTTP). +> **Note v2.5.2 — `/api/tool` request body excluded from CRS inspection.** +> The admin console proxies tool calls through `POST /api/tool`, whose body +> carries free-form Markdown (e.g. Memory Bank rules: `<` comparisons, +> the word `delete`, `**`, backticks). The OWASP CRS flagged these as +> XSS/SQLi, pushed the anomaly score past the threshold, and Coraza +> returned an empty-body 403 — surfacing in the UI as `space_create` +> failing with *"Empty response"*. The endpoint is already gated by an +> HttpOnly cookie **and** the `write` permission, and only proxies +> structured `{tool, arguments}` calls, so its **request body** is +> excluded from CRS inspection (`ctl:requestBodyAccess=Off` scoped to +> `REQUEST_URI @beginsWith /api/tool`). Rate-limiting, body-size limits, +> and URI/header inspection (path traversal, scanners) stay active — same +> trust rationale already applied to `/mcp*`. + --- ## 6. Backup & Restore diff --git a/VERSION b/VERSION index 73462a5..f225a78 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5.1 +2.5.2 diff --git a/src/live_mem/__init__.py b/src/live_mem/__init__.py index f77ad7b..cf37e37 100644 --- a/src/live_mem/__init__.py +++ b/src/live_mem/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- """Live Memory — Memory Bank as a Service pour agents IA collaboratifs.""" -__version__ = "2.5.1" +__version__ = "2.5.2" diff --git a/src/live_mem/static/js/admin-app.js b/src/live_mem/static/js/admin-app.js index 4a5f1a7..31cfbfc 100644 --- a/src/live_mem/static/js/admin-app.js +++ b/src/live_mem/static/js/admin-app.js @@ -287,11 +287,19 @@ async function renderSpaces(){ }`; } +function ownerOptionsHtml(){ + // Suggestions = détenteurs de tokens actifs, noms dédupliqués et triés. + const names=[...new Set((cache.tokens||[]) + .filter(t=>!t.revoked && t.name) + .map(t=>t.name))].sort((a,b)=>a.localeCompare(b)); + return names.map(n=>`