33800 Docs

← Retour

Confrontation Audit — ulias-org-web

Date : 15/02/2026 Status : IMPLEMENTEE Auditeur : Claude Opus 4.6 (synthese confrontee) Sources : Pass 1 (02h00), Pass 2 (03h00), Pass 3 (04h00)


Resume executif

Categorie Nombre
Findings CONFIRMES (2-3 passes concordantes) 12
Findings UNIQUES (1 seule passe) 13
Faux positifs elimines 4
Contradictions resolues 3

Total findings retenus : 22 (12 confirmes + 10 uniques valides) Total faux positifs : 4 (dont 2 declares par les passes elles-memes)


Findings CONFIRMES (2-3 passes concordantes)

C-01 : Polling 500ms infini apres toggle showArchived


C-02 : XSS via javascript: URI dans les liens Markdown


C-03 : window.location.href = '/' perd le message envoye dans retryObjective


C-04 : Race condition setTimeout(500ms) pour loadLastConversation


C-05 : Endpoint /api/teams sans header X-API-Key


C-06 : Erreurs reseau silencieuses partout (catch vides)


C-07 : Etat local desynchronise apres delete/archive/pin echoue


C-08 : Suppression sans confirmation (conversations et objectifs)


C-09 : Reconnexion WebSocket limitee a 5 tentatives sans recovery


C-10 : API key envoyee dans chaque message WS (pas seulement auth)


C-11 : Types TypeScript incomplets pour les events serveur


C-12 : useStore pour streamInfo — risque de serialisation Qwik


Findings UNIQUES (1 seule passe)

U-01 : searchTimerRef jamais nettoye au cleanup


U-02 : Accessibilite — modal prompts sans role/aria/trap de focus


U-03 : Accessibilite — boutons SVG sans aria-label


U-04 : Accessibilite — navigation clavier incomplete (div onClick$ sans role/tabIndex)


U-05 : Accessibilite — pas de live region pour le streaming


U-06 : Bus evenementiel non type — aucun typage sur les CustomEvent


U-07 : conversation_set/created/title_updated/summary_updated accedent a event.data?.title mais n'ont pas de champ data


U-08 : originalMessage jamais emis par le backend pour objective_completed/failed


U-09 : WebSocket connecte sur /ws/cli au lieu de /ws/webui


U-10 : /api/status ne verifie pas l'API key — validation login toujours true


U-11 : revokeObjectURL appele trop tot apres click()


U-12 : setTimeout(500ms) pour template message (meme pattern que C-04)


U-13 : 9 signaux Qwik par instance EventCard, potentiel probleme de performance


Contradictions resolues

Contradiction 1 : Severite du XSS via javascript: URI

Pass Severite Justification
Pass 1 (F11) FAIBLE "les navigateurs modernes bloquent javascript: dans les liens"
Pass 2 (2.1) BUG CERTAIN XSS exploitable
Pass 3 (F-06) BUG PROBABLE XSS exploitable

Resolution : Pass 2 a raison. Les navigateurs modernes ne bloquent PAS javascript: dans les <a href> cliques par l'utilisateur. Ils bloquent javascript: tape dans la barre d'adresse. Un lien <a href="javascript:alert(1)"> clique par l'utilisateur executera le JavaScript dans tous les navigateurs majeurs. L'attribut target="_blank" ne protege pas non plus car javascript: est execute dans le contexte de la page origine, pas dans un nouvel onglet (le navigateur ne cree pas de nouvel onglet pour javascript:). Severite retenue : BUG CERTAIN.


Contradiction 2 : Le polling archiveInterval — "boucle infinie" vs "ne fonctionne jamais"

Pass Description
Pass 1 (F01) "Apres le premier toggle, boucle infinie de requetes toutes les 500ms"
Pass 2 (1.4) "Apres le premier toggle, continue d'appeler loadConversations() indefiniment"
Pass 3 (F-09) Titre : "ne fonctionne jamais". Corps : confirme la boucle infinie.

Resolution : Pass 1 et Pass 2 sont precis. Le titre de Pass 3 est trompeur, mais le corps de son analyse arrive a la meme conclusion : apres le premier toggle, le setInterval appelle loadConversations() toutes les 500ms indefiniment. Le titre "ne fonctionne jamais" est incorrect — le mecanisme "fonctionne" techniquement (il detecte bien le changement), mais il ne s'arrete jamais. Les trois passes s'accordent sur le comportement reel. Conclusion : boucle infinie apres le premier toggle, pas "ne marche pas".


Contradiction 3 : Markdown &amp; dans les URLs — bug ou pas ?

Pass Verdict
Pass 1 (F04) "pas de bug, les navigateurs decodent &amp; dans les href"
Pass 3 (F-07) "BUG PROBABLE — les URLs sont corrompues"

Resolution : Pass 1 a raison pour le HTML. Dans un attribut HTML href, les entites HTML sont decodees par le parseur HTML avant l'envoi de la requete. Donc <a href="https://example.com?a=1&amp;b=2"> fonctionne correctement — le navigateur enverra la requete vers https://example.com?a=1&b=2. Cependant, Pass 3 a un point partiel : si le HTML est insere via dangerouslySetInnerHTML (ce qui est le cas), le navigateur decode les entites comme pour du HTML standard. Les liens fonctionnent correctement. C'est un FAUX POSITIF de Pass 3. Voir section "Faux positifs".


Faux positifs elimines

FP-01 : useStore mute dans le render (EventCard.tsx)

FP-02 : useNavigate() dans handler onClick$

FP-03 : checking.value early return dans login

FP-04 : Markdown &amp; dans les URLs corrompant les liens


Top 10 actions prioritaires

# Finding Action Effort Priorite
1 C-02 XSS : filtrer les protocoles dans le renderer Markdown — Ajouter une validation dans la regex des liens pour n'accepter que http:, https:, mailto: et /. Rejeter javascript:, data:, vbscript:. 5 min CRITIQUE
2 C-01 Supprimer le setInterval archiveInterval — Le handler onClick$ du bouton "Voir archivees" fait deja le fetch. L'interval est inutile et dangereux. 2 min CRITIQUE
3 C-03 Remplacer window.location.href par useNavigate() dans retryObjective (history/index.tsx). Stocker le message dans localStorage et l'envoyer apres la navigation, ou envoyer via le WS puis naviguer. 15 min HAUTE
4 C-04 Remplacer setTimeout(500) par un handshake — ChatMessages emet ulias:chat-ready dans son useVisibleTask$, le layout attend cet event avant d'envoyer l'historique. 20 min HAUTE
5 U-01 Ajouter clearTimeout(searchTimerRef.value) dans le cleanup de ConversationSidebar. 1 min HAUTE
6 C-07 Verifier res.ok avant de modifier l'etat local dans deleteConversation, archiveConversation, togglePin, saveRename, et deleteObjective. 15 min MOYENNE
7 C-08 Ajouter confirm() avant les suppressions dans deleteConversation et deleteObjective. 5 min MOYENNE
8 C-11 Completer les types TypeScript dans types.ts pour correspondre aux events reellement emis par le backend. 30 min MOYENNE
9 C-05 Ajouter le header X-API-Key dans teams/index.tsx et proteger l'endpoint cote backend. 5 min MOYENNE
10 U-10 Utiliser un endpoint authentifie pour la validation de la cle au login — remplacer /api/status par /api/profile dans login/index.tsx. 5 min MOYENNE