WP_Theme_JSON::remove_insecure_propertiespublic staticWP 5.9.0

Removes insecure data from theme.json.

Method of the class: WP_Theme_JSON{}

No Hooks.

Returns

Array. Sanitized structure.

Usage

$result = WP_Theme_JSON::remove_insecure_properties( $theme_json, $origin );
$theme_json(array) (required)
Structure to sanitize.
$origin(string)
What source of data this object represents. One of 'blocks', 'default', 'theme', or 'custom'.
Default: 'theme'

Changelog

Since 5.9.0 Introduced.
Since 6.3.2 Preserves global styles block variations when securing styles.
Since 6.6.0 Updated to allow variation element styles and $origin parameter.

WP_Theme_JSON::remove_insecure_properties() code WP 7.1

public static function remove_insecure_properties( $theme_json, $origin = 'theme' ) {
	if ( ! in_array( $origin, static::VALID_ORIGINS, true ) ) {
		$origin = 'theme';
	}

	$sanitized = array();

	$theme_json = WP_Theme_JSON_Schema::migrate( $theme_json, $origin );

	$blocks_metadata     = static::get_blocks_metadata();
	$valid_block_names   = array_keys( $blocks_metadata );
	$valid_element_names = array_keys( static::ELEMENTS );
	$valid_variations    = static::get_valid_block_style_variations( $blocks_metadata );

	$theme_json = static::sanitize( $theme_json, $valid_block_names, $valid_element_names, $valid_variations );

	$blocks_metadata          = static::get_blocks_metadata();
	$style_options            = array( 'include_block_style_variations' => true ); // Allow variations data.
	$style_nodes              = static::get_style_nodes( $theme_json, $blocks_metadata, $style_options );
	$responsive_media_queries = static::get_viewport_media_queries( $theme_json['settings']['viewport'] ?? null );

	foreach ( $style_nodes as $metadata ) {
		$input = _wp_array_get( $theme_json, $metadata['path'], array() );
		if ( empty( $input ) ) {
			continue;
		}

		$block_name = in_array( 'blocks', $metadata['path'], true )
			? static::get_block_name_from_metadata_path( $metadata )
			: null;

		// The global styles custom CSS is not sanitized, but can only be edited by users with 'edit_css' capability.
		if ( isset( $input['css'] ) && current_user_can( 'edit_css' ) ) {
			$output = $input;
		} else {
			$output = static::remove_insecure_styles( $input );
		}

		/*
		 * Get a reference to element name from path.
		 * $metadata['path'] = array( 'styles', 'elements', 'link' );
		 */
		$current_element = array_last( $metadata['path'] );

		/*
		 * $output is stripped of pseudo selectors. Re-add and process them
		 * or insecure styles here.
		 */
		if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] ) ) {
			foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] as $pseudo_selector ) {
				if ( isset( $input[ $pseudo_selector ] ) ) {
					$output[ $pseudo_selector ] = static::remove_insecure_styles( $input[ $pseudo_selector ] );
				}
			}
		}

		// Re-add and process responsive breakpoint styles.
		foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
			if ( isset( $input[ $breakpoint ] ) ) {
				$output[ $breakpoint ] = static::remove_insecure_styles( $input[ $breakpoint ] );

				if ( isset( $input[ $breakpoint ]['elements'] ) ) {
					$output[ $breakpoint ]['elements'] = static::remove_insecure_element_styles( $input[ $breakpoint ]['elements'], $responsive_media_queries );
				}

				if ( isset( $input[ $breakpoint ]['blocks'] ) ) {
					$output[ $breakpoint ]['blocks'] = static::remove_insecure_inner_block_styles( $input[ $breakpoint ]['blocks'], $responsive_media_queries );
				}

				if ( $block_name && isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) ) {
					foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) {
						if ( isset( $input[ $breakpoint ][ $pseudo_selector ] ) ) {
							$output[ $breakpoint ][ $pseudo_selector ] = static::remove_insecure_styles( $input[ $breakpoint ][ $pseudo_selector ] );
						}
					}
				}

				// Responsive custom CSS is allowed for users with 'edit_css' capability.
				if ( isset( $input[ $breakpoint ]['css'] ) && current_user_can( 'edit_css' ) ) {
					$output[ $breakpoint ]['css'] = $input[ $breakpoint ]['css'];
				}
			}
		}

		if ( ! empty( $output ) ) {
			_wp_array_set( $sanitized, $metadata['path'], $output );
		}

		if ( isset( $metadata['variations'] ) ) {
			foreach ( $metadata['variations'] as $variation ) {
				$variation_input = _wp_array_get( $theme_json, $variation['path'], array() );
				if ( empty( $variation_input ) ) {
					continue;
				}

				$variation_output = static::remove_insecure_styles( $variation_input );

				if ( isset( $variation_input['blocks'] ) ) {
					$variation_output['blocks'] = static::remove_insecure_inner_block_styles( $variation_input['blocks'], $responsive_media_queries );
				}

				if ( isset( $variation_input['elements'] ) ) {
					$variation_output['elements'] = static::remove_insecure_element_styles( $variation_input['elements'], $responsive_media_queries );
				}

				// Re-add and process responsive breakpoint styles for variations.
				foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
					if ( isset( $variation_input[ $breakpoint ] ) ) {
						$variation_output[ $breakpoint ] = static::remove_insecure_styles( $variation_input[ $breakpoint ] );

						if ( isset( $variation_input[ $breakpoint ]['elements'] ) ) {
							$variation_output[ $breakpoint ]['elements'] = static::remove_insecure_element_styles( $variation_input[ $breakpoint ]['elements'], $responsive_media_queries );
						}

						if ( isset( $variation_input[ $breakpoint ]['blocks'] ) ) {
							$variation_output[ $breakpoint ]['blocks'] = static::remove_insecure_inner_block_styles( $variation_input[ $breakpoint ]['blocks'], $responsive_media_queries );
						}

						if ( $block_name && isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) ) {
							foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) {
								if ( isset( $variation_input[ $breakpoint ][ $pseudo_selector ] ) ) {
									$variation_output[ $breakpoint ][ $pseudo_selector ] = static::remove_insecure_styles( $variation_input[ $breakpoint ][ $pseudo_selector ] );
								}
							}
						}

						// Responsive custom CSS is allowed for users with 'edit_css' capability.
						if ( isset( $variation_input[ $breakpoint ]['css'] ) && current_user_can( 'edit_css' ) ) {
							$variation_output[ $breakpoint ]['css'] = $variation_input[ $breakpoint ]['css'];
						}
					}
				}

				if ( ! empty( $variation_output ) ) {
					_wp_array_set( $sanitized, $variation['path'], $variation_output );
				}
			}
		}
	}

	$setting_nodes = static::get_setting_nodes( $theme_json );
	foreach ( $setting_nodes as $metadata ) {
		$input = _wp_array_get( $theme_json, $metadata['path'], array() );
		if ( empty( $input ) ) {
			continue;
		}

		$output = static::remove_insecure_settings( $input, array( 'settings' ) === $metadata['path'] );
		if ( ! empty( $output ) ) {
			_wp_array_set( $sanitized, $metadata['path'], $output );
		}
	}

	if ( empty( $sanitized['styles'] ) ) {
		unset( $theme_json['styles'] );
	} else {
		$theme_json['styles'] = $sanitized['styles'];
	}

	if ( empty( $sanitized['settings'] ) ) {
		unset( $theme_json['settings'] );
	} else {
		$theme_json['settings'] = $sanitized['settings'];
	}

	return $theme_json;
}