Skip to content

Commit bbd27ce

Browse files
committed
Styles: remove null and scalar style entries where the pre-save filter can add them
A style that is not an array cannot be sanitized or rendered, and a null one crashes the builder's style checks when the layout loads. The removal now lives in one helper, remove_invalid_styles(), which sanitize_all() runs on the output of the data migration filter; the per-list checks it replaces used isset(), which is false for a key holding null, so the null case they were written for never reached them. The three save paths also run the helper on the output of the siteorigin_panels_data_pre_save filter. sanitize_all() runs before that filter, so a callback on it could store a null style that nothing cleaned; the Toggle Visibility migration in SiteOrigin Premium did exactly that. Refs #1366, #1367.
1 parent 6b3d334 commit bbd27ce

4 files changed

Lines changed: 87 additions & 25 deletions

File tree

inc/abilities.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ protected function update_meta_layout( $post_id, $panels_data, $old_panels_data
414414
// Apply the same public pre-save filter save_post applies, so third-party
415415
// pre-save transforms run on ability writes too.
416416
$panels_data = apply_filters( 'siteorigin_panels_data_pre_save', $panels_data, $post, $post_id );
417+
// A pre-save callback can write a style that is not an array; the builder cannot load one.
418+
$panels_data = SiteOrigin_Panels_Styles_Admin::single()->remove_invalid_styles( $panels_data );
417419

418420
// Unconditional kses floor for the AI meta write (Audit #1 fix 1b): the
419421
// whole write is AI-originated, and AI output is prompt-injectable no

inc/admin.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ public function save_post( $post_id ) {
289289

290290
$panels_data = SiteOrigin_Panels_Styles_Admin::single()->sanitize_all( $panels_data );
291291
$panels_data = apply_filters( 'siteorigin_panels_data_pre_save', $panels_data, $post, $post_id );
292+
// A pre-save callback can write a style that is not an array; the builder cannot load one.
293+
$panels_data = SiteOrigin_Panels_Styles_Admin::single()->remove_invalid_styles( $panels_data );
292294

293295
/**
294296
* Fires with the fully-sanitized canonical panels_data immediately before it
@@ -1010,6 +1012,8 @@ public function save_home_page() {
10101012

10111013
$panels_data = SiteOrigin_Panels_Styles_Admin::single()->sanitize_all( $panels_data );
10121014
$panels_data = apply_filters( 'siteorigin_panels_data_pre_save', $panels_data, $page, $page_id );
1015+
// A pre-save callback can write a style that is not an array; the builder cannot load one.
1016+
$panels_data = SiteOrigin_Panels_Styles_Admin::single()->remove_invalid_styles( $panels_data );
10131017

10141018
update_post_meta( $page_id, 'panels_data', map_deep( $panels_data, array( 'SiteOrigin_Panels_Admin', 'double_slash_string' ) ) );
10151019

inc/styles-admin.php

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -566,17 +566,75 @@ public function panels_data_migrations( $panels_data ) {
566566
return $panels_data;
567567
}
568568

569+
/**
570+
* Remove style entries that are not arrays from a layout.
571+
*
572+
* A style is an array of settings. Anything else stored under the key, a
573+
* null, an empty string, a number, cannot be rendered or sanitized, and a
574+
* null crashes the builder's style checks when the layout is loaded. Only
575+
* the three style locations are touched: `widgets[*][panels_info][style]`,
576+
* `grids[*][style]` and `grid_cells[*][style]`. Items that are not arrays
577+
* are left alone. Running it twice gives the same result, so it is safe to
578+
* call both before and after filters that may write a style.
579+
*
580+
* @param mixed $panels_data The layout. Returned unchanged unless it is an array.
581+
*
582+
* @return mixed
583+
*/
584+
public function remove_invalid_styles( $panels_data ) {
585+
if ( ! is_array( $panels_data ) ) {
586+
return $panels_data;
587+
}
588+
589+
if ( ! empty( $panels_data['widgets'] ) && is_array( $panels_data['widgets'] ) ) {
590+
foreach ( $panels_data['widgets'] as $i => $widget ) {
591+
if (
592+
! is_array( $widget ) ||
593+
empty( $widget['panels_info'] ) ||
594+
! is_array( $widget['panels_info'] ) ||
595+
! array_key_exists( 'style', $widget['panels_info'] )
596+
) {
597+
continue;
598+
}
599+
600+
if ( ! is_array( $widget['panels_info']['style'] ) ) {
601+
unset( $panels_data['widgets'][ $i ]['panels_info']['style'] );
602+
}
603+
}
604+
}
605+
606+
foreach ( array( 'grids', 'grid_cells' ) as $list ) {
607+
if ( empty( $panels_data[ $list ] ) || ! is_array( $panels_data[ $list ] ) ) {
608+
continue;
609+
}
610+
611+
foreach ( $panels_data[ $list ] as $i => $item ) {
612+
if ( ! is_array( $item ) || ! array_key_exists( 'style', $item ) ) {
613+
continue;
614+
}
615+
616+
if ( ! is_array( $item['style'] ) ) {
617+
unset( $panels_data[ $list ][ $i ]['style'] );
618+
}
619+
}
620+
}
621+
622+
return $panels_data;
623+
}
624+
569625
/**
570626
* Sanitize the style fields in panels_data
571627
*
572628
* @return mixed
573629
*/
574630
public function sanitize_all( $panels_data ) {
575631
$panels_data = apply_filters( 'siteorigin_panels_data_migration', $panels_data );
632+
$panels_data = $this->remove_invalid_styles( $panels_data );
633+
576634
if ( ! empty( $panels_data['widgets'] ) ) {
577635
// Sanitize the widgets
578636
for ( $i = 0; $i < count( $panels_data['widgets'] ); $i ++ ) {
579-
if ( empty( $panels_data['widgets'][ $i ]['panels_info'] ) ) {
637+
if ( ! is_array( $panels_data['widgets'][ $i ] ) || empty( $panels_data['widgets'][ $i ]['panels_info'] ) ) {
580638
continue;
581639
}
582640

@@ -586,51 +644,42 @@ public function sanitize_all( $panels_data ) {
586644
);
587645
}
588646

589-
if ( isset( $panels_data['widgets'][ $i ]['panels_info']['style'] ) ) {
590-
if ( is_null( $panels_data['widgets'][ $i ]['panels_info']['style'] ) || ! is_array( $panels_data['widgets'][ $i ]['panels_info']['style'] ) ) {
591-
unset( $panels_data['widgets'][ $i ]['panels_info']['style'] );
592-
} elseif ( empty( $panels_data['widgets'][ $i ]['panels_info']['style'] ) ) {
593-
continue;
594-
} else {
595-
$panels_data['widgets'][ $i ]['panels_info']['style'] = $this->sanitize_style_fields( 'widget', $panels_data['widgets'][ $i ]['panels_info']['style'] );
596-
}
647+
if ( empty( $panels_data['widgets'][ $i ]['panels_info']['style'] ) ) {
648+
continue;
597649
}
650+
651+
$panels_data['widgets'][ $i ]['panels_info']['style'] = $this->sanitize_style_fields( 'widget', $panels_data['widgets'][ $i ]['panels_info']['style'] );
598652
}
599653
}
600654

601655
if ( ! empty( $panels_data['grids'] ) ) {
602656
// The rows
603657
for ( $i = 0; $i < count( $panels_data['grids'] ); $i ++ ) {
658+
if ( ! is_array( $panels_data['grids'][ $i ] ) ) {
659+
continue;
660+
}
661+
604662
if ( ! empty( $panels_data['grids'][ $i ]['label'] ) ) {
605663
$panels_data['grids'][ $i ]['label'] = sanitize_text_field(
606664
$panels_data['grids'][ $i ]['label']
607665
);
608666
}
609667

610-
if ( isset( $panels_data['grids'][ $i ]['style'] ) ) {
611-
if ( is_null( $panels_data['grids'][ $i ]['style'] ) || ! is_array( $panels_data['grids'][ $i ]['style'] ) ) {
612-
unset( $panels_data['grids'][ $i ]['style'] );
613-
} elseif ( empty( $panels_data['grids'][ $i ]['style'] ) ) {
614-
continue;
615-
} else {
616-
$panels_data['grids'][ $i ]['style'] = $this->sanitize_style_fields( 'row', $panels_data['grids'][ $i ]['style'] );
617-
}
668+
if ( empty( $panels_data['grids'][ $i ]['style'] ) ) {
669+
continue;
618670
}
671+
$panels_data['grids'][ $i ]['style'] = $this->sanitize_style_fields( 'row', $panels_data['grids'][ $i ]['style'] );
619672
}
620673
}
621674

622675
if ( ! empty( $panels_data['grid_cells'] ) ) {
623676
// And finally, the cells
624677
for ( $i = 0; $i < count( $panels_data['grid_cells'] ); $i ++ ) {
625-
if ( isset( $panels_data['grid_cells'][ $i ]['style'] ) ) {
626-
if ( is_null( $panels_data['grid_cells'][ $i ]['style'] ) || ! is_array( $panels_data['grid_cells'][ $i ]['style'] ) ) {
627-
unset( $panels_data['grid_cells'][ $i ]['style'] );
628-
} elseif ( empty( $panels_data['grid_cells'][ $i ]['style'] ) ) {
629-
continue;
630-
} else {
631-
$panels_data['grid_cells'][ $i ]['style'] = $this->sanitize_style_fields( 'cell', $panels_data['grid_cells'][ $i ]['style'] );
632-
}
678+
if ( ! is_array( $panels_data['grid_cells'][ $i ] ) || empty( $panels_data['grid_cells'][ $i ]['style'] ) ) {
679+
continue;
633680
}
681+
682+
$panels_data['grid_cells'][ $i ]['style'] = $this->sanitize_style_fields( 'cell', $panels_data['grid_cells'][ $i ]['style'] );
634683
}
635684
}
636685

tests/AbilitiesTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public static function kses_deep( $value ) {
105105
class Abilities_StylesSpy {
106106
public static $instance;
107107
public $sanitize_all_called = false;
108+
public $remove_invalid_styles_input = null;
108109

109110
public static function single() {
110111
return self::$instance;
@@ -115,6 +116,12 @@ public function sanitize_all( $panels_data ) {
115116

116117
return $panels_data;
117118
}
119+
120+
public function remove_invalid_styles( $panels_data ) {
121+
$this->remove_invalid_styles_input = $panels_data;
122+
123+
return $panels_data;
124+
}
118125
}
119126

120127
if ( ! class_exists( 'SiteOrigin_Panels_Styles_Admin' ) ) {

0 commit comments

Comments
 (0)