Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
27 changes: 27 additions & 0 deletions projects/packages/sync/src/class-defaults.php
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,33 @@ public static function get_post_meta_whitelist() {
return apply_filters( 'jetpack_sync_post_meta_whitelist', self::$post_meta_whitelist );
}

/**
* Array of post meta key prefixes whitelisted (wildcard match).
*
* @var array Post meta prefix whitelist.
*/
public static $post_meta_prefix_whitelist = array(
'_wpas_skip_',
);

/**
* Get the post meta key prefix whitelist (wildcard match).
*
* @return array Post meta prefix whitelist.
*/
public static function get_post_meta_prefix_whitelist() {
/**
* Filter the list of post meta key prefixes (wildcards) that are manageable via the JSON API.
*
* @module sync
*
* @since $$next_version
*
* @param array The default list of meta key prefixes.
*/
return apply_filters( 'jetpack_sync_post_meta_prefix_whitelist', self::$post_meta_prefix_whitelist );
}

/**
* Comment meta whitelist.
*
Expand Down
13 changes: 11 additions & 2 deletions projects/packages/sync/src/class-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,17 @@ public static function get_whitelisted_post_meta_sql() {
public static function get_allowed_post_meta_structured() {
return array(
'meta_key' => array(
'operator' => 'IN',
'values' => array_map( 'esc_sql', static::get_setting( 'post_meta_whitelist' ) ),
'group_operator' => 'OR',
'filters' => array(
array(
'operator' => 'IN',
'values' => array_map( 'esc_sql', static::get_setting( 'post_meta_whitelist' ) ),
),
array(
'operator' => 'LIKE',
'values' => array_map( 'esc_sql', Defaults::get_post_meta_prefix_whitelist() ),
),
),
),
);
}
Expand Down
46 changes: 31 additions & 15 deletions projects/packages/sync/src/modules/class-module.php
Original file line number Diff line number Diff line change
Expand Up @@ -584,31 +584,47 @@ protected function get_chunks_with_preceding_end( $chunks, $previous_interval_en
*
* @todo Refactor to use $wpdb->prepare() on the SQL query.
*
* @param array $ids Object IDs.
* @param string $meta_type Meta type.
* @param array $meta_key_whitelist Meta key whitelist.
* @param array $ids Object IDs.
* @param string $meta_type Meta type.
* @param array $meta_key_whitelist Exact meta keys to allow.
* @param array $meta_key_prefix_filters Optional. Prefixes of meta keys to allow.
* @return array Unserialized meta values.
*/
protected function get_metadata( $ids, $meta_type, $meta_key_whitelist ) {
protected function get_metadata( $ids, $meta_type, $meta_key_whitelist, $meta_key_prefix_filters = array() ) {
global $wpdb;
$table = _get_meta_table( $meta_type );
$id = $meta_type . '_id';
if ( ! $table ) {
return array();
}

$private_meta_whitelist_sql = "'" . implode( "','", array_map( 'esc_sql', $meta_key_whitelist ) ) . "'";

return array_map(
array( $this, 'unserialize_meta' ),
$wpdb->get_results(
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
"SELECT $id, meta_key, meta_value, meta_id FROM $table WHERE $id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . ' )' .
" AND meta_key IN ( $private_meta_whitelist_sql ) ",
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
OBJECT
)
$meta_key_conditions = array();

if ( ! empty( $meta_key_whitelist ) ) {
$escaped_keys = implode( "','", array_map( 'esc_sql', $meta_key_whitelist ) );
$meta_key_conditions[] = "meta_key IN ( '$escaped_keys' )";
}

foreach ( $meta_key_prefix_filters as $prefix ) {
$meta_key_conditions[] = $wpdb->prepare( 'meta_key LIKE %s', esc_sql( $prefix ) . '%' );
}

if ( empty( $meta_key_conditions ) ) {
return array(); // No whitelist or wildcard = deny all
}

$ids_sql = implode( ',', wp_parse_id_list( $ids ) );
$where_sql = implode( ' OR ', $meta_key_conditions );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
$results = $wpdb->get_results(
"SELECT $id, meta_key, meta_value, meta_id
FROM $table
WHERE $id IN ( $ids_sql )
AND ( $where_sql )",
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
OBJECT
);
return array_map( array( $this, 'unserialize_meta' ), $results );
}

/**
Expand Down
23 changes: 18 additions & 5 deletions projects/packages/sync/src/modules/class-posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Automattic\Jetpack\Constants as Jetpack_Constants;
use Automattic\Jetpack\Roles;
use Automattic\Jetpack\Sync\Defaults;
use Automattic\Jetpack\Sync\Modules;
use Automattic\Jetpack\Sync\Settings;

Expand Down Expand Up @@ -397,11 +398,23 @@ public function filter_meta( $args ) {
* Whether a post meta key is whitelisted.
*
* @param string $meta_key Meta key.
* @return boolean Whether the post meta key is whitelisted.
* @return bool Whether the post meta key is whitelisted.
*/
public function is_whitelisted_post_meta( $meta_key ) {
// The _wpas_skip_ meta key is used by Publicize.
return in_array( $meta_key, Settings::get_setting( 'post_meta_whitelist' ), true ) || str_starts_with( $meta_key, '_wpas_skip_' );
$whitelist = Settings::get_setting( 'post_meta_whitelist' );
$prefix_whitelist = Defaults::get_post_meta_prefix_whitelist();

if ( in_array( $meta_key, $whitelist, true ) ) {
return true;
}

foreach ( $prefix_whitelist as $prefix ) {
if ( str_starts_with( $meta_key, $prefix ) ) {
return true;
}
}

return false;
}

/**
Expand Down Expand Up @@ -843,7 +856,7 @@ public function expand_posts_with_metadata_and_terms( $args ) {
list( $post_ids, $previous_interval_end ) = $args;

$posts = $this->expand_posts( $post_ids );
$posts_metadata = $this->get_metadata( $post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) );
$posts_metadata = $this->get_metadata( $post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ), Defaults::get_post_meta_prefix_whitelist() );
$term_relationships = $this->get_term_relationships( $post_ids );

return array(
Expand Down Expand Up @@ -898,7 +911,7 @@ public function get_next_chunk( $config, $status, $chunk_size ) {
}
// Get the post IDs from the posts that were fetched.
$fetched_post_ids = wp_list_pluck( $posts, 'ID' );
$metadata = $this->get_metadata( $fetched_post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) );
$metadata = $this->get_metadata( $fetched_post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ), Defaults::get_post_meta_prefix_whitelist() );

// Filter the posts and metadata based on the maximum size constraints.
list( $filtered_post_ids, $filtered_posts, $filtered_posts_metadata ) = $this->filter_objects_and_metadata_by_size(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,10 +471,11 @@ private static function wc_get_order_status_keys() {
* @param array $ids List of order IDs.
* @param string $meta_type Meta type.
* @param array $meta_key_whitelist List of allowed meta keys.
* @param array $meta_key_prefix_filters Optional. Prefixes of meta keys to allow.
*
* @return array Filtered order metadata.
*/
protected function get_metadata( $ids, $meta_type, $meta_key_whitelist ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- returning empty meta is intentional.
protected function get_metadata( $ids, $meta_type, $meta_key_whitelist, $meta_key_prefix_filters = array() ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- returning empty meta is intentional.
return array(); // don't sync metadata, all allow-listed core data is available in the order object.
}

Expand Down
43 changes: 32 additions & 11 deletions projects/packages/sync/src/replicastore/class-table-checksum.php
Original file line number Diff line number Diff line change
Expand Up @@ -521,21 +521,42 @@ protected function prepare_filter_values_as_sql( $filter_values = array(), $tabl

$result = array();

foreach ( $filter_values as $field => $filter ) {
foreach ( $filter_values as $field => $filters ) {
$key = ( ! empty( $table_prefix ) ? $table_prefix : $this->table ) . '.' . $field;

switch ( $filter['operator'] ) {
case 'IN':
case 'NOT IN':
$filter_values_count = is_countable( $filter['values'] ) ? count( $filter['values'] ) : 0;
$values_placeholders = implode( ',', array_fill( 0, $filter_values_count, '%s' ) );
$statement = "{$key} {$filter['operator']} ( $values_placeholders )";
$group_operator = $filters['group_operator'] ?? 'AND'; // Default to AND if not specified.
// If filters is explicitly provided, separate it out
if ( isset( $filters['filters'] ) ) {
$filters = $filters['filters'];
} else {
$filters = array( $filters );
}

// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$prepared_statement = $wpdb->prepare( $statement, $filter['values'] );
$group_clauses = array();

foreach ( $filters as $filter ) {
switch ( $filter['operator'] ) {
case 'IN':
case 'NOT IN':
$filter_values_count = is_countable( $filter['values'] ) ? count( $filter['values'] ) : 0;
$values_placeholders = implode( ',', array_fill( 0, $filter_values_count, '%s' ) );
$statement = "{$key} {$filter['operator']} ( $values_placeholders )";

// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$group_clauses[] = $wpdb->prepare( $statement, $filter['values'] );
break;

case 'LIKE':
foreach ( $filter['values'] as $wildcard ) {
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$group_clauses[] = $wpdb->prepare( "{$key} LIKE %s", $wildcard . '%' );
}
break;
}
}

$result[] = $prepared_statement;
break;
if ( ! empty( $group_clauses ) ) {
$result[] = '( ' . implode( " {$group_operator} ", $group_clauses ) . ' )';
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: bugfix

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Sync: Allowing post meta prefix whitelist that works for Full Sync and Checksums.
Loading