Réflexion

Quand le plugin meurt : la dépendance qu'aucun audit ne voit

Huit galeries vidéo disparues du jour au lendemain. Le compte du service tiers appartenait à une société dissoute. Anatomie d'une dépendance invisible et de sa reconstruction.

Un matin de septembre, huit galeries vidéo ont disparu d'un site que je maintiens. Pas de panne serveur, pas d'erreur PHP, pas de mise à jour hasardeuse. Un service tiers avait simplement décidé que le compte du client n'existait plus. Et il avait raison : la société qui l'avait ouvert n'existait plus non plus.

Le site est celui de Capsule 12, une agence vidéo de Wavre. Ses réalisations s'affichaient dans des galeries fournies par Elfsight, un service de widgets prêts à l'emploi. Le genre de brique qu'on installe en trois minutes et qu'on oublie pendant trois ans.

La console racontait tout en une ligne :

eapps.Platform throws: "Widget "353c6241-f8be-412b-b9ab-e792f52a9a88"
can`t be initialized because WIDGET_DISABLED"

Huit widgets, huit fois le même message. Le serveur d'Elfsight répondait, le script se chargeait correctement, mais il refusait de servir le contenu. Interrogé directement, leur point d'entrée était sans ambiguïté :

{
  "status": 1,
  "data": {
    "widgets": {
      "353c6241-f8be-412b-b9ab-e792f52a9a88": {
        "status": 0,
        "reason": "WIDGET_DISABLED"
      }
    }
  }
}

1. Ce qu'on achète vraiment avec un plugin SaaS

Un widget hébergé, ce n'est pas du code qu'on installe. C'est un abonnement à un affichage. La nuance paraît théorique jusqu'au jour où elle devient le problème.

Sur ce site, les galeries tenaient dans quatre fichiers de 139 octets :

<script src="https://apps.elfsight.com/p/platform.js" defer></script>
<div class="elfsight-app-353c6241-f8be-412b-b9ab-e792f52a9a88"></div>

Deux lignes. Aucune donnée sur le serveur du client. Ni la liste des vidéos, ni l'ordre d'affichage, ni le nombre de colonnes, ni le comportement au clic. Tout vivait chez Elfsight, derrière un identifiant.

Quand le compte meurt, il ne reste rien à récupérer. J'ai essayé les trois voies possibles :

SourceCe que j'espéraisRésultat
API ElfsightLa configuration du widgetWIDGET_DISABLED
CDN ElfsightLe code du widget, à rétro-analyserHTTP 522
Wayback MachineUne capture de la page rendueAucune capture

Trois portes, trois murs. Le rendu d'origine a finalement été reconstitué à partir d'une capture d'écran que le client avait prise avant la panne. C'est dire le niveau de récupération : une image, en guise de spécification.

2. Le compte n'était pas le nôtre

Voilà le vrai sujet. Le compte Elfsight avait été ouvert par une société prestataire, dissoute depuis. Ni le client, ni moi n'avions la main dessus. Pas de mot de passe oublié, pas de procédure de récupération : l'entité juridique propriétaire du compte n'existait plus.

C'est une dépendance qu'aucun audit technique ne détecte. Le code est propre, le site fonctionne, les performances sont bonnes. Rien ne signale que huit blocs de contenu dépendent d'un compte que personne dans la chaîne ne contrôle.

La question à poser avant d'installer un service tiers n'est pas « est-ce que ça marche ? ». C'est « à quel nom est le compte, et que se passe-t-il le jour où ce nom disparaît ? ».

3. Reconstruire : trois voies, une seule tenable

Les vidéos étaient toutes sur le compte Vimeo du client, rangées dans quatre albums. La source de données existait donc, intacte. Restait à choisir comment y accéder.

L'API publique : séduisante, piégée

Vimeo expose une API v2 qui ne demande aucune authentification. Un appel, une liste de vidéos, pas de jeton à gérer. Tentant.

Deux tests l'ont écartée. D'abord la pagination :

page 1 : HTTP 200, 20 vidéos
page 2 : HTTP 200, 20 vidéos
page 3 : HTTP 200, 20 vidéos
page 4 : HTTP 403
page 5 : HTTP 403

Plafond dur à 60 vidéos par album. L'album principal en contient 100 : 42 vidéos seraient restées invisibles, sans le moindre message d'erreur. Un bug qui ne se voit pas, sur un site vitrine d'agence vidéo.

Ensuite, le comportement sur identifiant invalide. J'ai interrogé un album inexistant :

curl "https://vimeo.com/api/v2/album/9999999/videos.json"
→ HTTP 200, 2 vidéos
→ propriétaire : "Presley Media"

Pas une erreur : les vidéos d'un autre compte. Une faute de frappe dans un identifiant, et le site d'un client affiche le catalogue d'un inconnu. Vimeo documente d'ailleurs cette API comme non maintenue.

oEmbed : le bon outil, le mauvais usage

C'est la voie que Vimeo recommande en remplacement. Elle fonctionne parfaitement pour intégrer une vidéo précise, mais ne sait pas lister le contenu d'un album. Interrogée sur l'URL d'un album, elle le décrit comme un embed unique, pas comme une collection. Écartée.

L'API officielle : un jeton, mais le bon

L'API v3 exige une authentification. C'est exactement ce qu'on voulait éviter au départ, et c'est pourtant la seule option défendable, pour une raison qui n'a rien de technique : le jeton se crée depuis le compte Vimeo du client.

Une application déclarée sur son compte, en portée lecture seule sur du contenu déjà public. Le jeton lui appartient. S'il change de prestataire demain, il garde la main. Le scénario Elfsight devient structurellement impossible.

Résultat au premier appel :

album 8716475 : 100 vidéos sur 100, en un seul appel
vignettes disponibles : 100, 200, 295, 640, 960, 1280, 1920 px

4. Le module : 542 lignes contre deux

Les deux lignes d'Elfsight sont devenues un fichier de 542 lignes dans le thème. C'est le prix de l'autonomie, et il reste modeste au regard de ce qu'on récupère.

Le module fait trois choses qu'un widget hébergé ne fait pas :

  • Il met en cache - six heures en transient, plus un miroir permanent en base. Si l'API Vimeo tombe, le site continue d'afficher le dernier contenu connu au lieu d'une page vide.
  • Il se mĂ©fie de sa source - si le nombre de vidĂ©os chute sous le tiers de l'effectif connu, il conserve l'ancien contenu. C'est la parade directe Ă  l'Ă©chec silencieux constatĂ© sur l'API publique.
  • Il rend cĂ´tĂ© serveur - le HTML contient les vignettes dès le premier octet. Google voit les vidĂ©os, ce qui n'Ă©tait pas le cas avec Elfsight qui injectait tout en JavaScript après coup.

Ce dernier point mérite qu'on s'y arrête. Pendant des années, le contenu le plus valorisant de ce site - cent réalisations vidéo - était strictement invisible pour les moteurs de recherche. Le remplacement d'un plugin défaillant a réglé au passage un problème de référencement que personne n'avait identifié comme tel.

5. Le piège du remplaçant

Module déployé, galeries fonctionnelles, tests au vert. Puis un clic sur une vignette, et ceci :

Veuillez accepter le consentement des cookies

CookieYes, le gestionnaire de bandeau cookies du site, bloquait le lecteur Vimeo. Un bandeau noir en plein milieu de la grille, sans bouton, sans issue. Le visiteur clique sur une vidéo, il obtient un mur.

En inspectant sa configuration, le mécanisme apparaît :

_providersToBlock : [
  { url: "vimeo.com", categories: ["analytics"] },
  ...
]

Vimeo était classé en catégorie analytics à cause d'un cookie de suivi, vuid. Sauf que le lecteur était appelé avec le paramètre dnt=1, le mode « ne pas suivre » de Vimeo. J'ai mesuré ce qu'il déposait réellement :

CookieSans dntAvec dnt=1
vuid (suivi)présentabsent
playerprésentabsent
COOKIE_ID_PICOX_IDprésentabsent
__cf_bm (technique)présentprésent

Un seul cookie subsiste, celui de protection anti-robots de Cloudflare, exempté de consentement. Le blocage n'avait plus d'objet : CookieYes protégeait le visiteur d'un traceur qui n'existait plus.

Deux contournements techniques fonctionnaient. L'attribut data-cky-tag, et surtout l'iframe imbriquée en srcdoc, qui passait sous le radar de leur détecteur. Je ne les ai pas retenus. Contourner un outil de conformité, c'est mettre le client en porte-à-faux le jour d'un contrôle, pour économiser cinq minutes de configuration.

La correction propre tenait en une ligne : retirer vuid de la déclaration, sur le compte CookieYes du client. Déclarer un cookie qui n'est plus déposé fausse la déclaration autant que d'en omettre un.

6. Ce que ça coûte, ce que ça rapporte

Trois heures de travail, diagnostic et tests compris. En face, un abonnement Elfsight à 260 euros par an, désormais inutile.

D'un côté une facture unique, de l'autre un loyer qui court. Le développement est amorti en neuf mois ; au-delà, le client garde les 260 euros dans sa poche chaque année, sans rien à renouveler.

Mais l'économie n'est pas l'essentiel. Ce qui change vraiment, c'est la structure de la dépendance :

AvantAprès
Propriétaire du compteSociété tierce dissouteLe client
Données du siteChez le prestataireSur le serveur du client
Si le service tombePage videDernier contenu connu
Visible par GoogleNonOui
Coût récurrent260 euros/an0

Et pour le client, rien ne change au quotidien : il continue de déposer ses vidéos dans l'album Vimeo correspondant, elles apparaissent sur le site dans les six heures. C'était la contrainte principale, elle est tenue.

Ce que j'en retiens

  • Un plugin SaaS n'est pas une brique, c'est un locataire. Il occupe une place dans votre page sans jamais vous appartenir. Tant qu'il paie son loyer, tout va bien.
  • La dĂ©pendance dangereuse n'est pas technique, elle est juridique. Ce n'est pas le service qui a lâchĂ©, c'est la sociĂ©tĂ© qui dĂ©tenait le compte. Aucun audit de code ne dĂ©tecte ça.
  • Le jeton du client vaut mieux que pas de jeton du tout. L'API sans authentification paraissait plus simple ; elle amputait le catalogue de 42 vidĂ©os et pouvait afficher le contenu d'un inconnu.
  • Un remplacement bien fait rĂ©pare plus que la panne. Cent vidĂ©os sont devenues visibles par Google au passage, sans que ce soit l'objectif.
  • Contourner un outil de conformitĂ© n'est jamais une solution. Ça fonctionne, c'est rapide, et ça reporte le risque sur le client.

Trois heures pour remplacer deux lignes de code. Dit comme ça, c'est cher. Mais ces deux lignes étaient le seul lien entre un site vitrine et cent réalisations, et ce lien appartenait à une société morte.

La vraie question n'est pas ce qu'un plugin vous fait gagner à l'installation. C'est ce qu'il vous coûte le jour où il s'arrête, et qui décide de ce jour-là.