Skip to content

Sécurité : bloquer les liens javascript: via autoliens et URL entre chevrons - #46

Merged
jmcollin merged 1 commit into
mainfrom
fix/block-unsafe-link-schemes
Oct 8, 2026
Merged

jmcollin merged 1 commit into
mainfrom
fix/block-unsafe-link-schemes

Conversation

@jmcollin

@jmcollin jmcollin commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Problème : deux XSS en mode par défaut (sans allowRawHtml)

Je les ai trouvés en travaillant sur les liens.

Entrée Sortie sur main
[a](``&lt; javascript:alert(1)&gt;``) <a href=" javascript:alert(1)">
[a](``&lt; javascript:alert(1)&gt;``) '&lt;img src=" javascript:alert(1)"&gt;'
<javascript:alert(1)> <a href="javascript:alert(1)">
<vbscript:…>, <data:text/html;base64,…> lien cliquable
  • URL entre chevrons : isSafeAngleBracketUrl() vérifiait le schéma sans retirer les espaces. parse_url(" javascript:…") ne voit aucun schéma, donc l'URL passait. Or les navigateurs retirent les espaces de début et de fin d'un href : le script s'exécute au clic.
  • Autoliens : aucun contrôle de schéma. Un test unitaire figeait même javascript: comme autorisé, avec le commentaire « les consommateurs filtrent eux-mêmes ». C'est incohérent avec [x](javascript:…), qui est déjà bloqué.

Correctif

  • Le schéma d'une URL entre chevrons est évalué sur sa forme sans espaces de début et de fin. Les URL avec espaces internes (</my uri>) fonctionnent toujours.
  • Les autoliens dont le schéma est javascript, vbscript ou data sont rendus en texte littéral échappé. Les autres schémas (ftp:, irc:…) restent des liens, comme le veut CommonMark §6.9.

Tests

  • Nouveau tests/Security/LinkSchemeBypassTest.php : 6 vecteurs, qui échouent tous sur main, plus 2 cas légitimes qui doivent continuer à marcher.
  • AutolinkTest : le test qui exigeait que <javascript:…> devienne un lien est remplacé par un test à 4 cas qui vérifie le rejet.
  • PHPUnit OK (863 tests), PHPStan et Psalm sans erreur.

À merger en priorité. La PR sur les liens inline, qui arrive ensuite, s'appuie dessus.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CPcuaaxrCottkeL1ykoSWe


Generated by Claude Code

… links

Two XSS vectors reached the output in the default (escaped) mode:

- Angle-bracket destinations were checked before trimming, so
  [a](< javascript:alert(1)>) produced href=" javascript:alert(1)";
  browsers strip the leading space and run the script on click. The
  check now runs on the trimmed URL (links and images).
- URL autolinks were never scheme-checked: <javascript:alert(1)>,
  <vbscript:…> and <data:…> became clickable links. They are now
  rendered as literal text; other schemes (ftp:, irc:, …) still link, as
  CommonMark requires.

The unit test that pinned javascript: autolinks as allowed is replaced
by one asserting they are rejected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPcuaaxrCottkeL1ykoSWe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants