Skip to content

WP org now requires register_setting() with sanitization cb + incorrect chars in po filename #1533

Closed

Description

@rubengc

Hi!

I just submitted a new plugin to Wp org and needed to make changes to CMB2 to get the plugin approved:

1 - register_setting() needs sanitization cb
https://developer.wordpress.org/reference/functions/register_setting/

CMB2 calls to register_setting() without the 3rd parameter

This is now mandatory, note that the function should be public accesible, I tested with an array( $this, 'sanitization_cb' ) and WP does not approves it, I needed to make an external funcion to be able to pass it

Note: Settings registration should be called on "admin_init" and "rest_api_init" hooks

2 - po and mo files with incorrect chars

CMB2 contains a po and mo files named as "en@pirated", the "@" char is not allowed and makes any plugin submission fail too

Hope this report helps!

Activity

  1. Mte90 commented on Mar 24, 2025

    @Mte90
    Contributor

    Can you share the changes you did to CMB2 for register_setting?
    Maybe we can do a PR and in meanwhile fix it.

  2. rubengc commented on Mar 24, 2025

    @rubengc
    ContributorAuthor

    Can you share the changes you did to CMB2 for register_setting? Maybe we can do a PR and in meanwhile fix it.

    Of course

            register_setting( 'cmb2', $this->option_key, array(
                'type' => 'array',
                'sanitize_callback' => 'cmb2_settings_sanitization_cb',
            ) );
    
    // ... at bottom of the file
    
    if( ! function_exists( 'cmb2_settings_sanitization_cb' ) ) {
        function cmb2_settings_sanitization_cb( $settings ) {
            global $cmb2_settings_sanitizated;
    
            if( $cmb2_settings_sanitizated === true ) {
                return $settings;
            }
    
            $cmb2_settings_sanitizated = true;
    
            return map_deep( $settings, 'sanitize_text_field' );
        }
    
    }
    
  3. changed the title [-]WP org now requires register_setting() with sanitization cb + icorrect chars on po file[/-] [+]WP org now requires register_setting() with sanitization cb + incorrect chars in po filename[/+] on Mar 25, 2025
  4. rubengc commented on Sep 14, 2026

    @rubengc
    ContributorAuthor

    @jtsternberg since you are right now submitting new changes, have you checked this one? PluginCheck stills triggering this one

  5. jtsternberg commented on Sep 21, 2026

    @jtsternberg
    Member

    Part 2 is done — the en@pirate .po/.mo files are deleted on develop in 01629e9.

    Part 1 is done too, but not with that callback. map_deep( $settings, 'sanitize_text_field' ) strips HTML and collapses newlines, so it would gut every non-plain-text value on save. Try it: put <strong>bold</strong> in a wysiwyg field on a CMB2 options page, add the callback, save, reload — tags gone, no way back. And it isn't limited to the options.php flow: register_setting() hangs sanitize_callback on sanitize_option_{$option_name}, which update_option() fires — including CMB2's own save path.

    Nothing was unsanitized, to be clear. Values already go through CMB2_Sanitize per field type, which is why there was no third argument. Plugin Check just can't see that.

    So what landed on develop is a third argument whose sanitize_callback returns the value untouched, cmb2_sanitize_option_passthrough(), documented as to why. Verified against plugin-check 2.1.0: wp plugin check cmb2 --checks=setting_sanitization reported SettingSanitization.register_settingMissing at includes/CMB2_Options_Hookup.php:62 before, and No errors found after.

    Worth knowing if you're patching your own copy: the current sniff is looser than either of us assumed. It errors only when the third argument is absent, or is numeric / true / false / null — it never looks inside an args array for a sanitize_callback key. So array( $this, 'sanitization_cb' ) should pass it today; plugin-check removed its dynamic-callback warning at some point, so whatever rejected yours was an older version or a human reviewer. There's also an AI-review pass that judges whether a callback really sanitizes, but per plugin-check's readme it only adds a summary alongside the results — it can't fail a check that passed.

    Paths, and why the callback is inert

    Call site: includes/CMB2_Options_Hookup.php:62. Callback: cmb2_sanitize_option_passthrough() in includes/helper-functions.php.

    Save path the filter lands in — CMB2_Options_Hookup::save_options() (:293) → CMB2::save_fields() → CMB2_Options::set() (includes/CMB2_Options.php:167) → update_option() (:197). So it runs on every CMB2 options-page save, and on any third-party update_option() against a CMB2 option key.

    Existing per-type sanitization: CMB2_Sanitize, dispatched from CMB2_Field::sanitization_cb() (includes/CMB2_Field.php:471). text_url → esc_url_raw, wysiwyg/textarea → wp_kses_post, groups/repeatables walked as arrays. Anything that re-sanitizes the assembled array applies one field type's rule to all of them.

    Your $cmb2_settings_sanitizated global is the tell that the filter fired twice in one request. Being request-scoped, it would also skip a second options page saved in the same request.

    No function_exists() guard on it, unlike the two polyfills at the end of that file: init.php version-elects between bundled copies, so exactly one copy of includes/ ever loads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions