Make WordPress Core

Opened 4 years ago

Last modified 5 weeks ago

#54184 new defect (bug)

HTML entities get decoded in Customizer text fields

Reported by: jqz's profile jqz Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 5.8.1
Component: Customize Keywords:
Focuses: ui, administration, coding-standards Cc:

Description

Part 1

  1. Use WP_Customize_Manager::add_setting() and WP_Customize_Manager::add_control() to create a text setting/control with the default value ©;
  2. Navigate to the newly added setting in the Customizer.

Expected Result

The literal text © is in the edit box (as specified for the default value).

Actual Result

A copyright symbol (©) is in the edit box.

Part 2

  1. Enter the text & in the above edit box (replacing what was there);
  2. Publish (save) settings;
  3. Reload the page and navigate to the setting once again.

Expected Result

The literal text & is in the edit box (as originally entered).

Actual Result

Only a single ampersand symbol (&) is in the edit box.

Note

Same results if the setting is given type textarea.

Whatever is entered into text controls, or provided for a default value, should be preserved as is. The WordPress Customizer has no knowledge of how it is going to be used and should not make presumptions that it is going to be used in HTML output.

Change History (5)

#1 @jonsurrell
7 weeks ago

Thanks for the report! Does this happen with other HTML character references?

Use WP_Customize_Manager::add_setting() and WP_Customize_Manager::add_control() to create a text setting/control with the default value ©

It would be helpful if you share a full working code example to test this out.

#2 @jqz
6 weeks ago

Thank you for finally responding after 4 years.

Here is some code that exhibits the problem:

<?php

\add_action(
  'customize_register',
  function ($wpCustomize) {
    $wpCustomize->add_setting(
      'test54184',
      [
        'default' => '&copy;',
      ]
    );
    $wpCustomize->add_control(
      'test54184',
      [
        'label' => 'Test for #54184',
        'type' => 'text',
        'section' => 'title_tagline',
      ]
    );
  }
);

(Edited for code formatting - @jonsurrell)

Last edited 5 weeks ago by jonsurrell (previous) (diff)

#3 @jqz
6 weeks ago

Replying to jonsurrell:

Does this happen with other HTML character references?

It happens with all valid HTML character references, including with decimal and hex notation.

Last edited 5 weeks ago by jqz (previous) (diff)

#4 follow-up: @jonsurrell
5 weeks ago

This seems like a case of the esc_attr() trying to avoid double-encoding entities around here:

WP_Customize_Control::render_content().

#60229 may someday help with this by introducing templating and the ability to distinguish between HTML and unescaped plaintext.

For this specific issue, a workaround may be to override ::render_content(), but swap any esc_attr() or esc_html() calls for a strtr() like the HTML API uses. This snippet is a suitable replacement for esc_attr() and esc_html() where escaping is intended (regardless of whether things appear to be double-escaped):

<?php
$escaped_content = strtr(
      $plaintext_content,
      array(
            '<' => '&lt;',
            '>' => '&gt;',
            '&' => '&amp;',
            '"' => '&quot;',
            "'" => '&apos;',
      )
)

This is the same issue as #64054, except that this issue is for the customizer.

Last edited 5 weeks ago by jonsurrell (previous) (diff)

#5 in reply to: ↑ 4 @jqz
5 weeks ago

Replying to jonsurrell:

a workaround may be to override ::render_content(), but swap any esc_attr() or esc_html() calls for a strtr()

Just calling htmlspecialchars() directly (rather than strtr()) would do the job. The esc_xxxx() functions indirectly do that, but pass false as the $double_encode parameter. I guess that behaviour is a hangover from WordPress having various strings floating around that have been HTML-encoded out-of-context.

Maybe simply adding $double_encode as an optional parameter to the esc_xxxx() functions (default false to maintain current behaviour by default) would be a fairly straightforward solution to allow correct escaping in situations where it is known that the string is not already HTML-encoded.

Note: See TracTickets for help on using tickets.