Skip to content

Commit f7fd55c

Browse files
authored
Merge pull request #457 from cloudinary/fix/insecure-replacements
reorganize to allow for consistent end of cycle replacements.
2 parents 5cb9913 + 1bdffcc commit f7fd55c

File tree

2 files changed

+77
-82
lines changed

2 files changed

+77
-82
lines changed

php/class-delivery.php

Lines changed: 73 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -80,42 +80,38 @@ public function clear_cache() {
8080
public function setup() {
8181
$this->filter = $this->media->filter;
8282
// Add filters.
83-
add_filter( 'the_content', array( $this, 'filter_local' ) );
84-
add_action( 'save_post', array( $this, 'remove_replace_cache' ), 10, 2 );
83+
add_action( 'save_post', array( $this, 'remove_replace_cache' ) );
8584
add_action( 'cloudinary_string_replace', array( $this, 'catch_urls' ) );
86-
add_filter( 'post_thumbnail_html', array( $this, 'rebuild_tag' ), 100, 5 );
85+
add_filter( 'post_thumbnail_html', array( $this, 'process_featured_image' ), 100, 3 );
8786
}
8887

8988
/**
90-
* Delete the content replacement cache data.
89+
* Add classes to the featured image tag.
90+
*
91+
* @param string $html The image tah HTML to add to.
92+
* @param int $post_id Ignored.
93+
* @param int $attachment_id The attachment_id.
9194
*
92-
* @param int $post_id The post ID to remove cache from.
93-
* @param WP_Post $post The post object.
95+
* @return string
9496
*/
95-
public function remove_replace_cache( $post_id, $post ) {
96-
delete_post_meta( $post_id, self::META_CACHE_KEY );
97+
public function process_featured_image( $html, $post_id, $attachment_id ) {
98+
// Get tag element.
99+
$tag_element = $this->parse_element( $html );
100+
$tag_element['atts']['class'][] = 'wp-image-' . $attachment_id;
101+
if ( true === (bool) $this->media->get_post_meta( $post_id, Global_Transformations::META_FEATURED_IMAGE_KEY, true ) ) {
102+
$tag_element['atts']['class'][] = 'cld-overwrite';
103+
}
104+
105+
return HTML::build_tag( $tag_element['tag'], $tag_element['atts'] );
97106
}
98107

99108
/**
100-
* Filter out the local URLS from the content.
101-
*
102-
* @param string $content The HTML of the content to filter.
109+
* Delete the content replacement cache data.
103110
*
104-
* @return string
111+
* @param int $post_id The post ID to remove cache from.
105112
*/
106-
public function filter_local( $content ) {
107-
$post_id = get_the_ID();
108-
if ( ! empty( $post_id ) ) {
109-
$replacements = get_post_meta( $post_id, self::META_CACHE_KEY, true );
110-
if ( empty( $replacements ) ) {
111-
$replacements = $this->convert_tags( $post_id, $content );
112-
}
113-
foreach ( $replacements as $search => $replace ) {
114-
String_Replace::replace( $search, $replace );
115-
}
116-
}
117-
118-
return $content;
113+
public function remove_replace_cache( $post_id ) {
114+
delete_post_meta( $post_id, self::META_CACHE_KEY );
119115
}
120116

121117
/**
@@ -194,12 +190,19 @@ public function find_attachment_size_urls( $urls ) {
194190
/**
195191
* Convert media tags from Local to Cloudinary, and register with String_Replace.
196192
*
197-
* @param int $post_id The post ID.
198193
* @param string $content The HTML to find tags and prep replacement in.
199194
*
200195
* @return array
201196
*/
202-
public function convert_tags( $post_id, $content ) {
197+
public function convert_tags( $content ) {
198+
if ( is_singular() ) {
199+
$cache_key = self::META_CACHE_KEY;
200+
$has_cache = get_post_meta( get_the_ID(), $cache_key, true );
201+
$type = is_ssl() ? 'https' : 'http';
202+
if ( ! empty( $has_cache ) && ! empty( $has_cache[ $type ] ) ) {
203+
return $has_cache[ $type ];
204+
}
205+
}
203206
$tags = $this->filter->get_media_tags( $content );
204207
$replacements = array();
205208
$attachment_ids = array();
@@ -209,7 +212,7 @@ public function convert_tags( $post_id, $content ) {
209212
continue;
210213
}
211214
// Register replacement.
212-
$replacements[ $element ] = $this->rebuild_tag( $element, null, $attachment_id );
215+
$replacements[ $element ] = $this->rebuild_tag( $element, $attachment_id );
213216
$attachment_ids[] = $attachment_id;
214217
}
215218

@@ -221,7 +224,13 @@ public function convert_tags( $post_id, $content ) {
221224
}
222225
}
223226
// Update the post meta cache.
224-
update_post_meta( $post_id, self::META_CACHE_KEY, $replacements );
227+
if ( isset( $cache_key ) && isset( $type ) ) {
228+
if ( empty( $has_cache ) ) {
229+
$has_cache = array();
230+
}
231+
$has_cache[ $type ] = $replacements;
232+
update_post_meta( get_the_ID(), $cache_key, $has_cache );
233+
}
225234

226235
return $replacements;
227236
}
@@ -230,53 +239,67 @@ public function convert_tags( $post_id, $content ) {
230239
* Rebuild a tag with cloudinary urls.
231240
*
232241
* @param string $element The original HTML tag.
233-
* @param null|int $post_id The optional associated post ID.
234242
* @param null|int $attachment_id The attachment ID.
235243
*
236244
* @return string
237245
*/
238-
public function rebuild_tag( $element, $post_id, $attachment_id ) {
246+
public function rebuild_tag( $element, $attachment_id ) {
239247
// Add our filter if not already added.
240248
if ( ! has_filter( 'wp_calculate_image_srcset', array( $this->media, 'image_srcset' ) ) ) {
241249
add_filter( 'wp_calculate_image_srcset', array( $this->media, 'image_srcset' ), 10, 5 );
242250
}
243-
$element = trim( $element, '</>' );
244251

245-
// Break element up.
246-
$atts = shortcode_parse_atts( $element );
247-
248-
// Remove tag.
249-
$tag = array_shift( $atts );
252+
// Get tag element.
253+
$tag_element = $this->parse_element( $element );
250254

251255
// Remove the old srcset if it has one.
252-
if ( isset( $atts['srcset'] ) ) {
253-
unset( $atts['srcset'] );
256+
if ( isset( $tag_element['atts']['srcset'] ) ) {
257+
unset( $tag_element['atts']['srcset'] );
254258
}
255259

256260
// Get overwrite flag.
257-
$overwrite = false;
258-
if ( is_null( $post_id ) && isset( $atts['class'] ) ) {
259-
$overwrite = (bool) strpos( $atts['class'], 'cld-overwrite' );
260-
} elseif ( $post_id ) {
261-
$overwrite = (bool) $this->media->get_post_meta( $post_id, Global_Transformations::META_FEATURED_IMAGE_KEY, true );
262-
}
261+
$overwrite = in_array( 'cld-overwrite', $tag_element['atts']['class'], true );
263262

264263
// Get size.
265-
$size = $this->get_size_from_atts( $atts );
264+
$size = $this->get_size_from_atts( $tag_element['atts'] );
266265

267266
// Get transformations if present.
268-
$transformations = $this->get_transformations_maybe( $atts['src'] );
267+
$transformations = $this->get_transformations_maybe( $tag_element['atts']['src'] );
269268

270269
// Create new src url.
271-
$atts['src'] = $this->media->cloudinary_url( $attachment_id, $size, $transformations, null, $overwrite );
270+
$tag_element['atts']['src'] = $this->media->cloudinary_url( $attachment_id, $size, $transformations, null, $overwrite );
272271

273272
// Setup new tag.
274-
$replace = HTML::build_tag( $tag, $atts );
273+
$replace = HTML::build_tag( $tag_element['tag'], $tag_element['atts'] );
275274

276275
// Add new srcset.
277276
return $this->media->apply_srcset( $replace, $attachment_id, $overwrite );
278277
}
279278

279+
/**
280+
* Parse an html element into tag, and attributes.
281+
*
282+
* @param string $element The HTML element.
283+
*
284+
* @return array
285+
*/
286+
public function parse_element( $element ) {
287+
// Cleanup element.
288+
$element = trim( $element, '</>' );
289+
290+
// Break element up.
291+
$atts = shortcode_parse_atts( $element );
292+
if ( ! empty( $atts['class'] ) ) {
293+
$atts['class'] = explode( ' ', $atts['class'] );
294+
}
295+
$tag_element = array(
296+
'tag' => array_shift( $atts ),
297+
'atts' => $atts,
298+
);
299+
300+
return $tag_element;
301+
}
302+
280303
/**
281304
* Get the size from the attributes.
282305
*
@@ -322,7 +345,7 @@ protected function get_transformations_maybe( $url ) {
322345
* @param string $content The HTML to catch URLS from.
323346
*/
324347
public function catch_urls( $content ) {
325-
$known = array();
348+
$known = $this->convert_tags( $content );
326349
$urls = wp_extract_urls( $content );
327350
$dirs = wp_get_upload_dir();
328351
$urls = array_map(

php/media/class-filter.php

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -706,37 +706,13 @@ public function catch_media_templates_maybe() {
706706
* @return string
707707
*/
708708
public function filter_image_block_render_block( $block_content, array $block ) {
709-
if ( 'core/image' === $block['blockName'] && empty( $block['cld_render'] ) ) {
710-
$filtered_block = $this->filter_image_block_pre_render( $block, $block );
711-
$filtered_block['cld_render'] = true;
712-
$block_content = render_block( $filtered_block );
713-
709+
if ( 'core/image' === $block['blockName'] && ! empty( $block['attrs']['overwrite_transformations'] ) ) {
710+
$block_content = str_replace( 'wp-image-' . $block['attrs']['id'], 'wp-image-' . $block['attrs']['id'] . ' cld-overwrite', $block_content );
714711
}
715712

716713
return $block_content;
717714
}
718715

719-
/**
720-
* Filter an image block to add the class for cld-overriding.
721-
*
722-
* @param array $block The current block structure.
723-
* @param array $source_block The source, unfiltered block structure.
724-
*
725-
* @return array
726-
*/
727-
public function filter_image_block_pre_render( $block, $source_block ) {
728-
729-
if ( 'core/image' === $source_block['blockName'] ) {
730-
if ( ! empty( $source_block['attrs']['overwrite_transformations'] ) ) {
731-
foreach ( $block['innerContent'] as &$content ) {
732-
$content = str_replace( 'wp-image-' . $block['attrs']['id'], 'wp-image-' . $block['attrs']['id'] . ' cld-overwrite', $content );
733-
}
734-
}
735-
}
736-
737-
return $block;
738-
}
739-
740716
/**
741717
* Add filters for Rest API handling.
742718
*/
@@ -782,12 +758,8 @@ public function setup_hooks() {
782758
add_action( 'admin_footer', array( $this, 'catch_media_templates_maybe' ), 9 );
783759

784760
// Filter for block rendering.
785-
if ( has_filter( 'render_block_data' ) ) {
786-
add_filter( 'render_block_data', array( $this, 'filter_image_block_pre_render' ), 10, 2 );
787-
} else {
788-
// The render_block_data filter was only introduced on WP 5.1.0. This is the fallback for 5.0.*.
789-
add_filter( 'render_block', array( $this, 'filter_image_block_render_block' ), 10, 2 );
790-
}
761+
add_filter( 'render_block', array( $this, 'filter_image_block_render_block' ), 10, 2 );
762+
791763

792764
// Filter out locals and responsive images setup.
793765
if ( $this->media->can_filter_out_local() || is_admin() ) {

0 commit comments

Comments
 (0)