Skip to main content
Retour aux billets
Oct. 20268 min de lecture

Possessed : votre vérificateur de liens est au vert, et huit de ces liens ouvrent maintenant un casino

Un vérificateur de liens pose une seule question : le serveur a-t-il répondu? Quand un domaine expire et que quelqu'un l'achète, la réponse est encore 200. Possessed est une GitHub Action où Gemma 4 lit chaque lien approuvé contre la capture de l'Internet Archive du jour de son ajout, et redirige les liens possédés vers l'original. Sur les 1 000 billets les plus aimés de DEV, il en a trouvé 183.

HacktoberfestDEV ChallengeGemma 4EmbeddingGemmaOllamaGitHub ActionsInternet ArchiveOpen SourceHackathon
J'ai écrit ce billet pour le DEV Hacktoberfest Open-Source AI Challenge, semaine 4 : Ghost in the Machine (octobre 2026), et j'ai bâti Possessed pour la même soumission. Code source sur github.com/JonathanSolvesProblems/possessed, sous licence MIT, avec des modèles à poids ouverts par Ollama, sans clé d'API. #hf26challenge
La revendication en une phrase : sur design-resources-for-developers (67 000 étoiles), un vérificateur de codes d'état passe chaque lien, alors que 8 d'entre eux ouvrent aujourd'hui des sites de jeux d'argent, ce que n'importe qui peut vérifier contre la capture de l'Internet Archive liée dans chaque ligne de la demande de tirage; et sur les 1 000 billets les plus aimés de DEV, 183 liens dans 96 billets ouvrent autre chose que ce que l'auteur citait, comptés seulement là où Gemma 4 et un juge Qwen à l'aveugle s'entendent ou là où une vérification manuelle confirme. Les chiffres de précision plus bas sont mesurés contre des étiquettes qui viennent de ces deux modèles et de mes propres vérifications, donc ils sont en partie autocorrigés et le rappel ne peut pas être mesuré; aucune des deux demandes de tirage n'a encore été fusionnée.
Carte titre sur un fond sombre parsemé de petits 200 OK : une photo de profil ronde au-dessus du mot Possessed et de la ligne Your link checker is green. Some of those links now open a casino.
Possessed : une GitHub Action où Gemma 4 lit les liens que votre vérificateur de codes d'état a déjà approuvés.

Un vérificateur de liens pose une seule question

design-resources-for-developers est une liste de liens qui compte 67 000 étoiles. Elle a un vérificateur de liens, et il passe. Ouvrez la section React UI Libraries, cliquez sur Chakra Templates, et vous tombez sur une page en indonésien sur les machines à sous qui paient. Le domaine a expiré, quelqu'un l'a acheté, et le serveur répond 200, la seule question que pose un vérificateur de liens. Huit entrées de cette liste ouvrent aujourd'hui des sites de jeux d'argent. Vingt-deux autres ouvrent une page de domaine à vendre, un moulin à dissertations, un atelier d'esthétique automobile ou une autre entreprise sans rapport avec les mots à côté du lien. Aucun n'échoue au vérificateur.

Côte à côte pour chakra-templates.dev : à gauche, la capture de l'Internet Archive du 3 juin 2022 qui montre Production-ready Chakra UI Templates for developers; à droite, la page d'aujourd'hui à mainslotresmi.org, une vitrine de machines à sous AGEN338.
Chakra Templates sur une liste de 67 000 étoiles, archivé en juin 2022. Le même lien ouvre aujourd'hui AGEN338, un site de machines à sous.
Un terminal qui exécute link-check sur readme.md : neuf URL, chacune avec un crochet vert et le statut 200, qui se termine par 9/9 links OK. All checks passed.
Ce que voit un vérificateur de codes d'état : 200 pour chaque lien possédé, toutes les vérifications réussies.

Gemma lit la page, puis l'archive

Possessed passe après votre vérificateur de liens. Pour chaque lien approuvé, Gemma 4 ouvre ce que le lien affiche aujourd'hui et la capture de l'Internet Archive du jour où le lien a été ajouté (git log -S trouve ce commit), lit les deux à côté de la phrase qui cite le lien, et rend l'un de quatre verdicts : correct, déménagé, possédé, mort. Gemma choisit ses propres preuves. Il a fetch_live, find_snapshot, list_snapshots et read_snapshot, et quatre outils de verdict. Un lien possédé lui prend habituellement trois ou quatre étapes : ouvrir la page en ligne, trouver la capture la plus proche du jour d'ajout, la lire, décider. Quand la capture la plus proche est déjà la page parquée, il liste les captures et remonte plus loin. Le lien Chakra Templates a pris quatre appels d'outils et 9,5 secondes sur le GPU de mon portable.

Un terminal qui exécute possessed.repo sur design-resources-for-developers : 1245 liens, le vérificateur en passe 1114, et les appels d'outils de Gemma pour Chakra Templates (fetch_live qui aboutit à mainslotresmi.org, find_snapshot, read_snapshot, puis possessed avec le remplacement archivé), à côté de vignettes de la capture de 2022 et du site de machines à sous d'aujourd'hui.
Gemma 4 choisit ses propres preuves : la page en ligne, puis la capture de l'Internet Archive du moment où le lien a été ajouté.

Rien dans la demande de tirage n'est deviné

Un verdict qui propose un remplacement est refusé par le code si ce remplacement n'est pas une page que Gemma a ouverte pendant la session : une capture d'archive qu'il a lue, ou une URL en ligne qu'un vérificateur de liens passerait. Le refus retourne au modèle comme une erreur d'outil qui dit quoi faire à la place. La barrière a commencé comme une consigne, « n'invente jamais d'URL », et est devenue vingt lignes dans tools.py, parce qu'une règle qui a un test objectif a sa place dans le code. Sur 597 jugements dans cinq listes, elle s'est déclenchée 18 fois, et les 18 étaient mes bogues plutôt que ceux de Gemma : sept venaient d'une barre oblique finale que j'avais stockée autrement que le modèle réécrivait l'URL, onze de captures rendues en JavaScript que ma règle prenait pour des pages vides. Pas une seule fois Gemma n'a proposé une URL qu'il n'avait pas ouverte. J'ai corrigé les deux bogues, gardé la barrière et les traces, et rejugé la grande liste avec la barrière corrigée : zéro refus.

Un terminal qui exécute le test de la barrière : d'abord, un verdict possessed avec une URL d'archive que le modèle n'a jamais ouverte, marqué REFUSED par la barrière avec l'erreur d'outil renvoyée; puis le même verdict après que find_snapshot et read_snapshot ont ouvert la capture de juin 2022, qui passe. À côté, la capture ouverte.
La barrière dans le code : un remplacement que Gemma n'a jamais ouvert est refusé. Après qu'il a lu la capture, le même verdict passe.

Deux demandes de tirage que vous pouvez ouvrir

L'Action en a écrit une toute seule, sur un fork de design-resources-for-developers, à partir d'un exécuteur GitHub hébergé sans GPU : sept liens, dont six sites de jeux d'argent, chacun redirigé vers la capture d'archive du mois de son ajout. Sur le processeur, Gemma lit un lien en deux à trois minutes, alors l'exécution a pris 30 minutes au total sur une liste de 1 245 liens, dont dix-huit où Gemma lisait huit liens, et l'Action a un paramètre limit qui prend d'abord les signaux les plus forts. L'autre est allée à un responsable, sur vuejs/awesome-vue : six entrées, dont une listée en 2018 comme une PWA d'échange de jeux vidéo usagés qui ouvre aujourd'hui un guide coréen des sites de baccara. Les six ont survécu au nettoyage des liens morts de la liste en janvier, parce qu'elles répondent encore 200. Pour celle-là, j'ai utilisé le mode confirmé, qui n'applique que les corrections sur lesquelles les deux modèles s'entendent.

La demande de tirage GitHub n° 1, Replace links that now open something else, ouverte par github-actions[bot], avec un tableau Possessed links qui liste sept liens, la date d'ajout de chacun, ce qu'il ouvre aujourd'hui (sites de jeux d'argent, une page de domaine à vendre) et ce qu'il était, chacun marqué possessed.
La demande de tirage que l'Action a ouverte sur un exécuteur hébergé sans GPU : sept liens, dont six sites de jeux d'argent.
La demande de tirage n° 4593 de vuejs/awesome-vue, Remove six entries whose links now open something else, avec un tableau de six liens, ce que chacun ouvre aujourd'hui et ce qu'il était, chacun retiré avec un lien vers l'original archivé, et une note indiquant que chaque ligne a été vérifiée par Gemma 4 et par Qwen 3.5 4B.
vuejs/awesome-vue n° 4593 : six entrées qui ont survécu au nettoyage des liens morts de la liste, retirées avec preuves d'archive.

Ce que chaque modèle a ajouté

Quatre détecteurs ont reçu la même question sur les mêmes 188 liens de design-resources : ce lien a-t-il besoin d'une correction? Un vérificateur de codes d'état n'en signale aucun, par construction. Les heuristiques d'URL (un autre domaine, une page d'accueil, des mots de parking) en ont signalé 61 et avaient raison pour 29, une précision de 0,47. La similarité EmbeddingGemma seule en a signalé 30 et avait raison pour 22, 0,73. Gemma 4 qui lit la page en a signalé 67 et avait raison pour 49, 0,73, et sur le seul verdict possédé, il avait raison pour 30 sur 37, là où les heuristiques avaient raison pour 19 sur 61. C'est aussi EmbeddingGemma qui rend la lecture abordable : sur design-resources, il a ramené 1 114 liens approuvés à 200 lectures.

Puis je l'ai pointé sur DEV

J'ai pris les 1 000 billets les plus aimés de DEV et extrait leurs 13 788 liens uniques; un vérificateur de codes d'état en passe 11 119 comme pages lisibles. Le triage a retenu 2 951 candidats, plus 150 témoins au hasard parmi les liens qu'il n'avait pas signalés, et Gemma 4 a jugé les 3 101 pendant la nuit sur mon portable. 145 des 150 témoins étaient corrects, alors le triage ne cache pas grand-chose. Qwen a rejugé chaque lien signalé, à l'aveugle. Là où les deux modèles s'entendent indépendamment et où les vérifications manuelles le confirment, 183 liens dans 96 billets, totalisant 95 627 réactions, ouvrent maintenant autre chose que ce que l'auteur citait. Une collection de ressources React à 3 438 réactions pointe vers js.coach, aujourd'hui un répertoire suédois de casinos. Une liste de livres de programmation à 3 135 réactions sert maintenant une page de loterie indonésienne.

Carte de résultats intitulée DEV's 1,000 most-loved posts, links that pass a status-code checker but open something else : 183 liens, 96 billets, 95 627 réactions, avec une note indiquant que Gemma 4 a jugé chaque candidat et que Qwen 3.5 a rejugé à l'aveugle, comptés seulement là où les deux s'entendent ou là où une vérification manuelle confirme.
Les 1 000 billets les plus aimés de DEV : 183 liens dans 96 billets, totalisant 95 627 réactions, ouvrent maintenant autre chose.

Ce qui n'est pas revendiqué

Les chiffres de précision sont mesurés contre des étiquettes qui viennent des cas où Gemma et Qwen s'entendent, plus des vérifications manuelles quand ils ne s'entendent pas, et c'est Claude Code qui a écrit le premier jet de ces étiquettes manuelles (les fichiers d'étiquettes le disent), donc l'examen est en partie autocorrigé. Un lien que les deux modèles ont raté n'est pas dans l'ensemble, alors le rappel ne peut pas être mesuré ainsi, et je ne le revendique pas. L'accord n'est pas non plus la vérité : en vérifiant les premières lignes de DEV avec de nouvelles requêtes, j'ai trouvé les deux modèles dans l'erreur ensemble sur sept liens d'un même blogue dont les articles se chargent encore derrière une redirection de ressources, et ces lignes sont corrigées à la main; les 399 liens que Gemma a signalés sans confirmation de Qwen ne sont pas comptés du tout. Gemma fait des erreurs connues : un profil GitHub crédité comme auteur d'un projet lui paraît possédé, et une bannière promotionnelle a été lue comme une prise de contrôle. L'Action seule applique les verdicts de Gemma, c'est pourquoi chaque ligne porte ses preuves, et c'est le mode confirmé que j'ai utilisé pour la liste d'un responsable. Les pages derrière un mur anti-robots et les coquilles JavaScript vides ne peuvent pas être lues sans navigateur (42 des 1 114 sur design-resources), et Possessed les compte au lieu de les juger. Il ne peut pas vous dire qu'une page est bonne, seulement qu'elle correspond encore à ce que l'archive a vu quand vous l'avez liée. Aucune des deux demandes de tirage n'a été fusionnée, et design-resources-for-developers a suspendu les contributions externes, alors ses corrections vivent sur un fork.

Projet associé

Possessed: a GitHub Action where Gemma 4 reads the links your status-code checker already passed, and points the ones that now open a casino back at the Internet Archive's original

Voir le projet