wp_kses() stoi za wp_kses_post(), czyszczeniem komentarzy, tekstem widgetów i każdym wywołaniem wp_kses( $html, $allowed ) w Twojej wtyczce. W trunku działa teraz na HTML API zamiast na wyrażeniach regularnych. Według raportu Dennisa Snella zmiana ma trafić do WordPressa 7.2 albo 7.3, zależnie od wyników testów. Poniżej: co zmienia się w wyniku, jak znaleźć własny kod, którego to dotyczy, i do czego służy tymczasowy filtr wyłączający zmianę.
Co dokładnie zmienia się w wp_kses()?
Stara implementacja rozbierała znaczniki wyrażeniami regularnymi. Nowa czyta HTML tokenizerem z HTML API, dla każdego znacznika i atrybutu sprawdza, czy jest dozwolony, a potem zapisuje wynik od nowa. Pull request nosi tytuł “KSES: Reimplement with Tag Processor” (PR 13271) i jest powiązany z biletem Trac 66208. Według opisu PR nowy kod siedzi w funkcji wp_sanitize_html_kses().
Intencja z raportu jest jasna: wp_kses() nie ma być gorsze niż było. Raport mówi też, że wtyczki i motywy nie powinny wymagać zmian. Oba zdania mogą być prawdziwe, a Twoje testy i tak mogą zaświecić się na czerwono, bo “tak samo bezpiecznie” nie znaczy “te same bajty”. Ponowna serializacja to sedno zmiany, a ona zmienia stringi.
Źródło: Progress Report: wp_kses(), 7 października 2026.
Które dane wejściowe dają inny wynik?
Raport podaje pary przed i po. Poniższy blok jest na nich wzorowany (moja tablica dozwolonych znaczników, wejścia z raportu). Zanim zaufasz mojemu przepisaniu, odpal je na własnym buildzie trunk.
<?php
// Modelled on the examples in the Make Core progress report.
$allowed = array(
'a' => array( 'href' => true ),
'code' => true,
'img' => array( 'src' => true, 'class' => true ),
);
wp_kses( 'Click on my <script>alert(1)</script>!', $allowed );
// legacy: Click on my alert(1)!
// new: Click on my !
wp_kses( 'The <IMG class=bike class="boat" src=\'vehicle.png\' /> & the wheels…', $allowed );
// legacy: The <IMG class="bike" src="vehicle.png" /> & the wheels…
// new: The <img class="bike" src="vehicle.png"> & the wheels…
wp_kses( 'Add <code>/?preview=true§ion=grilling</code>.', $allowed );
// legacy: Add <code>/?preview=true&section=grilling</code>.
// new: Add <code>/?preview=true§ion=grilling</code>.
wp_kses( 'Click on the <butto', $allowed );
// legacy: Click on the <butto
// new: Click on the (trailing space, nothing else)Raport grupuje różnice tak:
- Normalizacja. Nazwy znaczników trafiają do małych liter, atrybuty dostają podwójne cudzysłowy, powtórzony atrybut zostaje tylko pierwszy, a odwołania do znaków są zapisywane od nowa.
- Luźne nawiasy kątowe. Tekst typu
<$500albo>5yobył wcześniej połykany. Teraz jest zamieniany na<i>, więc tekst zostaje. - Niejednoznaczny ampersand. Nazwane odwołanie bez średnika, na przykład
§ionw adresie URL w węźle tekstowym, jest dekodowane tak jak w przeglądarce. Stąd w trzecim przykładzie wychodzi znak paragrafu. - Script i style. Gdy te elementy nie są dozwolone, ich zawartość znika razem ze znacznikami. Stary kod zostawiał zawartość jako widoczny tekst.
- Ucięty koniec danych. Niedokończony ostatni znacznik jest odrzucany, nie escapowany.
- Elementy specjalne. Tekst podobny do znacznika wewnątrz dozwolonego
<title>jest escapowany, a ten sam tekst wewnątrz<div>znika razem z nieznanym znacznikiem. - SVG i MathML. Treść wymagająca złożonego parsowania powoduje, że funkcja przerywa i zwraca tylko to, co było przed tym miejscem. Raport pokazuje
<li>z MathML i zagnieżdżonym<span>, ucięty w tym punkcie. - Twarde limity. Część zniekształconych komentarzy,
NOSCRIPTi mylące elementy są teraz odrzucane. - Niezbilansowane znaczniki. Zostają bez zmian, bo istniejący kod na tym polega.
W polskim sklepie na WooCommerce typowi kandydaci do sprawdzenia to opisy produktów z wymiarami w rodzaju “<5 cm” albo “temperatura >60 stopni”, wklejone z arkusza dostawcy, oraz zaimportowane z hurtowni opisy z dużymi literami w znacznikach. Pierwsze wcześniej traciły fragment tekstu, drugie zmienią zapis po ponownej serializacji. To domysł o tym, gdzie szukać, nie wynik pomiaru.
Nie potwierdziłem w przeczytanych źródłach, jak zachowuje się <script>, gdy wprost dopuścisz go w tablicy. Sprawdź ten przypadek sam, nie zakładaj żadnego wyniku.
Czy moja własna tablica allowed html nadal działa?
Większość tak, bo logika listy dozwolonych jest ta sama: znacznik lub atrybut jest w tablicy albo go nie ma. Zmienia się to, co wychodzi dookoła. Warto spojrzeć na trzy wzorce.
- Tablice dopuszczające inline
scriptlubstyle. Shortcode, który drukuje skrypt w treści i przepuszcza go przezwp_kses()z dopuszczonymscript, leży dokładnie w obszarze, którego dotyka raport. Porównaj stary i nowy wynik. Zastanów się też, czy skrypt nie powinien iść przezwp_add_inline_script(), a nie przez sanitizer. - Tablice dopuszczające
svglubmath. Typowy przypadek to zestawy ikon inline. Jeśli reguła z raportu ma zastosowanie, wynik może zostać ucięty na pierwszej konstrukcji, której parser nie obsłuży. Porównaj każdą ikonę, którą dostarczasz. - Tablice dla
<title>i podobnych.<title>to element specjalny, a jego zawartość jest traktowana jak tekst. Stąd przykład z escapowanym<sneaky>.
Jeśli tablica dopuszcza tylko a, strong, em, img i kilka atrybutów, skutek będzie raczej kosmetyczny: <IMG ... /> zamieni się w <img ...>.
Co psuje się w testach porównujących oczyszczony HTML?
Wszystko, co sprawdza dokładny string. Test jak pierwsza asercja poniżej przechodzi dziś i może paść po zmianie, choć znaczniki znaczą to samo. Druga asercja normalizację przeżyje.
<?php
// Brittle: pins the exact string the sanitizer happens to emit.
$this->assertSame( '<a href="/x">Read</a>', wp_kses_post( "<A HREF='/x'>Read</A>" ) );
// Sturdier: asserts on what the markup means.
$p = new WP_HTML_Tag_Processor( wp_kses_post( "<A HREF='/x'>Read</A>" ) );
$this->assertTrue( $p->next_tag( 'a' ) );
$this->assertSame( '/x', $p->get_attribute( 'href' ) );Testuj zachowanie, nie bajty. Fixtury, które muszą zostać dosłowne, wygeneruj raz na trunku, przeczytaj diff własnymi oczami i zapisz nową wartość z komentarzem, od której wersji się zmieniła. Hurtowe “zaktualizuj snapshoty” to sposób, w jaki prawdziwa regresja dostaje akceptację.
To samo dotyczy testów wizualnych i plików wzorcowych. Szablony maili, które czyścisz przed wysyłką, to typowe miejsce, gdzie zapisany string porównuje się ze świeżym.
Jak testować na trunku bez dotykania produkcji?
Trzy drogi, wszystkie na stagingu albo lokalnie. To moje propozycje, nie część oficjalnego raportu.
Wtyczka WordPress Beta Tester. Pozwala przenieść staging na nocne buildy, a później na bety 7.2. Według harmonogramu Beta 1 wychodzi 20 października 2026. Nie umiałem potwierdzić, czy przepisanie jest już w Beta 1, więc sprawdź, czy w zainstalowanym kodzie istnieje filtr wp_kses_force_legacy_parser.
WP-CLI nightly. Na jednorazowej kopii:
# Staging only, never production.
wp core update --version=nightly --force
wp core version
wp eval-file bin/kses-diff.php > kses-diff.txt
tail -n 1 kses-diff.txtwp-env. Wskaż rdzeń na lustro trunka i zamontuj wtyczkę:
{
"core": "WordPress/WordPress#master",
"plugins": [ "." ]
}Którąkolwiek drogą pójdziesz, na koniec upewnij się, że filtr istnieje, na przykład przez grep -rn wp_kses_force_legacy_parser wp-includes/. Jeśli go nie ma, nie testujesz przepisania.
Jak porównać oczyszczony wynik na prawdziwej treści?
Przypadki syntetyczne znajdą tylko błędy, które już sobie wyobraziłeś. Wartościowy test to Twoja własna baza. Skrypt poniżej czyści każdy opublikowany wpis dwa razy, raz z wymuszonym starym parserem i raz bez, i wypisuje tylko te wpisy, które się różnią.
<?php
// bin/kses-diff.php
// Run on a trunk build: wp eval-file bin/kses-diff.php > kses-diff.txt
$ids = get_posts( array(
'post_type' => 'any',
'post_status' => 'publish',
'numberposts' => -1,
'fields' => 'ids',
) );
$diff = 0;
foreach ( $ids as $id ) {
$raw = get_post_field( 'post_content', $id );
add_filter( 'wp_kses_force_legacy_parser', '__return_true' );
$old = wp_kses_post( $raw );
remove_filter( 'wp_kses_force_legacy_parser', '__return_true' );
$new = wp_kses_post( $raw );
if ( $old !== $new ) {
++$diff;
printf( "== %d %s\n--- old\n%s\n+++ new\n%s\n", $id, get_permalink( $id ), $old, $new );
}
}
printf( "%d of %d posts differ\n", $diff, count( $ids ) );Odpal go na kopii bazy produkcyjnej, bo redaktorzy wklejają dziwne rzeczy. Pierwsze dwadzieścia różnic przeczytaj ręcznie. Spodziewaj się kilku powtarzalnych przyczyn (samozamykające się znaczniki, luźny < przy kwocie, SVG inline), a naprawa jednej przyczyny załatwia wiele wpisów naraz.
Rozszerz to tym samym wzorcem: zamień wp_kses_post() na wywołanie, które robi Twoja wtyczka z własną tablicą, i podaj mu wartości opcji, tekst widgetów i komentarze, nie tylko wpisy.
Kiedy użyć filtra starego parsera?
Filtr nazywa się wp_kses_force_legacy_parser. Zwrócenie true przywraca stary parser. Raport opisuje go jako sposób, w jaki właściciele witryn mogą wyłączyć nowy kod, a kluczowe jest słowo tymczasowy.
<?php
// wp-content/mu-plugins/kses-legacy-parser.php
// Stopgap only. Delete it once your output diff is clean.
add_filter( 'wp_kses_force_legacy_parser', '__return_true' );Są dwie sytuacje. Pierwsza: w oknie testów znalazłeś regresję na stronie klienta i musisz ustabilizować witrynę, zanim poprawisz kod albo zgłosisz błąd. Druga: wtyczka, której nie kontrolujesz, przestaje działać, a autor nie wydał poprawki. Wrzuć filtr do mu-pluginu, żeby był widoczny w jednym miejscu, i załóż zadanie z datą usunięcia.
Nie wdrażaj tego jako stałego ustawienia. Stara implementacja ma znane problemy, które przepisanie ma naprawić, między innymi awarie PCRE przy dużych wartościach atrybutów i zawartość skryptów pokazywaną jako tekst, według opisu PR. Ciche wyłączenie zachowuje te problemy i ukrywa migrację przed następnym programistą.
Nie znalazłem potwierdzonej daty usunięcia starego parsera. Traktuj filtr jako coś, co zniknie, i planuj pod to.
Jaki jest harmonogram WordPressa 7.2?
Z posta o harmonogramie 7.2, wszystko o 15:00 UTC:
| Etap | Data |
|---|---|
| Beta 1 | 20 października 2026 |
| Beta 2 | 27 października 2026 |
| Beta 3 | 3 listopada 2026 |
| Beta 4 | 10 listopada 2026 |
| RC1 | 17 listopada 2026 |
| RC2 | 24 listopada 2026 |
| RC3 | 1 grudnia 2026 |
| Wydanie finalne | 8 grudnia 2026 |
Raport mówi: 7.2 albo 7.3. Nie możesz więc liczyć na żadne z wydań, ale nie możesz też wykluczyć 7.2. Rozsądny plan: skrypt różnic na stagingu przed Beta 1, ponownie po RC1, a wszystko zaskakujące zgłoś w bilecie Trac, póki okno jest otwarte. Autor prosi właśnie o takie opinie.
Co agencja powinna zrobić w tym tygodniu?
- Przeszukaj kod pod kątem
wp_kses(,wp_kses_post(,wp_kses_data(i własnych tablic$allowed. Spisz, które dostają HTML od użytkowników lub redaktorów. - Postaw staging na trunku jedną z dróg powyżej i potwierdź, że filtr istnieje.
- Puść skrypt różnic na kopii treści produkcyjnej. Segreguj po przyczynie, nie po wpisie.
- Przepisz kruche testy tak, by sprawdzały strukturę, a fixtury tekstowe wygeneruj raz i zrecenzuj ręcznie.
- Sprawdź shortcodes i bloki, które emitują inline script, style, SVG lub MathML.
- Zdecyduj osobno dla każdego klienta, czy filtr jest potrzebny, opisz to i ustaw datę usunięcia.
Jeśli w projekcie leży kilka lat własnego czyszczenia HTML wokół ekosystemu wtyczek, taki audyt mieści się w naszej usłudze tworzenia i rozwijania stron WordPress na zamówienie.
Źródła
- Progress Report: wp_kses(), Make Core, 7 października 2026. Przykłady, nazwa filtra, zdanie o 7.2 lub 7.3.
- Harmonogram 7.2 release party, Make Core, 6 października 2026.
- Pull request 13271, KSES: Reimplement with Tag Processor. Nazwa funkcji, filtr, lista zmian zachowania. Bilety Trac 66208 i 65984 są tam wskazane.
- Bilet Trac 66208. Nie udało mi się otworzyć biletu bezpośrednio, więc fakty o nim pochodzą ze strony PR.
Sprawdzone 10 października 2026. Zachowanie trunka może się zmienić przed wydaniem.







