PrestaFlow : tester une preprod derrière Cloudflare (WAF, Bot Fight)
Décor
Votre préproduction PrestaShop est derrière Cloudflare. WAF actif, protection anti-bots activée (Bot Fight Mode sur le plan Free, Super Bot Fight Mode sur les plans payants), éventuellement une règle « challenge » sur tout ce qui ressemble à un bot. C’est une configuration courante, et c’est très bien, sauf que vos scénarios PrestaFlow se prennent :
- des 403 (WAF Managed Rules) sur la home,
- des challenges anti-bots qui affichent une page d’attente Cloudflare à la place de la boutique,
- ou pire, des redirections silencieuses vers une page de captcha qui ne rend rien d’exploitable pour la régression visuelle.
Le scénario tombe sur une erreur qui n’a rien à voir avec l’application testée. Vous cherchez une heure avant de suspecter Cloudflare.
Cette annexe montre comment laisser passer PrestaFlow proprement, et explique pourquoi la solution qui vient d’abord à l’esprit (filtrer sur le User-Agent) est cassée pour de mauvaises raisons de sécurité.
La fausse bonne idée : http.user_agent eq "PrestaFlow"
Le premier réflexe, c’est de créer une règle WAF qui bypasse toutes les protections quand le User-Agent vaut PrestaFlow — par défaut, c’est ce que la lib envoie.
Expression: (http.user_agent eq "PrestaFlow")
Action: Skip → All remaining custom rules, Super Bot Fight Mode
Ça marche pour vos tests. Ça marche aussi pour n’importe quel bot qui pense à mettre PrestaFlow dans son User-Agent. Une seule ligne de curl suffit :
curl -A "PrestaFlow" https://preprod.example.com/admin-dev/
Vous venez d’ouvrir un couloir dans votre WAF que le premier crawler curieux peut trouver en itérant sur les UA connus. La documentation de PrestaFlow (page « Presets ») propose encore cette approche, à côté de l’usurpation d’un User-Agent de navigateur courant. Les deux reposent sur une valeur que n’importe qui peut envoyer. Préférez le secret partagé ci-dessous.
La bonne approche : header custom + secret partagé
Un vrai facteur d’authentification, c’est un secret aléatoire assez long pour ne pas être devinable. On le transporte dans un header HTTP arbitraire, et on demande à Cloudflare de skipper les protections uniquement quand le header correspond.
1. Générer un secret
openssl rand -hex 32
Vous obtenez une chaîne de 64 caractères hexadécimaux, soit 256 bits d’entropie. Impossible à deviner par force brute à l’échelle d’Internet.
Gardez-le sous la main pour les étapes 2 et 3 — c’est le même secret des deux côtés.
2. Créer la règle WAF Cloudflare
Direction Security → WAF → Custom rules → Create rule.
Expression :
(http.request.headers["x-ci-bypass"][0] eq "<votre_secret>")
Action : Skip, et cochez a minima :
- All remaining custom rules
- All rate limiting rules
- All managed rules
- All Super Bot Fight Mode rules (plans Pro et supérieurs)
- User Agent Blocking
- Browser Integrity Check
C’est ceinture et bretelles, mais c’est ce qui permet à vos scénarios de traverser sans qu’un des étages de protection ne se réveille au dernier moment sur une requête XHR.
3. Envoyer le header depuis vos scénarios
PrestaFlow lit une variable d’environnement PRESTAFLOW_EXTRA_HEADERS qui contient un objet JSON. Chaque paire clé/valeur est posée sur toutes les requêtes de la connexion navigateur — même mécanisme que PRESTAFLOW_BASIC_* de l’annexe sur htpasswd, mais générique.
PRESTAFLOW_EXTRA_HEADERS='{"X-CI-Bypass":"<votre_secret>"}'
Les guillemets simples autour du JSON le font lire tel quel par le chargeur de .env.
Rien à changer côté scénarios. Vos suites tournent comme si la boutique était accessible sans Cloudflare.
En CI : passer par des secrets
Standard GitHub Actions. Ajoutez CLOUDFLARE_BYPASS_TOKEN en secret du dépôt (Settings → Secrets and variables → Actions).
Le réflexe serait de passer PRESTAFLOW_EXTRA_HEADERS dans le bloc env: de l’étape qui lance PrestaFlow. Avec la version 1.7.1 de la lib, c’est un piège : elle lit cette variable dans $_ENV, et PHP est généralement configuré sans le E de variables_order (c’est le réglage du php.ini de production), si bien que $_ENV reste vide. Pire, la variable posée par env: apparaît dans $_SERVER, et le chargeur de .env (phpdotenv en mode immuable) refuse alors d’écraser une variable déjà définie. Résultat : l’en-tête n’est jamais envoyé, sans le moindre message, et Cloudflare continue de bloquer. Un correctif de la lib est en préparation. En attendant, la recette fiable consiste à écrire la variable dans le fichier .env, à la racine du projet, lors d’une étape préalable.
Deux points à connaître sur le chargement de ce fichier. La lib lit .env.local puis .env dans le répertoire courant, c’est-à-dire là où vous lancez prestaflow, et elle s’arrête au premier des deux qu’elle trouve : si un .env.local existe, votre .env est ignoré. Et la variable ne doit pas être définie par ailleurs dans l’environnement du job, sinon elle masque celle du fichier. Les autres variables PRESTAFLOW_* sont lues de la même façon : on les écrit donc au même endroit.
- name: Préparer le .env PrestaFlow
env:
CF_BYPASS: ${{ secrets.CLOUDFLARE_BYPASS_TOKEN }}
run: |
# La lib charge .env.local à la place de .env s'il existe : on s'en assure.
if [ -f .env.local ]; then
echo "::error::.env.local présent, le .env ne serait pas chargé"
exit 1
fi
{
echo "PRESTAFLOW_FO_URL=https://preprod.example.com/"
echo "PRESTAFLOW_BO_URL=https://preprod.example.com/admin-dev/"
printf "PRESTAFLOW_EXTRA_HEADERS='{\"X-CI-Bypass\":\"%s\"}'\n" "$CF_BYPASS"
} >> .env
- name: Run PrestaFlow (preprod protégée par Cloudflare)
run: composer prestaflow -- run ./tests/prestaflow
Le secret ne transite que par la variable CF_BYPASS de la première étape, et le .env ainsi produit disparaît avec le runner. Si votre dépôt versionne déjà un .env, l’ajout en fin de fichier (>>) suffit : quand une clé apparaît deux fois, c’est la dernière valeur qui l’emporte.
Le cache : à traiter séparément
Même avec le WAF passé, un cache Cloudflare agressif peut vous rendre des pages figées — vos assertions passent alors que la boutique est cassée en réalité, ou l’inverse.
Deux options :
- Désactiver le cache sur les URL testées — via une Page Rule ou une Cache Rule qui matche votre preprod, action « Bypass cache ». Le plus propre, si la preprod est un domaine dédié.
- Purger le cache avant chaque run — via l’API Cloudflare, une étape avant
prestaflow run. Utile si vous partagez le domaine entre preprod et prod (ce que vous ne devriez pas faire, mais ça arrive).
Le choix dépend de votre setup — la seule règle universelle : ne laissez pas Cloudflare cacher des pages que vos tests visent, sinon vous testez le cache, pas l’application.
Combiner les couches d’auth
Le bypass Cloudflare n’est qu’une des couches d’accès que PrestaFlow sait gérer. Elles se cumulent :
- Header Cloudflare (cette annexe) — l’accès au domaine protégé par le WAF
- HTTP Basic (htpasswd) — l’accès au domaine protégé par Basic Auth
- Cookies pré-injectés (
PRESTAFLOW_COOKIES) — la session BO déjà ouverte login()dans unit— le login programmatique classique
Ordre dans lequel PrestaFlow les applique :
before()pose les headers de connexion (Authorization: Basic …, puis ceux dePRESTAFLOW_EXTRA_HEADERScommeX-CI-Bypass) avant toute navigationbefore()pose ensuite les cookies avant toute navigation- Vos scénarios peuvent choisir de faire un
login()ou pas
Les couches cohabitent. Cas pratique : une preprod protégée par Cloudflare et par un htpasswd, avec un cookie de session admin déjà valide → vos suites vont directement à l’action métier, sans challenge Cloudflare, sans fenêtre d’authentification Basic, sans formulaire de login.
Rotation du secret
Contrairement aux cookies de session PS qui expirent, le secret Cloudflare est stable — vous le rotez à la demande, pas sur un cycle.
Le geste :
- Générez un nouveau secret (
openssl rand -hex 32). - Éditez la règle WAF Cloudflare, remplacez la valeur.
- Mettez à jour le secret GitHub (
gh secret set CLOUDFLARE_BYPASS_TOKEN --repo owner/repo).
Entre les étapes 2 et 3, il y a une courte fenêtre où l’ancien secret ne marche plus et où le nouveau n’est pas encore en place. Un run qui tombe dedans échoue : relancez-le une fois le secret GitHub à jour.
Quand roter ? Sur suspicion de fuite (contributeur qui part, secret aperçu dans une capture, etc.), et par hygiène tous les 6-12 mois.
Notes
Dans la Série PrestaFlow — article 10 sur 23