-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Block Bindings: Don't show protected fields that are bound to blocks and post meta #6197
Changes from all commits
105ffe1
00b4d65
46a29e4
0b106f9
1736c83
1fb2c47
81d4e34
8d0a0be
ce4aa0c
f1655d6
01d45e5
7d25302
dfe02a5
6414a97
193131f
a0b761b
4cdf241
6e37a4c
8389270
953058d
bc81da4
64f0784
56beecc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
<?php | ||
/** | ||
* Tests for Block Bindings API "core/post-meta" source. | ||
* | ||
* @package WordPress | ||
* @subpackage Blocks | ||
* @since 6.5.0 | ||
* | ||
* @group blocks | ||
* @group block-bindings | ||
*/ | ||
class Tests_Block_Bindings_Post_Meta_Source extends WP_UnitTestCase { | ||
protected static $post; | ||
protected static $wp_meta_keys_saved; | ||
|
||
/** | ||
* Modify the post content. | ||
* | ||
* @param string $content The new content. | ||
*/ | ||
private function get_modified_post_content( $content ) { | ||
$GLOBALS['post']->post_content = $content; | ||
return apply_filters( 'the_content', $GLOBALS['post']->post_content ); | ||
} | ||
|
||
/** | ||
* Sets up shared fixtures. | ||
*/ | ||
public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { | ||
self::$post = $factory->post->create_and_get(); | ||
self::$wp_meta_keys_saved = isset( $GLOBALS['wp_meta_keys'] ) ? $GLOBALS['wp_meta_keys'] : array(); | ||
} | ||
|
||
/** | ||
* Tear down after class. | ||
*/ | ||
public static function wpTearDownAfterClass() { | ||
$GLOBALS['wp_meta_keys'] = self::$wp_meta_keys_saved; | ||
SantosGuillamot marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* Set up before each test. | ||
* | ||
* @since 6.5.0 | ||
*/ | ||
public function set_up() { | ||
parent::set_up(); | ||
// Needed because tear_down() will reset it between tests. | ||
$GLOBALS['post'] = self::$post; | ||
} | ||
|
||
/** | ||
* Tests that a block connected to a custom field renders its value. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_custom_field_value_is_rendered() { | ||
register_meta( | ||
'post', | ||
'tests_custom_field', | ||
array( | ||
'show_in_rest' => true, | ||
'single' => true, | ||
'type' => 'string', | ||
'default' => 'Custom field value', | ||
) | ||
); | ||
|
||
$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"tests_custom_field"}}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' ); | ||
$this->assertSame( | ||
'<p>Custom field value</p>', | ||
$content, | ||
'The post content should show the value of the custom field . ' | ||
); | ||
} | ||
|
||
/** | ||
* Tests that an html attribute connected to a custom field renders its value. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_html_attribute_connected_to_custom_field_value_is_rendered() { | ||
register_meta( | ||
'post', | ||
'tests_url_custom_field', | ||
array( | ||
'show_in_rest' => true, | ||
'single' => true, | ||
'type' => 'string', | ||
'default' => 'https://example.com/foo.png', | ||
) | ||
); | ||
|
||
$content = $this->get_modified_post_content( '<!-- wp:image {"metadata":{"bindings":{"url":{"source":"core/post-meta","args":{"key":"tests_url_custom_field"}}}}} --><figure class="wp-block-image"><img alt=""/></figure><!-- /wp:image -->' ); | ||
$this->assertSame( | ||
'<figure class="wp-block-image"><img decoding="async" src="https://example.com/foo.png" alt=""/></figure>', | ||
$content, | ||
'The image src should point to the value of the custom field . ' | ||
); | ||
} | ||
|
||
/** | ||
* Tests that a blocks connected in a password protected post don't render the value. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_custom_field_value_is_not_shown_in_password_protected_posts() { | ||
register_meta( | ||
'post', | ||
'tests_custom_field', | ||
array( | ||
'show_in_rest' => true, | ||
'single' => true, | ||
'type' => 'string', | ||
'default' => 'Custom field value', | ||
) | ||
); | ||
|
||
add_filter( 'post_password_required', '__return_true' ); | ||
|
||
$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"tests_custom_field"}}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' ); | ||
|
||
remove_filter( 'post_password_required', '__return_true' ); | ||
|
||
$this->assertSame( | ||
'<p>Fallback value</p>', | ||
$content, | ||
'The post content should show the fallback value instead of the custom field value.' | ||
); | ||
} | ||
|
||
/** | ||
* Tests that a blocks connected in a post that is not publicly viewable don't render the value. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_custom_field_value_is_not_shown_in_non_viewable_posts() { | ||
register_meta( | ||
'post', | ||
'tests_custom_field', | ||
array( | ||
'show_in_rest' => true, | ||
'single' => true, | ||
'type' => 'string', | ||
'default' => 'Custom field value', | ||
) | ||
); | ||
|
||
add_filter( 'is_post_status_viewable', '__return_false' ); | ||
|
||
$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"tests_custom_field"}}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' ); | ||
|
||
remove_filter( 'is_post_status_viewable', '__return_false' ); | ||
|
||
$this->assertSame( | ||
'<p>Fallback value</p>', | ||
$content, | ||
'The post content should show the fallback value instead of the custom field value.' | ||
); | ||
} | ||
|
||
/** | ||
* Tests that a block connected to a meta key that doesn't exist renders the fallback. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_binding_to_non_existing_meta_key() { | ||
$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"tests_non_existing_field"}}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' ); | ||
|
||
$this->assertSame( | ||
'<p>Fallback value</p>', | ||
$content, | ||
'The post content should show the fallback value.' | ||
); | ||
} | ||
|
||
/** | ||
* Tests that a block connected without specifying the custom field renders the fallback. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_binding_without_key_renders_the_fallback() { | ||
$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta"}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' ); | ||
|
||
$this->assertSame( | ||
'<p>Fallback value</p>', | ||
$content, | ||
'The post content should show the fallback value.' | ||
); | ||
} | ||
|
||
/** | ||
* Tests that a block connected to a protected field doesn't show the value. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_protected_field_value_is_not_shown() { | ||
register_meta( | ||
'post', | ||
'_tests_protected_field', | ||
array( | ||
'show_in_rest' => true, | ||
'single' => true, | ||
'type' => 'string', | ||
'default' => 'Protected value', | ||
) | ||
); | ||
|
||
$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"_tests_protected_field"}}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' ); | ||
|
||
$this->assertSame( | ||
'<p>Fallback value</p>', | ||
$content, | ||
'The post content should show the fallback value instead of the protected value.' | ||
); | ||
} | ||
|
||
/** | ||
* Tests that a block connected to a field not exposed in the REST API doesn't show the value. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_custom_field_not_exposed_in_rest_api_is_not_shown() { | ||
register_meta( | ||
'post', | ||
'tests_show_in_rest_false_field', | ||
array( | ||
'show_in_rest' => false, | ||
'single' => true, | ||
'type' => 'string', | ||
'default' => 'Protected value', | ||
) | ||
); | ||
|
||
$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"tests_show_in_rest_false_field"}}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' ); | ||
|
||
$this->assertSame( | ||
'<p>Fallback value</p>', | ||
$content, | ||
'The post content should show the fallback value instead of the protected value.' | ||
); | ||
} | ||
|
||
/** | ||
* Tests that meta key with unsafe HTML is sanitized. | ||
* | ||
* @ticket 60651 | ||
*/ | ||
public function test_custom_field_with_unsafe_html_is_sanitized() { | ||
register_meta( | ||
'post', | ||
'tests_unsafe_html_field', | ||
array( | ||
'show_in_rest' => true, | ||
'single' => true, | ||
'type' => 'string', | ||
'default' => '<script>alert("Unsafe HTML")</script>', | ||
) | ||
); | ||
|
||
$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"tests_unsafe_html_field"}}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' ); | ||
|
||
$this->assertSame( | ||
'<p>alert(“Unsafe HTML”)</p>', | ||
$content, | ||
'The post content should not include the script tag.' | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -198,4 +198,40 @@ public function test_update_block_with_value_from_source_image_placeholder() { | |
'The block content should be updated with the value returned by the source.' | ||
); | ||
} | ||
|
||
/** | ||
* Tests if the block content is sanitized when unsafe HTML is passed. | ||
* | ||
* @ticket 60651 | ||
* | ||
* @covers ::register_block_bindings_source | ||
*/ | ||
public function test_source_value_with_unsafe_html_is_sanitized() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also add another test for a Button block where we put something tricky as the link's URL. Example: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering what if we should handle that case in the bindings because passing |
||
$get_value_callback = function () { | ||
return '<script>alert("Unsafe HTML")</script>'; | ||
}; | ||
|
||
register_block_bindings_source( | ||
self::SOURCE_NAME, | ||
array( | ||
'label' => self::SOURCE_LABEL, | ||
'get_value_callback' => $get_value_callback, | ||
) | ||
); | ||
|
||
$block_content = <<<HTML | ||
<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"test/source"}}}} --> | ||
<p>This should not appear</p> | ||
<!-- /wp:paragraph --> | ||
HTML; | ||
$parsed_blocks = parse_blocks( $block_content ); | ||
$block = new WP_Block( $parsed_blocks[0] ); | ||
$result = $block->render(); | ||
|
||
$this->assertSame( | ||
'<p>alert("Unsafe HTML")</p>', | ||
trim( $result ), | ||
'The block content should be updated with the value returned by the source.' | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per my other comment, the static
$post
variable is redundant if you make it global anyway.