PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / extensions / blocks / goodreads / render.php
jetpack / extensions / blocks / goodreads Last commit date
goodreads.php 1 month ago render.php 2 weeks ago
render.php
128 lines
1 <?php
2 /**
3 * Goodreads block render implementation.
4 *
5 * Loaded lazily from goodreads.php only when the block is rendered, to keep
6 * the render body out of the eager front-end PHP/opcache footprint.
7 *
8 * @package automattic/jetpack
9 */
10
11 namespace Automattic\Jetpack\Extensions\Goodreads;
12
13 use Automattic\Jetpack\Blocks;
14 use Jetpack_Gutenberg;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit( 0 );
18 }
19
20 /**
21 * Validates a Goodreads widget script URL.
22 *
23 * Accepts only URLs shaped like those generated by createGoodreadsEmbedLink()
24 * in utils.js — keep the two in sync when either side changes.
25 *
26 * @param mixed $url URL to validate.
27 *
28 * @return string Canonical URL rebuilt from the validated components, or an empty string when the URL is not allowed.
29 */
30 function get_validated_script_url( $url ) {
31 if ( ! is_string( $url ) || str_contains( $url, '\\' ) ) {
32 return '';
33 }
34
35 $parsed = wp_parse_url( esc_url_raw( $url, array( 'https' ) ) );
36 $encoded_path = is_array( $parsed ) ? $parsed['path'] ?? '' : '';
37 $path = rawurldecode( $encoded_path );
38 $has_encoded_separator = 1 === preg_match( '~%(?:2f|5c)~i', $encoded_path );
39
40 if (
41 ! is_array( $parsed )
42 || empty( $parsed['scheme'] )
43 || 'https' !== strtolower( $parsed['scheme'] )
44 || empty( $parsed['host'] )
45 || 'www.goodreads.com' !== strtolower( $parsed['host'] )
46 || isset( $parsed['user'] )
47 || isset( $parsed['pass'] )
48 || isset( $parsed['port'] )
49 || isset( $parsed['fragment'] )
50 || empty( $parsed['query'] )
51 || $has_encoded_separator
52 || str_contains( $path, '\\' )
53 ) {
54 return '';
55 }
56
57 // Only the documented widget endpoints: a numeric Goodreads ID and a non-empty title.
58 // Goodreads treats literal slashes as part of the title, so allow them while
59 // rejecting dot segments that a URL parser could normalize outside this route.
60 if ( 1 !== preg_match( '~^/review/(custom|grid)_widget/[0-9]+\.(.+)$~', $path, $match ) ) {
61 return '';
62 }
63
64 foreach ( explode( '/', $match[2] ) as $title_segment ) {
65 if ( '.' === $title_segment || '..' === $title_segment ) {
66 return '';
67 }
68 }
69
70 $allowed_query_args = 'grid' === $match[1]
71 ? array( 'cover_size', 'num_books', 'order', 'shelf', 'sort', 'widget_id' )
72 : array( 'num_books', 'order', 'shelf', 'show_author', 'show_cover', 'show_rating', 'show_review', 'show_tags', 'show_title', 'sort', 'widget_id' );
73 $query_args = array();
74
75 wp_parse_str( $parsed['query'], $query_args );
76
77 if ( array_diff( array_keys( $query_args ), $allowed_query_args ) ) {
78 return '';
79 }
80
81 foreach ( $query_args as $value ) {
82 if ( is_array( $value ) ) {
83 return '';
84 }
85 }
86
87 return 'https://www.goodreads.com' . $parsed['path'] . '?' . $parsed['query'];
88 }
89
90 /**
91 * Dynamic rendering of the block.
92 *
93 * @param array $attr Array containing the Goodreads block attributes.
94 *
95 * @return string
96 */
97 function render_implementation( $attr ) {
98 Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
99
100 if ( isset( $attr['id'] ) ) {
101 if ( isset( $attr['link'] ) ) {
102 $script_url = get_validated_script_url( $attr['link'] );
103
104 if ( '' !== $script_url ) {
105 wp_enqueue_script(
106 'jetpack-goodreads-' . esc_attr( $attr['id'] ),
107 $script_url,
108 array(),
109 JETPACK__VERSION,
110 true
111 );
112 }
113 }
114
115 $id = esc_attr( $attr['id'] );
116 } else {
117 $id = '';
118 }
119
120 $classes = esc_attr( Blocks::classes( Blocks::get_block_feature( __DIR__ ), $attr ) );
121
122 return sprintf(
123 '<div id="%1$s" class="%2$s"></div>',
124 $id,
125 $classes
126 );
127 }
128