Compétence de soumission de plugin WordPress.org
Faites passer n'importe quel plugin via le scan automatisé et la revue manuelle de wp.org sans
devoir le téléverser cinq fois. Tirée de cycles de revue réels (le genre où un simple
.gitkeep fait échouer tout le scan).
Mainteneur : moksa · moksaweb.com · Licence : MIT
Quand l'utiliser
Déclenchez cette compétence lorsque l'utilisateur demande :
- Empaqueter / construire un zip d'installation propre pour wordpress.org
- Soumettre ou resoumettre un plugin dans le répertoire wp.org
- Corriger les retours de l'équipe de revue wp.org (les 4 catégories classiques)
- Diagnostiquer un rejet du scan automatisé (fichiers cachés, préfixes, appels DB…)
- Mettre à jour la version d'un plugin de manière cohérente (en-tête + readme + constantes)
- Pré-vérifier un plugin avant téléversement
Ne PAS utiliser pour :
- Projets non-WordPress
- Plugins privés/internes qui n'iront jamais sur wp.org (les règles de revue ne s'appliquent pas, bien que les pratiques de sécurité soient toujours utiles)
Les deux portes
- Scan automatisé (Plugin Check, exécuté au téléversement) — blocages stricts. Doit être réussi pour même
atteindre un humain. Voir
references/automated-scan.md. - Revue manuelle (un humain, après la réussite du scan) — vous envoie des problèmes par email, vous corrigez + répondez.
Presque toujours l'une des 4 catégories de code (
references/review-categories.md) plus des directives de politique qu'aucun linter ne détecte — licence, code à distance, suivi, notifications d'administration, marques déposées, divulgation de service externe (references/guidelines.md).
Carte des références
| Sujet | Fichier |
|---|---|
| Plugin Check (PCP) manuel d'utilisation — UI/CLI, les 31 vérifications → correspondance correction, erreurs vs avertissements | references/plugin-check.md |
| Blocages stricts du scan automatisé (fichiers cachés, DB, appels interdits) | references/automated-scan.md |
| Les 4 catégories de revue manuelle + formulation de réponse | references/review-categories.md |
| Leçons de revues réelles (exemple→généralisation, nonce à 3 cas, phpcs:ignore≠escape, cast≠sanitize, chemins, préfixes de fork) | references/real-review-lessons.md |
| Préfixage de chaque globale (fonctions/options/hooks/handles/CPT/meta…) | references/prefixing.md |
Sanitize · validate · escape + $wpdb->prepare + signatures de webhook | references/sanitization-escaping.md |
| Enqueue JS/CSS (dépendances/version, en ligne, condition, blocs, pas de balises brutes) | references/enqueue.md |
| i18n (textdomain==slug, chaînes littérales, commentaires de traducteurs, JS) | references/i18n.md |
| Capacités et callbacks de permission (authz, REST, priv/nopriv) | references/capabilities.md |
| Directives de politique (licence, code à distance, suivi, notifications, marques, divulgation) | references/guidelines.md |
| Format readme.txt (en-têtes, balises≤5, description courte≤150, captures d'écran, services externes) | references/readme-txt.md |
| API Abilities · Client IA · MCP (authz par capacité, frontière de données, confirmation destructive, divulgation du fournisseur) | references/abilities-ai-mcp.md |
| Sécurité supplémentaire (fichiers/WP_Filesystem, téléversements, SSRF, appels interdits/obfusqués) | references/security-extras.md |
| Désinstallation et cycle de vie (nettoyage des données, ne pas supprimer le contenu utilisateur, multisite) | references/uninstall.md |
| Empaquetage du zip + cohérence version/en-tête/readme + .mo | references/packaging.md |
| Correspondance scan/PHPCS code → correction | data/plugin-check-errors.csv |
Liste de vérification rapide avant téléversement (copier-coller-vérifier)
SCAN AUTOMATISÉ (blocages stricts)
[ ] Aucun fichier caché dans le zip : `find . -name '.*' -not -name '.' -not -name '..'` est VIDE
(.gitkeep / .gitignore / .DS_Store / .editorconfig échouent tous le test "hidden_files")
[ ] Aucun node_modules/, .git/, tests/, scripts/, phpcs.xml, webpack.config.js dans le zip
[ ] Toutes les globales appartenant au plugin utilisent un préfixe unique >=4 caractères (pas de wp_ / __ / _ seul)
[ ] Pas de requêtes $wpdb directes sans prepare() ; pas d'exécution de code à distance ; pas de phone-home
REVUE MANUELLE (les 4 catégories)
[ ] Enqueue : pas de raw echo '<script>'/'<style>' ; wp_enqueue_* + wp_add_inline_*
[ ] Nonce + current_user_can sur chaque AJAX/admin_post qui modifie l'état ; les webhooks vérifient la
signature du fournisseur avec hash_equals avant utilisation (nonce WP impossible)
[ ] Chaque $_POST/$_GET nettoyé (wp_unslash + sanitize_*) ; json_decode -> map_deep ;
sortie échappée tardive (esc_html/esc_attr/esc_url/wp_kses)
[ ] textdomain == slug ; les arguments de __() sont des chaînes littérales ; /* translators: */ avant %s
POLITIQUE (revue manuelle, aucun linter ne les détecte — references/guidelines.md)
[ ] Licence compatible GPL indiquée + fichier LICENSE ; chaque bibliothèque/ressource incluse
est compatible GPL
[ ] Pas d'exécution de code à distance / pas de chargement de JS exécutable depuis un CDN ; inclure les
ressources localement
[ ] Tout appel API tiers divulgué dans le readme "== External services ==" (quelles données, quand, URLs)
[ ] Pas de collecte/suivi de données sans consentement explicite (désactivé par défaut) ; pas de lien
de crédit en front-end sauf consentement ; les notifications d'administration sont
fermables et limitées à l'écran (pas de spam de notification)
[ ] Le slug/nom ne commence pas par une marque déposée que vous ne possédez pas (woocommerce-… / google-…)
README.TXT (references/readme-txt.md — valider avec le validateur de readme wp.org)
[ ] L'en-tête se parse : Contributors=noms d'utilisateurs wp.org, Tags<=5, Stable tag = version livrée réelle
[ ] Description courte <=150 caractères, texte brut ; le Changelog a une entrée pour la version du
Stable tag
[ ] Les captures d'écran/bannière/icône vont dans SVN /assets/, PAS dans le zip
IA / ABILITIES / MCP (si le plugin enregistre des abilities, appelle un LLM, ou expose MCP)
[ ] Chaque wp_register_ability a un vrai permission_callback (cap au niveau objet), pas __return_true
[ ] Frontière de données : un outil ability/MCP retourne seulement ce que les capacités de l'utilisateur
actuel permettent
[ ] L'entrée du modèle/agent est traitée comme non fiable ; les abilities destructives sont
conditionnées par une confirmation
[ ] Le SDK LLM/IA est inclus (GPL) ; le fournisseur est divulgué dans == External services == ; les clés ne
sont jamais journalisées
[ ] Le point de terminaison MCP authentifie (App Password/OAuth) ET exécute le permission_callback de
chaque ability
SÉCURITÉ SUPPLÉMENTAIRE + CYCLE DE VIE
[ ] Pas de eval/shell/obfuscation/extract ; pas de var_dump/print_r/error_log laissés
[ ] Fichiers via WP_Filesystem uniquement dans uploads ; téléversements via wp_handle_upload + liste
blanche MIME
[ ] Appels distants via wp_safe_remote_* (SSRF : bloquer les plages internes) + timeout + vérification
de réponse
[ ] uninstall.php protégé par WP_UNINSTALL_PLUGIN ; supprime uniquement vos données préfixées ; jamais le
contenu utilisateur
PAQUET + MÉTADONNÉES
[ ] Version synchronisée : en-tête Version du fichier principal, constante de version, readme Stable tag,
Changelog
[ ] Les en-têtes Requires correspondent dans le fichier principal ET le readme.txt (WP / PHP / WC) — et toute
constante MIN_* d'exécution qui verrouille l'activation
[ ] vendor/ est `composer install --no-dev` (pas de squizlabs/phpunit/phpcompatibility)
[ ] Le nom du dossier racine du zip == slug du plugin
Flux de travail
- Lisez les règles de la tâche (voir la Carte des références ci-dessus) — commencez par
references/automated-scan.md+references/review-categories.md, puis l'analyse approfondie qui correspond au constat (prefixing.md/sanitization-escaping.md/enqueue.md/i18n.md) etreferences/packaging.mdpour la construction. - Construisez le zip propre — exécutez
scripts/build-zip.sh(gère git archive, supprime les fichiers de développement + TOUS les fichiers dotfiles, installevendor/--no-dev, compresse avec le slug comme racine). Ne zippez jamais manuellement l'arborescence de travail ; c'est ainsi que les déchets de développement et les dotfiles s'infiltrent. - Pré-vérifiez — exécutez
scripts/preflight.sh <chemin-vers-le-zip-ou-dossier>: analyse les fichiers cachés, vérifie la cohérence version/en-tête/readme, exécute PHPCS si configuré. Corrigez jusqu'à ce que ce soit propre. - Exécutez Plugin Check (PCP) — l'outil officiel qui exécute les vérifications automatisées du
répertoire localement.
wp plugin check <slug> --require=./wp-content/plugins/plugin-check/cli.php(ou Outils → Plugin Check). Corrigez chaque ERREUR, examinez chaque AVERTISSEMENT. Voirreferences/plugin-check.mdpour les 31 vérifications, les indicateurs et quel chapitre de la compétence corrige chacun. - Téléversez. Si le scan automatisé rejette → lisez le code exact, associez-le via
data/plugin-check-errors.csv, corrigez, reconstruisez, retéléversez. - Réponse à la revue manuelle — lorsque le relecteur envoie les problèmes par email, corrigez
chacun partout (les exemples sont un échantillon — greppez toute l'arborescence, exécutez Plugin Check
- PHPCS sur l'ensemble du plugin ; la même catégorie récurrente = rejet immédiat). Répondez ensuite
point par point en référençant ce qui a changé. Voir
references/review-categories.mdpour les corrections canoniques + la formulation de réponse etreferences/real-review-lessons.mdpour les commandes de balayage par catégorie et les pièges qui survivent à une première correction.
- PHPCS sur l'ensemble du plugin ; la même catégorie récurrente = rejet immédiat). Répondez ensuite
point par point en référençant ce qui a changé. Voir
Pièges durement acquis (les plus coûteux)
.gitkeepfait échouer le scan automatisé. C'est un fichier caché. Supprimez chaque dotfile du paquet ; retirez.gitkeepde git pour quegit archivene le réajoute jamais.- Décalage de version requise. wp.org lit l'en-tête du fichier principal ET le readme.txt ; une
constante
MIN_WC/MIN_PHPd'exécution peut verrouiller l'activation séparément. Les trois doivent concorder, sinon un utilisateur avec une version trop basse installe avec succès puis rencontre des fonctionnalités cassées. - Les webhooks ne peuvent pas utiliser les nonces WP. Les relecteurs signalent "pas de nonce" sur les
gestionnaires IPN — c'est un faux positif ; la réponse correcte est la vérification de la signature du
fournisseur avec
hash_equals(). Documentez-la avec unphpcs:ignorepar ligne + raison. json_decoden'est pas une validation. Utilisez toujoursmap_deep(sanitize_text_field)sur le résultat.composer install(sans--no-dev) fait fuiter phpcs/phpunit dans vendor/ → paquet gonflé, signalé. Faites toujours--no-devpour le zip livré.