chore(gitignore): stop tracking local IdeA tickets state

This commit is contained in:
2026-07-27 17:23:19 +02:00
parent 41aa2789a7
commit 851f1f8f2c
201 changed files with 4 additions and 6762 deletions

View File

@ -1,55 +0,0 @@
---
issueRef: "#72"
version: 3
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1784236365445
---
# Ticket #72 — Confiance reverse proxy : carnet de chantier
Statut : **livré et mergé dans `develop` (`7fbaa8e`)** le 2026-07-16.
Commits : `75d1ce8` (code), `42804fc` (doc), `ae01297` (merge `--no-ff`). Branche supprimée.
## Ce qui a été livré
- `trusted_proxies` dans `ServerConfig` + flag CLI `--trusted-proxy` (répétable, IP ou CIDR).
- Refus **au démarrage** d'un bind non-loopback distant sans proxy autorisé.
- Guard runtime sur les **trois** surfaces — statique, `/api/*`, WebSocket : le `peer_addr` est vérifié **avant** toute lecture de header.
- `X-Forwarded-Proto: https` obligatoire en mode proxy.
- `X-Forwarded-For` n'autorise **jamais** rien — journalisé uniquement.
- **B0** : réconciliation du port effectif dans `run_server` (le chemin CLI standalone souffrait du même bug que l'embedded : `origin_allowed` comparait à `http://127.0.0.1:0`).
- Diagnostics : `UntrustedProxyPeer`, `ForwardedProtoRejected`, `ForwardedHostMismatch`.
## Ajustement en cours de route — le hard reject `X-Forwarded-Host` a été RETIRÉ
Cadré initialement comme un refus, il est devenu un **warning de diagnostic**. Raison (Architect) : redondant avec la vérification d'`Origin` en égalité stricte déjà appliquée à toutes les routes `/api/*`, et il **cassait la configuration par défaut de nginx**, qui n'envoie pas cet en-tête. Le vrai contrôle d'accès reste pairing/session + pair proxy autorisé. Gain trop faible pour la friction créée.
Le repli de lecture accepte `X-Forwarded-Host`, `Forwarded host=` **ou** `Host`.
## Vérification réelle (Main, hors sandbox)
`cargo test -p web-server`**66 passed / 0 failed**. Re-vérifié sur `develop` après merge : 66 · 41 (backend) · 35 (app-tauri), 0 échec.
**Comportement exercé sur un serveur réellement lancé**, pas seulement en test :
- proxy autorisé + `X-Forwarded-Proto: https` + **host absent** (= nginx par défaut) → **passe** le guard, `forwardedHostMismatch` en diagnostic.
- host **différent** → passe également, warning seulement.
- `X-Forwarded-Proto` absent → **403**, message : « Configure the reverse proxy to send it; for nginx add: proxy_set_header X-Forwarded-Proto $scheme; ».
## ⚠️ Piège d'environnement — un faux vert s'est produit ici, deux fois
**DevBackend et QA sont tous deux bloqués par EPERM sur `TcpListener::bind`.** Un `cargo test -p web-server` sandboxé rend un vert qui ne prouve rien : sur #68 B1, un « 57 passed » sandboxé a masqué un **vrai bug produit** (port éphémère vs `origin_allowed`). Tout vert sur ce crate doit être produit **hors sandbox**. C'est consigné dans les messages de commit, pas seulement ici.
## Revue de sécurité (Git, au-delà du vert)
Guard câblé sur les trois surfaces, aucune route qui y échappe. Le chemin de production propage le vrai pair (`handle_tcp_connection``Some(peer_addr.ip())`). Le repli `peer_ip.unwrap_or(config.listen.ip())` n'est atteignable que depuis des appelants `#[cfg(test)]`. CIDR correct, y compris `prefix == 0` (qui aurait débordé au shift sans traitement à part).
## Changement de comportement ASSUMÉ
Le durcissement **casse volontairement les configurations existantes** : un bind non-loopback distant sans `--trusted-proxy` est désormais refusé au démarrage, avec un message qui dit quoi faire. Consigné dans l'historique git.
## Origine du ticket
Trouvé par **Git**, pas par une revue d'architecture : c'est lui qui a établi que `--trust-reverse-proxy` n'avait aucun effet runtime — il n'était lu que pour exiger sa propre présence. La trilogie déclarait une topologie sécurisée sans jamais la garantir.
## Point ouvert, remonté par Git — à arbitrer par l'utilisateur
**#73 (TLS intégré) rendrait une partie de #72 obsolète.** Si `idea-serve` termine lui-même TLS, la cérémonie proxy — `--trusted-proxy`, le guard, ces diagnostics — ne concerne plus que les déploiements qui gardent un proxy devant. Ce n'est pas du travail perdu (#72 corrige un vrai trou aujourd'hui, et B0 était un bug réel), mais l'ordre des deux mérite un regard.

View File

@ -1,53 +0,0 @@
---
id: "0fba0a7f-8f04-460e-b81a-93790e10b066"
number: 72
title: "Sécurité : donner un effet réel à la confiance reverse proxy (--trust-reverse-proxy est un drapeau creux)"
status: "closed"
priority: "high"
sprint: null
links: [{"target":"#68","kind":"blocks"},{"target":"#71","kind":"relatesTo"},{"target":"#65","kind":"relatesTo"}]
agentRefs: []
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1784218596269
updatedAt: 1784236365445
version: 3
---
**Vulnérabilité de conception existante, dans le mode CLI d'aujourd'hui — indépendante de #68.** Trouvée par Git pendant la mise en place de #68, confirmée et cadrée par Architect.
**Le constat.** `--trust-reverse-proxy` n'a **aucun effet à l'exécution**. Vérifié dans `crates/web-server/src/lib.rs` : le champ n'est lu qu'en ligne 192, dans `ServerConfig::validate()`, **pour exiger sa propre présence** quand `allow_remote` est vrai. Le serveur ne parse jamais `X-Forwarded-*`. C'est un drapeau d'intention pur.
**Le problème.** `validate()` valide une configuration, pas la réalité : rien ne vérifie qu'un proxy est réellement devant le serveur. **Binder `0.0.0.0` avec les trois drapeaux, sans aucun proxy, satisfait `validate()` et sert du HTTP en clair au monde entier.** La trilogie `allow_remote` + origine HTTPS + `trust_reverse_proxy` DÉCLARE une topologie sécurisée, elle ne la GARANTIT pas.
Verdict Architect : « la trilogie actuelle n'est pas un invariant de sécurité, c'est seulement une déclaration d'intention ». Aggravé par #68 : en CLI, taper trois drapeaux est un acte délibéré d'opérateur ; dans un panneau desktop, cocher un bouton ne l'est pas. Le drapeau donnerait un faux sentiment de protection en un clic.
**Invariant runtime corrigé (Architect).**
- `allow_remote=true` signifie : mode public accepté UNIQUEMENT derrière un proxy HTTPS déclaré.
- Les headers `X-Forwarded-*` ne sont fiables QUE si le `peer_addr` réseau est un proxy autorisé — vérifier le pair AVANT de leur faire confiance.
- `X-Forwarded-Proto: https` exigé en mode remote.
- `X-Forwarded-Host` (ou équivalent) doit correspondre à `public_origin`.
- **`X-Forwarded-For` ne doit JAMAIS servir à autoriser une requête** — spoofable, log uniquement.
- Proxy sur cette machine : bind loopback, peer forcément loopback, `trust_reverse_proxy` reste acceptable.
- Proxy sur une autre machine : bind LAN autorisé SEULEMENT avec un proxy explicite (`--trusted-proxy 192.168.1.10` ou CIDR). Toute requête dont le `peer_addr` est hors allowlist est refusée, même porteuse de `X-Forwarded-Proto: https`.
Conséquence produit : le mode desktop « proxy autre machine » **ne peut pas être un clic magique**. Il doit demander l'IP/CIDR du proxy autorisé — ce n'est pas une « listen address » mais un contrôle de sécurité compréhensible : « quelle machine a le droit de parler au serveur IdeA ? ».
**Lots (Architect).**
- **B1 config** : `trusted_proxies: Vec<IpNet/Cidr>` dans `ServerConfig`, flag CLI `--trusted-proxy`, DTO desktop équivalent.
- **B2 runtime guard** : en tête du handling HTTP/WS, vérifier `peer_addr`, `X-Forwarded-Proto=https`, host public conforme.
- **B3 diagnostics** : événements `untrustedProxyPeer`, `forwardedProtoRejected`, `forwardedHostRejected` (à croiser avec #71).
- **B4 docs** : exemples séparés proxy local vs proxy autre machine.
**Points de vérité QA.**
- `0.0.0.0 + allow_remote + public_origin + trust_reverse_proxy` sans trusted proxy → REFUSÉ.
- peer non autorisé vers bind LAN → rejeté.
- peer autorisé mais `X-Forwarded-Proto=http` → rejeté.
- peer autorisé + https + host conforme → passe.
- origine API non conforme → toujours 403.
- aucun test ne fait confiance à `X-Forwarded-For`.
**Compat / migration.** Garder `--trust-reverse-proxy` pour la CLI, mais le déclasser en option de mode : il ne suffit plus pour un bind non-loopback. Chemin de migration à choisir (refus au démarrage vs refus runtime).
⚠️ **Impact utilisateur réel connu** : l'installation actuelle de l'utilisateur (bind `192.168.1.75:17373` + trilogie, proxy sur une autre machine, exposé via `https://idea.anthonybouteiller.ovh`) sera **refusée** par la validation corrigée tant qu'elle ne déclare pas `--trusted-proxy`. Le chemin de migration doit être explicite et le message d'erreur doit dire quoi faire, pas seulement refuser.
**Ordre imposé par Architect** : #68 core (B1 shared-core) peut continuer, il n'est pas exposé utilisateur. Mais **#72 doit être mergé dans `develop` AVANT `feature/ticket68-embedded-server-panel`** — pas de panneau remote avant ce durcissement.