You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
WP org now requires register_setting() with sanitization cb + incorrect chars in po filename #1533
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
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
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.
Activity
Mte90 commentedon Mar 24, 2025
Can you share the changes you did to CMB2 for
register_setting?Maybe we can do a PR and in meanwhile fix it.
rubengc commentedon Mar 24, 2025
Of course
[-]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[/+]rubengc commentedon Sep 14, 2026
@jtsternberg since you are right now submitting new changes, have you checked this one? PluginCheck stills triggering this one
i18n: drop en@pirate translation files
jtsternberg commentedon Sep 21, 2026
Part 2 is done — the
en@pirate.po/.mofiles are deleted ondevelopin 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 theoptions.phpflow:register_setting()hangssanitize_callbackonsanitize_option_{$option_name}, whichupdate_option()fires — including CMB2's own save path.Nothing was unsanitized, to be clear. Values already go through
CMB2_Sanitizeper field type, which is why there was no third argument. Plugin Check just can't see that.So what landed on
developis a third argument whosesanitize_callbackreturns the value untouched,cmb2_sanitize_option_passthrough(), documented as to why. Verified against plugin-check 2.1.0:wp plugin check cmb2 --checks=setting_sanitizationreportedSettingSanitization.register_settingMissingatincludes/CMB2_Options_Hookup.php:62before, andNo errors foundafter.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 asanitize_callbackkey. Soarray( $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()inincludes/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-partyupdate_option()against a CMB2 option key.Existing per-type sanitization:
CMB2_Sanitize, dispatched fromCMB2_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_sanitizatedglobal 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.phpversion-elects between bundled copies, so exactly one copy ofincludes/ever loads.Give the registered cmb2 setting a sanitize_callback