Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Progress Report: wp_kses()

Дата публикации: 07-10-2026 19:57:21

Since its inception, WordPress has relied on the KSES1 subsystem to sanitize HTML. Its purpose has grown and changed over time, but primarily serves two purposes: correct aesthetic defects that are likely the result of typos or pasting errors; and remove security risks that might appear in untrusted inputs. The original kses library was written […]

Основное содержимое страницы с новостью.

Since its inception, WordPress has relied on the KSES1 subsystem to sanitize HTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers.. Its purpose has grown and changed over time, but primarily serves two purposes: correct aesthetic defects that are likely the result of typos or pasting errors; and remove security risks that might appear in untrusted inputs. The original kses library was written before HTML5 fully-formalized HTML parsing and at a time when it was common to “hand type” HTML. Today, however, there is no such thing as “seriously malformed2” HTML and most people author content through rich editors, such as WordPress’ Block Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. editor. This means that today, the primary purpose of wp_kses() is to prune out risks from untrusted inputs, and it’s time for it to get an overhaul.

As part of #66208, PR#13271 replaced the implementation of wp_kses() with a new one based on the HTML API. This update replaces a complicated chain of functions, string processing, PCRE regular expressions, and outdated logic to ensure that WordPress can perform its sanitization role reliably and comprehensibly.

What changes might affect existing plugins, themes, or Core Core is the set of software required to run WordPress. The Core Development Team builds WordPress. code?

Rebuilding wp_kses() should not require any changes to plugins or themes; it’s an in-place upgrade with no interface changes. However, as its mode of operation is being entirely overhauled, there may be significant impacts on inputs that were treated in particular ways by the legacy implementation.

An opt-out filter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. preserves existing behaviors.

For now, the original implementation is being kept around for compatibility reasons. A new filter gives site owners the opportunity to opt-out of running the replacement code. Reverting to the legacy mode is therefore as easy as adding the following line to your site. Before the full release of WordPress 7.2.0, any and all feedback on this new implementation will not only be greatly appreciated, but will help ensure a stable update for everyone.

add_filter( 'wp_kses_force_legacy_parser', '__return_true' );

It’s safe to run the new code, as the new internal parser is more robust, faster, and requires less memory to run, but in case some existing code depends on the idiosyncrasies of the legacy wp_kses(), that implementation remains available for now, but it will disappear in future releases or even in WordPress 7.2.03.

What will change as a result of this refactor in the function’s behavior?

The goal of the refactor is to preserve the intent of the safety and aesthetic checks in the legacy wp_kses() function while ensuring that there will never be a mis-parse of HTML. To that extent, it is hard to quantify or completely qualify the kinds of changes this will bring to bear. There are a number of common scenarios, however, that are likely to present themselves.

Text nodes and all syntax tokens are normalized.

The new implementation starts by structurally parsing its input documents, meaning that logical choices are based on the meaning of the input text, not the text directly. Whereas the legacy wp_kses() implementation attempts to examine each token in an HTML input and make adjustments to that original document, the new implementation creates its output string as it proceeds by creating a fresh serialization of the parsed input.

This means that character references in text nodes will be fully un-escaped and deterministically re-escaped, tags will appear all-lowercase, attributes double-quoted, no ignored self-closing flags for void elements4, etc.

<!-- Input HTML -->
The <IMG class=bike class="bo&#x0061;t" src='vehicle.png' /> & the wheels&hellip;

<!-- Legacy behavior -->
The <IMG class="bike" src="vehicle.png" /> &amp; the wheels&hellip;

<!-- New behavior -->
The <img class="bike" src="vehicle.png"> &amp; the wheels…

The goal of these changes is to produce more predictable HTML so that other parsing code is more likely to successfully recognize the HTML it receives, even if that other code suffers the same problems characteristic with over-simplified HTML parsing. This is attempting to remove the edge cases that make handling HTML reliably so difficult.

This normalization also means that there are a number of places that content was erroneously removed before which will now be preserved. The legacy implementation took defensive measures against inputs it didn’t understand, often swallowing up content that appeared like a tag A directory in Subversion. WordPress uses tags to store a single snapshot of a version (3.6, 3.6.1, etc.), the common convention of tags in version control systems. (Not to be confused with post tags.). Since the HTML API An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. understands that structure, however, these situations are recognized and normalized to prevent this very kind of confusion.

<!-- Input HTML -->
It would have to be <$500 for me to be interested in buying it; it’s already >5yo and past its warranted life.

<!-- Legacy behavior -->
It would have to be 5yo and past its warranted life.

<!-- New behavior -->
It would have to be &lt;$500 for me to be interested in buying it; it’s already &gt;5yo and past its warranted life.
Ambiguous ampersands are resolved.

This one is a bit esoteric, but might appear when least expected: there’s a subtlety in how HTML parsers treat named character references which are missing their closing semicolon. Some character references may be recognized without a terminating semicolon, such as &copy, while others unconditionally require them, such as with &middot;. Things become ambiguous when there are successive characters after such a named reference which themselves do not form another character reference: e.g. &copypaste; It looks like there’s a name here, but there’s no character reference named copypaste.

Inside of text nodes, these semicolon-optional character references are decoded, sometimes surprisingly, but when found inside HTML attributes they aren’t, which is yet another surprise. In the &copypaste; example the rendered text would show ©paste;, but an attribute value would remain &copypaste;. The reason for this discrepancy is how URL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org search parameters are frequently mis-encoded in src, href and other attributes. Consider a search link: <a href="/search?q=cat+pictures&not=cartoon">; it’s clear that the intent here was to add a search parameter named not to exclude search results, but if decoded strictly, would be interpreted as /search?q=cat+pictures¬=cartoon because the &not character reference doesn’t require its semicolon.

This exception was made to prevent breaking the existing web and remains today in all spec-compliant HTML parsers and browsers. Where this might turn up is when text nodes contain URLs containing query args or snippets of query args. Note that &sect substring has been replaced by the new implementation in the following snippet with the section sign §, just as a browser would.

<!-- Input HTML -->
Add <code>/?preview=true&section=grilling</code>.

<!-- Legacy behavior -->
Add <code>/?preview=true&amp;section=grilling</code>.

<!-- New behavior -->
Add <code>/?preview=true§ion=grilling</code>.

To avoid this situation, authors should always escape the ampersand character & when writing HTML, and always include the semicolon for character references. This is particularly true within URL query strings, as the unescaped ampersands can be easy to overlook; one must remember that the decoded value should be &, thus the attribute value must contain &amp;.

The contents of SCRIPT and STYLE elements are removed with their corresponding tags.

The HTML API defines the concept of a “special atomic element.” These are essentially the elements whose content cannot contain other markup. That is, all of the bytes found inside of them are parsed as plaintext, sometimes un-escaping character references and other times not. Because of the unique rules applied to the content of each of these kinds of elements, the HTML API treats them as if they were a single unit — it provides the entire element or none at all.

In the legacy wp_kses() implementation, if a SCRIPT tag were not allowed (as is the case for the default “post” context), then the opening and closing tags would be removed but the content would stick around. This was safe behavior because the executable code was removed from its executable content, but it could leave large rendered spans of JavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com code on the final page.

The new behavior is that when one of these tags is not allowed, it will be removed with all of its content too, automatically. This is not the case for other tags generally. For example, if section isn’t passed in for the allowable HTML tags, then only the opening and closing <section> and </section> tags will be removed. The nature of the special atomic elements warrants the change.

<!-- Input HTML -->
Click on my <script>alert(1)</script>!

<!-- Legacy behavior -->
Click on my alert(1)!

<!-- New behavior -->
Click on my !

There’s another side of this that could be surprising: there might be content inside of these special elements that looks like a tag but isn’t. The updated code recognizes when things that look like tags but aren’t, and then escapes that content where possible to prevent further confusion from downstream code.

<!-- Input HTML -->
<title>The <sneaky> tag is not allowed.</title>
<div>The <sneaky> tag is removed.</div>

<!-- Legacy behavior -->
<title>The  tag is not allowed.</title>
<div>The  tag is removed.</div>

<!-- New behavior -->
<title>The &lt;sneaky&gt; tag is not allowed.</title>
<div>The  tag is removed.</div>
Incomplete inputs are truncated before the start of the ambiguity.

While wp_kses() is usually examining a snippet of HTML in isolation, its output is typically being rendered into a containing HTML document. Because of this, it’s important to guard the end of the output to make sure that it can’t form a tag when joined with other strings. The legacy implementation handles this by treating anything which fails to fully parse as plaintext and escape it, which produces safe but often corrupt outputs. The HTML API pauses when it finds text that looks like it could have been truncated, meaning that it might be the start of a tag or comment or other syntax element. Therefore the refactor avoids treating these tail-ends as if they were text and simply discards them instead.

<!-- Input -->
Click on the <butto

<!-- Legacy behavior -->
Click on the &lt;butto

<!-- New behavior -->
Click on the 

For most situations the new behavior will result in less-clobbered text on the rendered page. However, there are a few points to be aware of regarding the special atomic elements. wp_kses() was built in a way that allows for un-matched tags at the start and end of the input, and plenty of code sends these kinds of incomplete fragments. For the most part, this has been preserved, but the special atomic elements are unique in that the parser changes modes inside of the elements. Therefore, if any of these elements appears incompletely, then the entire element will be removed. Complete in this context would mean a proper opening tag followed by a proper closing tag, including the contained content.

<!-- Input -->
Use the <title> tag to open a title.

<!-- Legacy behavior (assuming that TITLE is allowed ) -->
Use the <title> tag to open a title.

<!-- New behavior -->
Use the 
SVG and MathML requiring more-complicated parsing is rejected.

When SVG or MathML content is directly embedded in HTML it presents a complicated situation. SVG and MathML are normatively XML applications and adhere to XML’s draconian error policy5 — a parser must reject a document entirely if there is a single error or mistake in it — but when embedded into an HTML document they are a blend of HTML and XML6. When the markup behaves XML-like, it’s relatively straightforward to parse, but when it toggles into the HTML-like syntax then it becomes harder to know when to switch back or when the content ends.

Because of this, the new implementation of wp_kses() will parse as far into SVG and MathML content as it can with certainty. It’s able to know when proceeding would require further complexity7, and when it would, then it aborts entirely and returns the input up until that SVG or MathML content begins. This is important not only for wp_kses(), but safeguards downstream code and parsers which also might be unaware of the more-substantial rules.

<!-- Input -->
<li>XML-like <math><mtext>MathML</mtext></math>
<li>HTML-like <math><mtext><span>MathML</span></mtext></math>

<!-- Legacy behavior -->
<li>XML-like <math><mtext>MathML</mtext></math>
<li>HTML-like <math><mtext><span>MathML</span></mtext></math>

<!-- New behavior -->
<li>XML-like <math><mtext>MathML</mtext></math>
<li>HTML-like
Some inputs are never trusted.

The availability of an HTML parser brings capabilities A capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability). that weren’t previously possible, and in some cases, there are challenging decisions on what to allow based on those capabilities. The new implementation of wp_kses() applies some hard-limits in situations where the legacy implementation didn’t. This includes rejecting various kinds of malformed HTML comments, documents containing the NOSCRIPT element, and various elements containing confusable content.

In certain risky situations, whereby the risk is high that downstream parsers might get confused, or where the major browsers currently disagree on how to interpret the specification, elements may be rewritten in another form. For example, CDATA sections and PLAINTEXT element contents are transformed into normal escaped text.

What rationale is leading behavioral changes?

It’s easy to point out issues with wp_kses(), but it’s much harder to classify if those issues warrant change or not. There is essentially one value leading the decisions in these cases: don’t make wp_kses() worse than it was. This manifests in a couple of ways: for normative runs it should reproduce the existing normative behaviors; but for cases where expectations are already broken, there is no need to retain those broken behaviors. These decisions are also made against the backdrop of the security-sensitive role that wp_kses() plays — its purpose is to prevent misuse, so some changes will result from diligence in hardening despite a regretful change in behavior8.

Preserve intentional behaviors.

There are some debatable historic decisions within wp_kses(). One, for instance, is preserving so-called “unbalanced tags” in the input. One might expect wp_kses() to add missing closing tags to its input, but plenty of existing code in the directory and in Core already passes “unbalanced” HTML intentionally through the function and expects it to remain unbalanced in the output. This is because people sometimes concatenate chunks of HTML which have been separately-sanitized. This may not be the ideal way to accomplish the task, but it’s well-established.

This is a situation where the existing behavior is preserved. Adding those missing tags was explored, but ultimately rejected for this reason.

Allow already-broken behaviors to change.

It’s also up for debate what “broken” exactly means, but for the sake of this refactor, and all refactors with the HTML API, the qualification is whether a broken behavior was expected or not. For instance, wp_kses() has generally not been aware of the special parsing rules inside an HTML TITLE element. Consider its behavior when img is and isn’t allowed.

<?php

echo wp_kses(
    '<title><img></title>',
    array( 'title' => true, 'img' => array( 'src' => array( 'required' => true ) ) ) 
);
// <title><img></title>

echo wp_kses(
    '<title><img /></title>',
    array( 'title' => true, 'img' => array( 'src' => array( 'required' => true ) ) )
);
// <title></title>

echo wp_kses( '<title><img></title>', array( 'title' => true ) );
// <title></title>

There is no real way to intuit the results of these calls because the parser was confused on the nature of the <img> and <img /> byte sequences. Every one of the outputs of these calls is wrong because no IMG element exists within the TITLE.

<?php

add_filter( 'wp_kses_force_legacy_parser', '__return_false' );
echo wp_kses(
    '<title><img></title>',
    array( 'title' => true, 'img' => array( 'src' => array( 'required' => true ) ) ) 
);
// <title>&lt;img&gt;</title>

The proper way to sanitize and normalize content within the TITLE element is to escape it as text, after first decoding any character references inside it. This behavior was changed because there’s no possible way to say that the existing behavior was correct without creating internal inconsistencies. It might have been “correct” in some cases for some people, but an adjustment here only changes who is affected by the inconsistent state and it’s not viable to attempt to evaluate whose broken flows are more or less important.

So this change was adopted because it establishes clear expectations for everyone and fixes situations in which it was not previously possible for any consistent notion of “it works.”

Conclusion

Please give the new implementation of wp_kses() a try and share any feedback you have in the ticket Created for both bug reports and feature development on the bug tracker. (#66208), or leave a comment below. Expect this to be part of WordPress 7.2 or 7.3 dependent on what this period of testing turns up. Thank you for your attention and participation!


Props to @jonsurrell and @westonruter who reviewed this post before publishing.

  1. “The name kses comes from the terms XSS and access.” — noted in the README of the 0.1.0 release of the kses library. ↩︎
  2. The HTML Living Specification prescribes how to parse every kind of malformed input, meaning that every compliant parser should produce the same output for any given input. There is no point of giving up, and there is no leniency in how a document is to be parsed. ↩︎
  3. The speed with which the legacy wp_kses() implementation will disappear is directly related to the severity The seriousness of the ticket in the eyes of the reporter. Generally, severity is a judgment of how bad a bug is, while priority is its relationship to other bugs. of issues uncovered in testing. If all goes well, WordPress 7.2.0 could include a wholesale replacement of the legacy function without any transition support in the released version. ↩︎
  4. There is a myth of XHTML-compatible HTML, but the sense of interoperability and security is superficial. It’s not safe to deliver HTML as XHTML and there is no general way to create a single document compatible under both systems. The mistaken self-closing flag on HTML elements appeared as a way to attempt to produce XHTML-safe HTML, but is non-normative and its presence continues a tradition of misunderstanding, because HTML has no self-closing elements. ↩︎
  5. Also referred to as “penalize innocent end-users,” XML’s “draconian error handling” was chosen to keep XML parser implementations simple. ↩︎
  6. When HTML5 was being standardized, one of the guiding rules was “don’t break the Internet.” Because a sufficient number of websites had already pasted SVG and MathML into HTML pages, with errors, and because the browsers at the time parsed and displayed that content anyway, the specification had to adapt to continue to allow these pages to render in spec-compliant browsers. In fact, this requirement for maximum backwards compatibility is the reason behind most of HTML’s parsing complexity. ↩︎
  7. The new wp_kses() implementation relies on the Tag Processor, which can parse every tag, comment, text node, etc., but which isn’t aware of the tree-structure of an HTML document. The HTML Processor, its more-powerful counterpart, is able to build that tree, but this process involves considerably more time and effort. ↩︎
  8. It’s worth emphasizing that wp_kses() is meant to sanitize untrusted inputs. There is a certain blurriness in how specific inputs should be handled in order to neutralize potential dangers. Because of this, there are intentionally fewer behavioral guarantees and the logic will lean conservatively. That is, it will be safety to regretfully reject some safe inputs in order to ensure that more unsafe inputs are rejected. ↩︎

#html-api, #html-api-progress-report, #progress-report

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Call for Code Review Help: Repairing Gutenberg GitHub Actions Workflows for wp/x.y Branches08.1316-10-2026
2Функция WordPress: wp_add_crossorigin_attributes()019.0920-08-2026
3WordPress Developer06.2213-08-2026
4WordPress : valider le code HTML des meta tags et oEmbeds09.4122-07-2026
5WordPress-Lücke: Login-Seite öffnet Tür zur Serverübernahme041.2512-08-2026
6Cybersecurity for WordPress: Protecting Websites from Next-Gen Threats #wordpress #internet #cybersecurity014.415-06-2026
7heise+ | Datenlecks auf WordPress-Websites finden und abdichten016.927-07-2026
8automattic/blocks-engine-php-transformer017.1403-08-2026
9The Wirth of Processing with WordPress’ Streaming APIs06.4610-07-2026

Классификация: . Схожих патентов: 0. Схожих новостей: 9. Тональность: 0. Информативность: 11.11. Источник: make.wordpress.org.