Files
nas-runbooks/common/context-hub-memory-entries-securite-mcp.md
T

34 lines
3.9 KiB
Markdown

# context-hub — correctif securite MCP + table memory_entries typee
**Date** : 30/07/2026
**Contexte** : Nabil a juge context-hub sous-exploite et decide de le faire evoluer en memoire IA dediee coding (best practices, erreurs communes), avec MD comme representation durable et l'API/MCP comme jonction read/write instantanee. Avant d'ouvrir a plus d'agents (Antigravity, futurs repos/IA), inspection du code reel a revele une faille et un modele de donnees inadapte.
## Faille identifiee et corrigee
`app/routes/mcp.py`, `handle_call_tool` : la couche REST (`api.py`, `X-API-Key` -> `get_current_agent`) authentifie correctement, y compris pour ouvrir la connexion SSE `/mcp`. Mais le handler MCP global ne recevait aucun contexte d'identite lie a cette connexion :
- `get_rules` et `search_rules` : **aucun** controle `has_scope_access`, quel que soit l'appelant.
- `get_agent_config` / `get_my_context` / `update_rule` : controlaient l'acces sur un champ `agent`/`name` **declare par le client dans les arguments**, jamais verifie contre la cle API reelle. Un agent authentifie GEMINI pouvait se declarer `CLAUDE` en argument et lire/ecrire hors de son perimetre.
**Fix** : `contextvars.ContextVar` pose dans `run_server()` au moment de l'etablissement SSE (`current_agent_var.set(agent)`, agent = identite reelle resolue par `get_current_agent`), lu par `handle_call_tool` a la place de `arguments.get("agent")`. Applique uniformement aux 7 tools, y compris `get_ports`/`check_port` qui n'etaient pas gates du tout cote MCP (alors que l'equivalent REST `/api/ports` exige le scope `infra`).
## Nouveau modele : memory_entries
Le PATCH existant (`app/models.py`, `set_rule`) fait un `dict.update()` plat par scope — une cle reecrite ecrase silencieusement la precedente. Inadapte a une liste croissante de decisions/erreurs/best practices. Ajout d'une table dediee, sans toucher `rules` (aucune rupture pour infra/llm/nyora/perso/tt existants) :
```sql
memory_entries(id, scope, project, type, title, body, tags, status, superseded_by, created_by, created_at, updated_at)
```
+ `memory_entries_fts` (FTS5) + triggers de sync insert/update/delete.
`type` in {decision, constraint, best-practice, common-error, do-not-use} — id sequentiel genere par `scope-abbr-NNNN` (ex: `coding-err-0007`). `status` in {active, archived, superseded} — pas de suppression dure, tombstone via `supersede_memory_entry`/`archive_memory_entry` (meme principe que `do-not-use.md` dans MemoryCustodian, evalue puis ecarte au profit de cette integration native context-hub).
Fonctions ajoutees dans `models.py` : `add_memory_entry`, `list_memory_entries`, `search_memory_entries` (FTS5), `supersede_memory_entry`, `archive_memory_entry`. Testees en conditions reelles dans le container (insert/list/search/supersede) — toutes OK. Pas encore d'endpoint HTTP/MCP expose (prochaine phase : scope `coding` + tools `record_lesson`/`get_context_pack`).
## Piege deploiement
`docker compose up -d --build` echoue depuis `ssh nas-host` (Best0f) : `.env` est en `600 root:root`, Best0f (uid 1026) n'a pas les droits de lecture, et `sudo` necessite un mot de passe interactif (regle deja actee : pas de sudo agent). Pour un changement Python pur (pas de Dockerfile touche), pas besoin de `--build` : le bind mount `./app:/app/app` est live, un simple `docker restart context-hub` suffit et evite le probleme .env. `docker` est joignable en direct sans sudo (`/usr/local/bin/docker`, Best0f est dans le groupe `docker` gid 65538) mais absent du PATH non-interactif SSH — utiliser le chemin complet.
## Push Gitea sans git
`git` absent de mcp-nas et de la session SSH nas-host. Push via API Contents Gitea (`PUT .../contents/{path}` avec `sha` courant recupere par un `GET` prealable, `POST` si creation) plutot que `git push` — pattern deja documente dans `common/git-push-depuis-mcp-nas.md`, confirme fonctionnel ici aussi pour des fichiers Python (pas seulement Markdown).