# SECURITY-08 — Audit sécurité applicative (code maison) : XSS réfléchies

**Date** : 2026-06-25 · **Statut** : ✅ **5 XSS High CORRIGÉES (2026-06-25)** — M/L différés (hors scope demandé).
**Skill** : `wordpress-security-validation` (modèle sanitize→validate→escape). Mode : read-only.
**Périmètre** : thème `toulouse-les-orgues`, plugins `pepille_shortcode(_bouton)`, mu-plugins. (Divi/tiers exclus.)

## Verdict
Le moteur de recherche **maison** expose des **XSS réfléchies** exploitables **sans authentification**
(paramètres `$_GET` réinjectés sans échappement dans `href`/`value` sur des pages front). Un lien piégé suffit.
**MAIS** : **0 SQLi** (tout `$wpdb` via `prepare()`/`%s`, meta_query échappées par WP_Meta_Query), **0 upload maison**,
**0 fonction dangereuse** (`eval`/`system`/`preg /e`/`include $_GET`). mu-plugin `tlo-hardening.php` sain.
Bilan : **0 Critical, 5 High, 2 Medium, 3 Low** — risque concentré sur **l'escaping de sortie**.

## HIGH — XSS réfléchies (toutes : échappement de sortie manquant)
| # | Fichier:ligne | Contexte | Fix |
|---|---|---|---|
| H1 | `pepille_shortcode/includes/resultatArtistes.php:133,135` | `$lettreCible`/`$texteCible` (issus de SESSION ← `$_GET` via `recupSession.php`) en HTML | `esc_html()` en sortie + `sanitize_text_field()` avant stockage session |
| H2 | `themes/.../single-artiste.php:239-240` + `single-ensemble.php:98-99` | `$_GET['texteCible']` concaténé en `href` puis `echo` | `esc_url(add_query_arg(...))` |
| H3 | `themes/.../single-instrument.php:318-325` | `villeDept/epoque/esthetique/facteurs/zone` (`$_GET`) bruts en `href` | `esc_url(add_query_arg(...))` (pattern déjà sûr dans `modele-resultat_orgues.php:442`) |
| H4 | `pepille_shortcode/pepille_shortcode.php:2496,2505` | `$_GET['texteCible']` en `value="…"` (form actu) | `esc_attr()` |
| H5 | `pepille_shortcode/includes/moteurArtistesTexte.php:15` | `$_GET['texteCible']` en `value="…"` (form artiste, sans nonce) | `esc_attr()` + nonce |

## MEDIUM
- **M1 — ReDoS** : `pepille_shortcode.php:2339-2345` (`fonction_pageActu`) — `$_GET['texteCible']` brut comme **pattern REGEXP MySQL** sur tous les posts actu (`-1`). Pas de SQLi (valeur échappée) mais pattern catastrophique = saturation MySQL. Fix : `sanitize_text_field` + limiter longueur + neutraliser métacaractères, ou passer en `LIKE %…%`.
- **M2 — Nonce manquant** : `themes/.../functions.php:478-485` (`traitement_recherche_instru`) — handler `submitSearchArtiste` sans `wp_verify_nonce` (les autres handlers en ont). Impact limité (lecture + `wp_safe_redirect` = pas d'open redirect) mais c'est le vecteur d'alimentation de H1/H2. Fix : aligner sur le pattern nonce des autres + `sanitize_text_field`.

## LOW
- **L1** : `themes/.../header.php:9` `print_r($_SESSION)` — atténué (gated `manage_options` + dans commentaire HTML). Supprimer.
- **L2** : `pepille_shortcode_bouton.php:25,154` `extract()` sur `shortcode_atts()` (clés whitelistées) + `$ancre/$lien` non échappés → `esc_url/esc_attr/esc_html`. Bug annexe : `break` manquant L145 (fallthrough).
- **L3** : `resultatArtistes.php:4,6` `==` au lieu de `=` (dans bloc commenté, inactif) — nettoyer.

## Plan de correction priorisé
1. **H2/H3** — généraliser `esc_url(add_query_arg(...))` sur les `href` des single-artiste/ensemble/instrument.
2. **H1** — `esc_html()` (resultatArtistes 133/135) + `sanitize_text_field()` dans recupSession.php.
3. **H4/H5** — `esc_attr()` sur les `value` + nonce sur le form artiste.
4. **M1** — neutraliser/limiter le REGEXP actu (ou LIKE).
5. **M2 + L1** — nonce sur `submitSearchArtiste` + retirer `print_r($_SESSION)`.

## Corrections appliquées (2026-06-25) ✅
6 fichiers édités (snapshots before/after dans `snapshots/xss-fix/`), `php -l` OK, déployés, cache purgé :
- **H1** `resultatArtistes.php` : `esc_html()` sur la sortie (L133/135) + `esc_url()` sur les href de résultats (L60/103) + `sanitize_text_field()` à la lecture session (L10/11).
- **H2** `single-artiste.php` + `single-ensemble.php` : href via `esc_url(add_query_arg('lettreCible'/'texteCible', sanitize_text_field(...), '…?page_id=1520'))`.
- **H3** `single-instrument.php` : URL multi-critères via `esc_url(add_query_arg($critRetour, '…?page_id=1162'))` + `array_map('sanitize_text_field', …)`.
- **H4** `pepille_shortcode.php` (`fonction_searchActu`) : `esc_attr()` sur `value` + `sanitize_text_field()` en entrée.
- **H5** `moteurArtistesTexte.php` : `esc_attr()` sur `value` + `sanitize_text_field()` en entrée.
**Vérif d'injection** : payload `"><script>alert(1)</script>` dans `texteCible`/`facteurs` sur pages artiste/ensemble/instrument/actu → **absent de la sortie** (échappé), pages 200.
**Non traités (hors scope "5 XSS")** : M1 ReDoS pageActu, M2 nonce submitSearchArtiste, L1 print_r, L2 bouton, L3 `==`.

## Note
Reflété (pas stocké) → nécessite qu'une victime clique un lien forgé. Réel mais pas "site owned".
Corrections = édition thème/plugin → snapshots before/after + redéploiement, sur validation.
Complète OPTIM-08 (perf) qui touchait les mêmes fichiers (session, requêtes -1).
