|
| 1 | +<?php |
| 2 | +/** |
| 3 | + * Output-escaping regression tests for block render callbacks. |
| 4 | + * |
| 5 | + * @package gutenberg-blocks |
| 6 | + */ |
| 7 | + |
| 8 | +use ThemeIsle\GutenbergBlocks\Render\Plugin_Card_Block; |
| 9 | +use ThemeIsle\OtterPro\Render\Review_Comparison_Block; |
| 10 | +use ThemeIsle\OtterPro\Render\Form_Stripe_Block; |
| 11 | + |
| 12 | +/** |
| 13 | + * Render escaping test case. |
| 14 | + */ |
| 15 | +class Test_Render_Escaping extends WP_UnitTestCase { |
| 16 | + |
| 17 | + /** |
| 18 | + * Provide a block-render context so get_block_wrapper_attributes() works when |
| 19 | + * render callbacks are invoked directly. |
| 20 | + */ |
| 21 | + public function set_up() { |
| 22 | + parent::set_up(); |
| 23 | + \WP_Block_Supports::$block_to_render = array( |
| 24 | + 'blockName' => '', |
| 25 | + 'attrs' => array(), |
| 26 | + ); |
| 27 | + } |
| 28 | + |
| 29 | + /** |
| 30 | + * Tear down the test. |
| 31 | + */ |
| 32 | + public function tear_down() { |
| 33 | + \WP_Block_Supports::$block_to_render = null; |
| 34 | + parent::tear_down(); |
| 35 | + } |
| 36 | + |
| 37 | + /** |
| 38 | + * Review comparison must escape values pulled from the referenced review block. |
| 39 | + */ |
| 40 | + public function test_review_comparison_escapes_referenced_block_attributes() { |
| 41 | + $block_markup = '<!-- wp:themeisle-blocks/review {"id":"aaaaaaaabbbb1234","title":"<script>alert(1)</script>","price":"9.99","description":"<img src=x onerror=alert(2)>","features":[{"title":"<script>alert(3)</script>","rating":8}]} /-->'; |
| 42 | + |
| 43 | + // Store the block markup raw (bypass save-time kses) so the test exercises the |
| 44 | + // render-layer escaping, not WordPress's save-time sanitization. |
| 45 | + kses_remove_filters(); |
| 46 | + $post_id = $this->factory()->post->create( |
| 47 | + array( |
| 48 | + 'post_content' => $block_markup, |
| 49 | + 'post_status' => 'publish', |
| 50 | + ) |
| 51 | + ); |
| 52 | + kses_init_filters(); |
| 53 | + |
| 54 | + $html = ( new Review_Comparison_Block() )->render( |
| 55 | + array( 'reviews' => array( $post_id . '-bbbb1234' ) ) |
| 56 | + ); |
| 57 | + |
| 58 | + $this->assertStringNotContainsString( '<script>alert(1)</script>', $html ); |
| 59 | + $this->assertStringNotContainsString( '<script>alert(3)</script>', $html ); |
| 60 | + $this->assertStringNotContainsString( 'onerror=', $html ); |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * The plugin card must escape the author field from the wordpress.org API. |
| 65 | + */ |
| 66 | + public function test_plugin_card_escapes_author() { |
| 67 | + $slug = 'otter-blocks'; |
| 68 | + |
| 69 | + $results = new stdClass(); |
| 70 | + $results->name = 'Otter'; |
| 71 | + $results->author = '<script>alert(1)</script>'; |
| 72 | + $results->version = '1.0'; |
| 73 | + $results->rating = 90; |
| 74 | + $results->num_ratings = 10; |
| 75 | + $results->active_installs = 1000; |
| 76 | + $results->requires = '6.0'; |
| 77 | + $results->tested = '6.4'; |
| 78 | + $results->homepage = 'https://example.com'; |
| 79 | + $results->download_link = 'https://example.com/plugin.zip'; |
| 80 | + $results->short_description = 'A plugin.'; |
| 81 | + $results->icons = array( 'default' => 'https://example.com/icon.png' ); |
| 82 | + |
| 83 | + set_transient( 'otter_plugin_card_' . sanitize_key( $slug ), $results, HOUR_IN_SECONDS ); |
| 84 | + |
| 85 | + $html = ( new Plugin_Card_Block() )->render( array( 'slug' => $slug ) ); |
| 86 | + |
| 87 | + delete_transient( 'otter_plugin_card_' . sanitize_key( $slug ) ); |
| 88 | + |
| 89 | + $this->assertStringNotContainsString( '<script>alert(1)</script>', $html ); |
| 90 | + } |
| 91 | + |
| 92 | + /** |
| 93 | + * The Stripe field block must escape id/name/option-name block attributes. |
| 94 | + */ |
| 95 | + public function test_form_stripe_field_attributes_are_escaped() { |
| 96 | + $out = ( new Form_Stripe_Block() )->get_field_attributes( |
| 97 | + array( |
| 98 | + 'id' => 'x"><script>alert(1)</script>', |
| 99 | + 'mappedName' => 'n"><script>alert(2)</script>', |
| 100 | + 'fieldOptionName' => 'f"><script>alert(3)</script>', |
| 101 | + ) |
| 102 | + ); |
| 103 | + |
| 104 | + $this->assertStringNotContainsString( '<script>', $out ); |
| 105 | + } |
| 106 | +} |
0 commit comments